You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2026-04-24 19:55:13 +02:00
Compare commits
204 Commits
v2.12.8
...
issue-8706
| Author | SHA1 | Date | |
|---|---|---|---|
| fb8b7ac966 | |||
| 59f8b43c21 | |||
| 5c63eb0913 | |||
| 5855748e06 | |||
| 4e2d36648e | |||
| 6aec75806e | |||
| 2e9f93ad9a | |||
| 26a967e53c | |||
| 2fda252a5e | |||
| 831b1ae035 | |||
| f807a0179d | |||
| b3801b333d | |||
| 808e175f7f | |||
| 03f1d86531 | |||
| b92cb7deb7 | |||
| 0edc66da49 | |||
| e96ad7ccfa | |||
| 817ef7bbed | |||
| 5bd0c9b3a0 | |||
| 46d9cd34a8 | |||
| c3e08237fd | |||
| b406f05241 | |||
| 2cbee6d8af | |||
| c859ad48c1 | |||
| 1141b1c2a1 | |||
| 39c118be90 | |||
| f9ac4e112b | |||
| 87e51aa8e6 | |||
| 41fdc0d44d | |||
| a754a8d772 | |||
| 41d0363fd0 | |||
| 2a4c7a334e | |||
| df1b0a96f4 | |||
| 0030681cb4 | |||
| e7014492c5 | |||
| 4804c1c0c3 | |||
| 270d96ad07 | |||
| 5ed3d94faa | |||
| d0e943630d | |||
| 406e933407 | |||
| 7108a4243d | |||
| 135e2e4a21 | |||
| c68c0bf501 | |||
| cf3d86698d | |||
| 3251c4c40e | |||
| bd5e0fd42a | |||
| 7d0b7122f0 | |||
| c50052ac04 | |||
| 357c23b588 | |||
| eca1afb6d5 | |||
| 85eddbfe22 | |||
| c6c2733726 | |||
| 5d87b4ca3e | |||
| 89f550ca48 | |||
| 9e55d90736 | |||
| 8d0d9b58de | |||
| ac5e484d4e | |||
| bce18a1614 | |||
| 950b16370f | |||
| 4337e2b79a | |||
| 90d75ce80e | |||
| 72d34788dc | |||
| 6a2e6173ab | |||
| 65bb9fa3c4 | |||
| 90e1502e73 | |||
| 3cc990e1a2 | |||
| 10fd8454f7 | |||
| 56d7030222 | |||
| 8696052e27 | |||
| 5f7e130ff9 | |||
| 434c890686 | |||
| 5ab1b0bfd0 | |||
| b9f632b634 | |||
| ae8f32e6b4 | |||
| ce8470ee7c | |||
| 3b1a726a23 | |||
| 3d4740203f | |||
| 7a74271e6a | |||
| 2cfae43fce | |||
| a143b980ee | |||
| 329c385358 | |||
| 1543c651aa | |||
| 9cdaff4f1e | |||
| 8e3247221a | |||
| ad975a473f | |||
| 9a8c0e9813 | |||
| 9d16dd22be | |||
| c0501fc4e0 | |||
| 1acbb5dc9a | |||
| 2f6e146841 | |||
| ba2bfa5b5c | |||
| 95ca89bf5b | |||
| 16d8a78d8a | |||
| f116504e88 | |||
| 4d0b6ae382 | |||
| 13c4eba3af | |||
| 9756f64c11 | |||
| 807384cfac | |||
| 2329e321ec | |||
| 6c2e0d9262 | |||
| 887c271853 | |||
| ea7c7f6447 | |||
| 29931c05ad | |||
| 17c227024e | |||
| 406a1496db | |||
| c8180b91e0 | |||
| 1978929114 | |||
| 3e7debbcc5 | |||
| a138b92b1b | |||
| 28619f1786 | |||
| e8f30b708b | |||
| 39bc7ed397 | |||
| 59852e252b | |||
| 51764cc933 | |||
| cb754604f1 | |||
| 2633bcef69 | |||
| 772c8abdcf | |||
| ac098759ea | |||
| 349dda30b8 | |||
| cbbd9b9a61 | |||
| 14ef0d725b | |||
| 40b89f61bd | |||
| 23c4020eea | |||
| 5ba374c03b | |||
| f76ae86204 | |||
| 46ccd94514 | |||
| 7140675181 | |||
| 31f8e725dd | |||
| 5edbdb2466 | |||
| 83cf5eb617 | |||
| 5b0c332477 | |||
| 21a5feb322 | |||
| 16c87c36da | |||
| a0ec928fca | |||
| b9659bb9c1 | |||
| 35f375d756 | |||
| 04c80fe00e | |||
| 06732d8b8a | |||
| 8fc1e71fb3 | |||
| aebfa6e96d | |||
| 3453abb670 | |||
| 637a0eac7f | |||
| abe479d03f | |||
| d1558d84b1 | |||
| 36e9e0a33c | |||
| d78ab16021 | |||
| c718706f9c | |||
| f6586239c4 | |||
| 287d8fee73 | |||
| 5357b2b0dc | |||
| 0c4513838a | |||
| d8a54da496 | |||
| 672ef1fd7c | |||
| c50181f0a6 | |||
| 778f317f9f | |||
| 0394694a91 | |||
| fec853735b | |||
| 087b206982 | |||
| 3bd04aeaf4 | |||
| 74e81eb6f3 | |||
| 3cb8fd035f | |||
| 0a96c3d56b | |||
| 2d16151fa8 | |||
| 93c5f6c9d3 | |||
| e47985c101 | |||
| 1c1d20f82c | |||
| 880304c2fb | |||
| bd3d5f213e | |||
| 0d6f4c9361 | |||
| c49b53cb4b | |||
| da177d3025 | |||
| 6d184fb47c | |||
| 3691662da2 | |||
| 776b6d8f12 | |||
| 82c33c4f0c | |||
| ab77f0e0f4 | |||
| df3c60e43f | |||
| 9aafdd2d10 | |||
| 1fe01dc0ce | |||
| f0b5ec3e3a | |||
| 955f724d36 | |||
| 6cf0ed6166 | |||
| 1bcaf9d167 | |||
| 06b2ba9d75 | |||
| 85079ad213 | |||
| b9225d1daa | |||
| c88b6b608a | |||
| 56cd782175 | |||
| 6ce8865719 | |||
| ac66332a4e | |||
| c70afe3478 | |||
| 3c2f193714 | |||
| bb9e970f8f | |||
| 9c8fbe831f | |||
| 4e1ee87269 | |||
| 9a32472522 | |||
| 1ecd6883d5 | |||
| f89c6523e4 | |||
| 5c7b48fce1 | |||
| 1e2e8ed099 | |||
| 4d7399973e | |||
| 2f8275be8c | |||
| 58e5b83648 | |||
| 49e3f01a0a |
+129
-87
@@ -82,8 +82,8 @@ packages/app-cli/app/LinkSelector.js
|
||||
packages/app-cli/app/base-command.js
|
||||
packages/app-cli/app/command-done.test.js
|
||||
packages/app-cli/app/command-e2ee.js
|
||||
packages/app-cli/app/command-mkbook.js
|
||||
packages/app-cli/app/command-mkbook.test.js
|
||||
packages/app-cli/app/command-mkbook.js
|
||||
packages/app-cli/app/command-settingschema.js
|
||||
packages/app-cli/app/command-sync.js
|
||||
packages/app-cli/app/command-testing.js
|
||||
@@ -92,6 +92,7 @@ packages/app-cli/app/services/plugins/PluginRunner.js
|
||||
packages/app-cli/app/setupCommand.js
|
||||
packages/app-cli/app/utils/testUtils.js
|
||||
packages/app-cli/tests/HtmlToMd.js
|
||||
packages/app-cli/tests/MarkupToHtml.js
|
||||
packages/app-cli/tests/MdToHtml.js
|
||||
packages/app-cli/tests/services/keychain/KeychainService.js
|
||||
packages/app-cli/tests/services/plugins/PluginService.js
|
||||
@@ -105,9 +106,9 @@ packages/app-cli/tests/testUtils.js
|
||||
packages/app-cli/tools/populateDatabase.js
|
||||
packages/app-desktop/ElectronAppWrapper.js
|
||||
packages/app-desktop/InteropServiceHelper.js
|
||||
packages/app-desktop/app.js
|
||||
packages/app-desktop/app.reducer.js
|
||||
packages/app-desktop/app.reducer.test.js
|
||||
packages/app-desktop/app.reducer.js
|
||||
packages/app-desktop/app.js
|
||||
packages/app-desktop/bridge.js
|
||||
packages/app-desktop/checkForUpdates.js
|
||||
packages/app-desktop/commands/copyDevCommand.js
|
||||
@@ -132,11 +133,12 @@ packages/app-desktop/gui/ClipperConfigScreen.js
|
||||
packages/app-desktop/gui/ConfigScreen/ButtonBar.js
|
||||
packages/app-desktop/gui/ConfigScreen/ConfigScreen.js
|
||||
packages/app-desktop/gui/ConfigScreen/Sidebar.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/ToggleAdvancedSettingsButton.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginBox.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/SearchPlugins.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/useOnInstallHandler.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/useOnInstallHandler.test.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/useOnInstallHandler.js
|
||||
packages/app-desktop/gui/Dialog.js
|
||||
packages/app-desktop/gui/DialogButtonRow.js
|
||||
packages/app-desktop/gui/DialogButtonRow/useKeyboardHandler.js
|
||||
@@ -154,6 +156,7 @@ packages/app-desktop/gui/HelpButton.js
|
||||
packages/app-desktop/gui/IconButton.js
|
||||
packages/app-desktop/gui/ImportScreen.js
|
||||
packages/app-desktop/gui/ItemList.js
|
||||
packages/app-desktop/gui/JoplinCloudConfigScreen.js
|
||||
packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js
|
||||
packages/app-desktop/gui/KeymapConfig/ShortcutRecorder.js
|
||||
packages/app-desktop/gui/KeymapConfig/styles/index.js
|
||||
@@ -217,8 +220,8 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/Toolbar.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/styles/index.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/index.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.test.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearch.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useExternalPlugins.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinCommands.js
|
||||
@@ -246,16 +249,18 @@ packages/app-desktop/gui/NoteEditor/commands/showLocalSearch.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/showRevisions.js
|
||||
packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.js
|
||||
packages/app-desktop/gui/NoteEditor/styles/index.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/clipboardUtils.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/clipboardUtils.test.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/clipboardUtils.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/contextMenu.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/index.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/resourceHandling.test.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/resourceHandling.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/types.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useDropHandler.js
|
||||
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
|
||||
@@ -264,13 +269,36 @@ packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useWindowCommandHandler.js
|
||||
packages/app-desktop/gui/NoteList/NoteList.js
|
||||
packages/app-desktop/gui/NoteList/NoteList2.js
|
||||
packages/app-desktop/gui/NoteList/NoteListSource.js
|
||||
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js
|
||||
packages/app-desktop/gui/NoteList/commands/index.js
|
||||
packages/app-desktop/gui/NoteList/types.js
|
||||
packages/app-desktop/gui/NoteList/utils/canManuallySortNotes.js
|
||||
packages/app-desktop/gui/NoteList/utils/defaultLeftToRightListRenderer.js
|
||||
packages/app-desktop/gui/NoteList/utils/defaultListRenderer.js
|
||||
packages/app-desktop/gui/NoteList/utils/getNoteTitleHtml.js
|
||||
packages/app-desktop/gui/NoteList/utils/prepareViewProps.js
|
||||
packages/app-desktop/gui/NoteList/utils/types.js
|
||||
packages/app-desktop/gui/NoteList/utils/useDragAndDrop.js
|
||||
packages/app-desktop/gui/NoteList/utils/useFocusNote.js
|
||||
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/useRenderedNotes.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
|
||||
packages/app-desktop/gui/NoteListControls/NoteListControls.js
|
||||
packages/app-desktop/gui/NoteListControls/commands/focusSearch.js
|
||||
packages/app-desktop/gui/NoteListControls/commands/index.js
|
||||
packages/app-desktop/gui/NoteListItem.js
|
||||
packages/app-desktop/gui/NoteListItem/NoteListItem.js
|
||||
packages/app-desktop/gui/NoteListItem/utils/types.js
|
||||
packages/app-desktop/gui/NoteListItem/utils/useItemElement.js
|
||||
packages/app-desktop/gui/NoteListItem/utils/useItemEventHandlers.js
|
||||
packages/app-desktop/gui/NoteListItem/utils/useOnContextMenu.js
|
||||
packages/app-desktop/gui/NoteListItem/utils/useRootElement.js
|
||||
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js
|
||||
packages/app-desktop/gui/NotePropertiesDialog.js
|
||||
packages/app-desktop/gui/NoteRevisionViewer.js
|
||||
@@ -288,17 +316,17 @@ packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/movements.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/movements.test.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/persist.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/movements.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/persist.test.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/persist.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/style.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/types.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js
|
||||
packages/app-desktop/gui/ResourceScreen.js
|
||||
@@ -357,14 +385,15 @@ packages/app-desktop/services/plugins/hooks/useViewIsReady.js
|
||||
packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js
|
||||
packages/app-desktop/services/restart.js
|
||||
packages/app-desktop/services/share/invitationRespond.js
|
||||
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.js
|
||||
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.js
|
||||
packages/app-desktop/services/sortOrder/notesSortOrderUtils.js
|
||||
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.js
|
||||
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.js
|
||||
packages/app-desktop/services/sortOrder/notesSortOrderUtils.js
|
||||
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.js
|
||||
packages/app-desktop/tools/notarizeMacApp.js
|
||||
packages/app-desktop/utils/checkForUpdatesUtils.js
|
||||
packages/app-desktop/utils/checkForUpdatesUtils.test.js
|
||||
packages/app-desktop/utils/checkForUpdatesUtils.js
|
||||
packages/app-desktop/utils/checkForUpdatesUtilsTestData.js
|
||||
packages/app-desktop/utils/markupLanguageUtils.js
|
||||
packages/app-mobile/PluginAssetsLoader.js
|
||||
packages/app-mobile/components/ActionButton.js
|
||||
@@ -379,17 +408,17 @@ packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js
|
||||
packages/app-mobile/components/NoteBodyViewer/hooks/useOnMessage.js
|
||||
packages/app-mobile/components/NoteBodyViewer/hooks/useOnResourceLongPress.js
|
||||
packages/app-mobile/components/NoteBodyViewer/hooks/useSource.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/decoratorExtension.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.bulletedVsChecklist.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.toggleList.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/syntaxHighlightingLanguages.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/createEditor.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.js
|
||||
@@ -405,6 +434,7 @@ packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarButton.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarOverflowRows.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/types.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.test.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.js
|
||||
packages/app-mobile/components/NoteEditor/SearchPanel.js
|
||||
packages/app-mobile/components/NoteEditor/SelectionFormatting.js
|
||||
@@ -420,9 +450,14 @@ packages/app-mobile/components/app-nav.js
|
||||
packages/app-mobile/components/biometrics/BiometricPopup.js
|
||||
packages/app-mobile/components/biometrics/biometricAuthenticate.js
|
||||
packages/app-mobile/components/biometrics/sensorInfo.js
|
||||
packages/app-mobile/components/getResponsiveValue.js
|
||||
packages/app-mobile/components/getResponsiveValue.test.js
|
||||
packages/app-mobile/components/screens/ConfigScreen.js
|
||||
packages/app-mobile/components/getResponsiveValue.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/ConfigScreen.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/ConfigScreenButton.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/NoteExportButton.test.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/NoteExportButton.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/exportAllFolders.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.js
|
||||
packages/app-mobile/components/screens/Note.js
|
||||
packages/app-mobile/components/screens/Notes.js
|
||||
packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js
|
||||
@@ -443,6 +478,7 @@ packages/app-mobile/tools/buildInjectedJs.js
|
||||
packages/app-mobile/utils/ShareExtension.js
|
||||
packages/app-mobile/utils/ShareUtils.js
|
||||
packages/app-mobile/utils/TlsUtils.js
|
||||
packages/app-mobile/utils/autodetectTheme.js
|
||||
packages/app-mobile/utils/checkPermissions.js
|
||||
packages/app-mobile/utils/createRootStyle.js
|
||||
packages/app-mobile/utils/debounce.js
|
||||
@@ -451,19 +487,19 @@ packages/app-mobile/utils/setupNotifications.js
|
||||
packages/app-mobile/utils/shareHandler.js
|
||||
packages/app-mobile/utils/types.js
|
||||
packages/fork-htmlparser2/src/CollectingHandler.js
|
||||
packages/fork-htmlparser2/src/FeedHandler.js
|
||||
packages/fork-htmlparser2/src/FeedHandler.spec.js
|
||||
packages/fork-htmlparser2/src/FeedHandler.js
|
||||
packages/fork-htmlparser2/src/MultiplexHandler.js
|
||||
packages/fork-htmlparser2/src/Parser.js
|
||||
packages/fork-htmlparser2/src/Parser.spec.js
|
||||
packages/fork-htmlparser2/src/Parser.js
|
||||
packages/fork-htmlparser2/src/Tokenizer.js
|
||||
packages/fork-htmlparser2/src/WritableStream.js
|
||||
packages/fork-htmlparser2/src/WritableStream.spec.js
|
||||
packages/fork-htmlparser2/src/WritableStream.js
|
||||
packages/fork-htmlparser2/src/__fixtures__/test-helper.js
|
||||
packages/fork-htmlparser2/src/__tests__/events.js
|
||||
packages/fork-htmlparser2/src/__tests__/stream.js
|
||||
packages/fork-htmlparser2/src/index.js
|
||||
packages/fork-htmlparser2/src/index.spec.js
|
||||
packages/fork-htmlparser2/src/index.js
|
||||
packages/generator-joplin/generators/app/templates/api/index.js
|
||||
packages/generator-joplin/generators/app/templates/api/types.js
|
||||
packages/generator-joplin/generators/app/templates/api_index.js
|
||||
@@ -476,19 +512,18 @@ packages/lib/BaseModel.js
|
||||
packages/lib/BaseSyncTarget.js
|
||||
packages/lib/ClipperServer.js
|
||||
packages/lib/CssUtils.js
|
||||
packages/lib/EventDispatcher.js
|
||||
packages/lib/EventDispatcher.test.js
|
||||
packages/lib/EventDispatcher.js
|
||||
packages/lib/HtmlToMd.js
|
||||
packages/lib/InMemoryCache.js
|
||||
packages/lib/InMemoryCache.test.js
|
||||
packages/lib/InMemoryCache.js
|
||||
packages/lib/JoplinDatabase.js
|
||||
packages/lib/JoplinError.js
|
||||
packages/lib/JoplinServerApi.js
|
||||
packages/lib/Logger.js
|
||||
packages/lib/ObjectUtils.js
|
||||
packages/lib/PoorManIntervals.js
|
||||
packages/lib/RotatingLogs.js
|
||||
packages/lib/RotatingLogs.test.js
|
||||
packages/lib/RotatingLogs.js
|
||||
packages/lib/SyncTargetJoplinCloud.js
|
||||
packages/lib/SyncTargetJoplinServer.js
|
||||
packages/lib/SyncTargetNone.js
|
||||
@@ -498,14 +533,16 @@ packages/lib/Synchronizer.js
|
||||
packages/lib/TaskQueue.js
|
||||
packages/lib/WelcomeUtils.js
|
||||
packages/lib/array.js
|
||||
packages/lib/callbackUrlUtils.js
|
||||
packages/lib/callbackUrlUtils.test.js
|
||||
packages/lib/callbackUrlUtils.js
|
||||
packages/lib/commands/historyBackward.js
|
||||
packages/lib/commands/historyForward.js
|
||||
packages/lib/commands/index.js
|
||||
packages/lib/commands/openMasterPasswordDialog.js
|
||||
packages/lib/commands/synchronize.js
|
||||
packages/lib/components/EncryptionConfigScreen/utils.js
|
||||
packages/lib/components/shared/config/shouldShowMissingPasswordWarning.test.js
|
||||
packages/lib/components/shared/config/shouldShowMissingPasswordWarning.js
|
||||
packages/lib/components/shared/note-screen-shared.js
|
||||
packages/lib/components/shared/reduxSharedMiddleware.js
|
||||
packages/lib/database-driver-better-sqlite.js
|
||||
@@ -527,41 +564,42 @@ packages/lib/geolocation-node.js
|
||||
packages/lib/hooks/useAsyncEffect.js
|
||||
packages/lib/hooks/useElementSize.js
|
||||
packages/lib/hooks/useEventListener.js
|
||||
packages/lib/htmlUtils.js
|
||||
packages/lib/htmlUtils.test.js
|
||||
packages/lib/htmlUtils.js
|
||||
packages/lib/htmlUtils2.test.js
|
||||
packages/lib/import-enex-md-gen.js
|
||||
packages/lib/import-enex-md-gen.test.js
|
||||
packages/lib/import-enex-md-gen.js
|
||||
packages/lib/import-enex.js
|
||||
packages/lib/locale.js
|
||||
packages/lib/initLib.js
|
||||
packages/lib/locale.test.js
|
||||
packages/lib/markdownUtils.js
|
||||
packages/lib/locale.js
|
||||
packages/lib/markdownUtils.test.js
|
||||
packages/lib/markdownUtils.js
|
||||
packages/lib/markdownUtils2.test.js
|
||||
packages/lib/markupLanguageUtils.js
|
||||
packages/lib/migrations/42.js
|
||||
packages/lib/models/Alarm.js
|
||||
packages/lib/models/BaseItem.js
|
||||
packages/lib/models/Folder.js
|
||||
packages/lib/models/Folder.sharing.test.js
|
||||
packages/lib/models/Folder.test.js
|
||||
packages/lib/models/ItemChange.js
|
||||
packages/lib/models/Folder.js
|
||||
packages/lib/models/ItemChange.test.js
|
||||
packages/lib/models/MasterKey.js
|
||||
packages/lib/models/ItemChange.js
|
||||
packages/lib/models/MasterKey.test.js
|
||||
packages/lib/models/MasterKey.js
|
||||
packages/lib/models/Migration.js
|
||||
packages/lib/models/Note.js
|
||||
packages/lib/models/Note.test.js
|
||||
packages/lib/models/Note.js
|
||||
packages/lib/models/NoteResource.js
|
||||
packages/lib/models/NoteTag.js
|
||||
packages/lib/models/Resource.js
|
||||
packages/lib/models/Resource.test.js
|
||||
packages/lib/models/Resource.js
|
||||
packages/lib/models/ResourceLocalState.js
|
||||
packages/lib/models/Revision.js
|
||||
packages/lib/models/Revision.test.js
|
||||
packages/lib/models/Revision.js
|
||||
packages/lib/models/Search.js
|
||||
packages/lib/models/Setting.js
|
||||
packages/lib/models/Setting.test.js
|
||||
packages/lib/models/Setting.js
|
||||
packages/lib/models/SmartFilter.js
|
||||
packages/lib/models/Tag.js
|
||||
packages/lib/models/dateTimeFormats.test.js
|
||||
@@ -571,20 +609,20 @@ packages/lib/models/utils/paginatedFeed.js
|
||||
packages/lib/models/utils/paginationToSql.js
|
||||
packages/lib/models/utils/readOnly.js
|
||||
packages/lib/models/utils/types.js
|
||||
packages/lib/models/utils/userData.js
|
||||
packages/lib/models/utils/userData.test.js
|
||||
packages/lib/models/utils/userData.js
|
||||
packages/lib/net-utils.js
|
||||
packages/lib/ntp.js
|
||||
packages/lib/onedrive-api.js
|
||||
packages/lib/path-utils.js
|
||||
packages/lib/reducer.js
|
||||
packages/lib/registry.js
|
||||
packages/lib/registry.test.js
|
||||
packages/lib/registry.js
|
||||
packages/lib/services/AlarmService.js
|
||||
packages/lib/services/AlarmServiceDriverNode.js
|
||||
packages/lib/services/BaseService.js
|
||||
packages/lib/services/CommandService.js
|
||||
packages/lib/services/CommandService.test.js
|
||||
packages/lib/services/CommandService.js
|
||||
packages/lib/services/DecryptionWorker.js
|
||||
packages/lib/services/ExternalEditWatcher.js
|
||||
packages/lib/services/ExternalEditWatcher/utils.js
|
||||
@@ -600,14 +638,14 @@ packages/lib/services/ReportService.js
|
||||
packages/lib/services/ResourceEditWatcher/index.js
|
||||
packages/lib/services/ResourceEditWatcher/reducer.js
|
||||
packages/lib/services/ResourceFetcher.js
|
||||
packages/lib/services/ResourceService.js
|
||||
packages/lib/services/ResourceService.test.js
|
||||
packages/lib/services/RevisionService.js
|
||||
packages/lib/services/ResourceService.js
|
||||
packages/lib/services/RevisionService.test.js
|
||||
packages/lib/services/RevisionService.js
|
||||
packages/lib/services/SettingUtils.js
|
||||
packages/lib/services/UndoRedoService.js
|
||||
packages/lib/services/WhenClause.js
|
||||
packages/lib/services/WhenClause.test.js
|
||||
packages/lib/services/WhenClause.js
|
||||
packages/lib/services/commands/MenuUtils.js
|
||||
packages/lib/services/commands/ToolbarButtonUtils.js
|
||||
packages/lib/services/commands/commandsToMarkdownTable.js
|
||||
@@ -618,40 +656,40 @@ packages/lib/services/commands/stateToWhenClauseContext.js
|
||||
packages/lib/services/contextkey/contextkey.js
|
||||
packages/lib/services/database/types.js
|
||||
packages/lib/services/debug/populateDatabase.js
|
||||
packages/lib/services/e2ee/EncryptionService.js
|
||||
packages/lib/services/e2ee/EncryptionService.test.js
|
||||
packages/lib/services/e2ee/EncryptionService.js
|
||||
packages/lib/services/e2ee/RSA.node.js
|
||||
packages/lib/services/e2ee/ppk.js
|
||||
packages/lib/services/e2ee/ppk.test.js
|
||||
packages/lib/services/e2ee/ppk.js
|
||||
packages/lib/services/e2ee/ppkTestUtils.js
|
||||
packages/lib/services/e2ee/types.js
|
||||
packages/lib/services/e2ee/utils.js
|
||||
packages/lib/services/e2ee/utils.test.js
|
||||
packages/lib/services/interop/InteropService.js
|
||||
packages/lib/services/e2ee/utils.js
|
||||
packages/lib/services/interop/InteropService.test.js
|
||||
packages/lib/services/interop/InteropService.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Base.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Custom.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.test.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Jex.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Md.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Md.test.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Md_frontmatter.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Md.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Md_frontmatter.test.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Md_frontmatter.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Raw.js
|
||||
packages/lib/services/interop/InteropService_Importer_Base.js
|
||||
packages/lib/services/interop/InteropService_Importer_Custom.js
|
||||
packages/lib/services/interop/InteropService_Importer_EnexToHtml.js
|
||||
packages/lib/services/interop/InteropService_Importer_EnexToMd.js
|
||||
packages/lib/services/interop/InteropService_Importer_Jex.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md.test.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md_frontmatter.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md_frontmatter.test.js
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md_frontmatter.js
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.test.js
|
||||
packages/lib/services/interop/Module.js
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.js
|
||||
packages/lib/services/interop/Module.test.js
|
||||
packages/lib/services/interop/Module.js
|
||||
packages/lib/services/interop/types.js
|
||||
packages/lib/services/joplinServer/personalizedUserContentBaseUrl.js
|
||||
packages/lib/services/keychain/KeychainService.js
|
||||
@@ -698,23 +736,23 @@ packages/lib/services/plugins/utils/makeListener.js
|
||||
packages/lib/services/plugins/utils/manifestFromObject.js
|
||||
packages/lib/services/plugins/utils/mapEventHandlersToIds.js
|
||||
packages/lib/services/plugins/utils/types.js
|
||||
packages/lib/services/plugins/utils/validatePluginId.js
|
||||
packages/lib/services/plugins/utils/validatePluginId.test.js
|
||||
packages/lib/services/plugins/utils/validatePluginVersion.js
|
||||
packages/lib/services/plugins/utils/validatePluginId.js
|
||||
packages/lib/services/plugins/utils/validatePluginVersion.test.js
|
||||
packages/lib/services/profileConfig/index.js
|
||||
packages/lib/services/plugins/utils/validatePluginVersion.js
|
||||
packages/lib/services/profileConfig/index.test.js
|
||||
packages/lib/services/profileConfig/index.js
|
||||
packages/lib/services/profileConfig/initProfile.js
|
||||
packages/lib/services/profileConfig/mergeGlobalAndLocalSettings.js
|
||||
packages/lib/services/profileConfig/splitGlobalAndLocalSettings.js
|
||||
packages/lib/services/profileConfig/types.js
|
||||
packages/lib/services/rest/Api.js
|
||||
packages/lib/services/rest/Api.test.js
|
||||
packages/lib/services/rest/Api.js
|
||||
packages/lib/services/rest/ApiResponse.js
|
||||
packages/lib/services/rest/actionApi.desktop.js
|
||||
packages/lib/services/rest/routes/auth.js
|
||||
packages/lib/services/rest/routes/events.js
|
||||
packages/lib/services/rest/routes/events.test.js
|
||||
packages/lib/services/rest/routes/events.js
|
||||
packages/lib/services/rest/routes/folders.js
|
||||
packages/lib/services/rest/routes/master_keys.js
|
||||
packages/lib/services/rest/routes/notes.js
|
||||
@@ -732,26 +770,26 @@ packages/lib/services/rest/utils/readonlyProperties.js
|
||||
packages/lib/services/rest/utils/requestFields.js
|
||||
packages/lib/services/rest/utils/requestPaginationOptions.js
|
||||
packages/lib/services/searchengine/SearchEngine.js
|
||||
packages/lib/services/searchengine/SearchEngineUtils.js
|
||||
packages/lib/services/searchengine/SearchEngineUtils.test.js
|
||||
packages/lib/services/searchengine/SearchEngineUtils.js
|
||||
packages/lib/services/searchengine/SearchFilter.test.js
|
||||
packages/lib/services/searchengine/filterParser.js
|
||||
packages/lib/services/searchengine/filterParser.test.js
|
||||
packages/lib/services/searchengine/gotoAnythingStyleQuery.js
|
||||
packages/lib/services/searchengine/filterParser.js
|
||||
packages/lib/services/searchengine/gotoAnythingStyleQuery.test.js
|
||||
packages/lib/services/searchengine/gotoAnythingStyleQuery.js
|
||||
packages/lib/services/searchengine/queryBuilder.js
|
||||
packages/lib/services/share/ShareService.js
|
||||
packages/lib/services/share/ShareService.test.js
|
||||
packages/lib/services/share/ShareService.js
|
||||
packages/lib/services/share/reducer.js
|
||||
packages/lib/services/spellChecker/SpellCheckerService.js
|
||||
packages/lib/services/spellChecker/SpellCheckerServiceDriverBase.js
|
||||
packages/lib/services/style/cssToTheme.js
|
||||
packages/lib/services/style/cssToTheme.test.js
|
||||
packages/lib/services/style/cssToTheme.js
|
||||
packages/lib/services/style/loadCssToTheme.js
|
||||
packages/lib/services/style/themeToCss.js
|
||||
packages/lib/services/style/themeToCss.test.js
|
||||
packages/lib/services/synchronizer/ItemUploader.js
|
||||
packages/lib/services/style/themeToCss.js
|
||||
packages/lib/services/synchronizer/ItemUploader.test.js
|
||||
packages/lib/services/synchronizer/ItemUploader.js
|
||||
packages/lib/services/synchronizer/LockHandler.js
|
||||
packages/lib/services/synchronizer/MigrationHandler.js
|
||||
packages/lib/services/synchronizer/Synchronizer.basics.test.js
|
||||
@@ -768,8 +806,8 @@ packages/lib/services/synchronizer/migrations/1.js
|
||||
packages/lib/services/synchronizer/migrations/2.js
|
||||
packages/lib/services/synchronizer/migrations/3.js
|
||||
packages/lib/services/synchronizer/syncDebugLog.js
|
||||
packages/lib/services/synchronizer/syncInfoUtils.js
|
||||
packages/lib/services/synchronizer/syncInfoUtils.test.js
|
||||
packages/lib/services/synchronizer/syncInfoUtils.js
|
||||
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js
|
||||
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js
|
||||
packages/lib/services/synchronizer/tools.js
|
||||
@@ -795,11 +833,12 @@ packages/lib/themes/type.js
|
||||
packages/lib/time.js
|
||||
packages/lib/utils/credentialFiles.js
|
||||
packages/lib/utils/joplinCloud.js
|
||||
packages/lib/utils/webDAVUtils.js
|
||||
packages/lib/utils/userFetcher.js
|
||||
packages/lib/utils/webDAVUtils.test.js
|
||||
packages/lib/utils/webDAVUtils.js
|
||||
packages/lib/uuid.js
|
||||
packages/lib/versionInfo.js
|
||||
packages/lib/versionInfo.test.js
|
||||
packages/lib/versionInfo.js
|
||||
packages/pdf-viewer/FullViewer.js
|
||||
packages/pdf-viewer/Page.js
|
||||
packages/pdf-viewer/PdfDocument.js
|
||||
@@ -820,30 +859,31 @@ packages/pdf-viewer/ui/IconButtons.js
|
||||
packages/pdf-viewer/ui/ZoomControls.js
|
||||
packages/plugin-repo-cli/commands/updateRelease.js
|
||||
packages/plugin-repo-cli/index.js
|
||||
packages/plugin-repo-cli/lib/checkIfPluginCanBeAdded.js
|
||||
packages/plugin-repo-cli/lib/checkIfPluginCanBeAdded.test.js
|
||||
packages/plugin-repo-cli/lib/errorsHaveChanged.js
|
||||
packages/plugin-repo-cli/lib/checkIfPluginCanBeAdded.js
|
||||
packages/plugin-repo-cli/lib/errorsHaveChanged.test.js
|
||||
packages/plugin-repo-cli/lib/gitCompareUrl.js
|
||||
packages/plugin-repo-cli/lib/errorsHaveChanged.js
|
||||
packages/plugin-repo-cli/lib/gitCompareUrl.test.js
|
||||
packages/plugin-repo-cli/lib/overrideUtils.js
|
||||
packages/plugin-repo-cli/lib/gitCompareUrl.js
|
||||
packages/plugin-repo-cli/lib/overrideUtils.test.js
|
||||
packages/plugin-repo-cli/lib/overrideUtils.js
|
||||
packages/plugin-repo-cli/lib/types.js
|
||||
packages/plugin-repo-cli/lib/updateReadme.js
|
||||
packages/plugin-repo-cli/lib/updateReadme.test.js
|
||||
packages/plugin-repo-cli/lib/updateReadme.js
|
||||
packages/plugin-repo-cli/lib/utils.js
|
||||
packages/plugins/ToggleSidebars/api/index.js
|
||||
packages/plugins/ToggleSidebars/api/types.js
|
||||
packages/plugins/ToggleSidebars/src/index.js
|
||||
packages/react-native-saf-x/src/index.js
|
||||
packages/renderer/HtmlToHtml.test.js
|
||||
packages/renderer/HtmlToHtml.js
|
||||
packages/renderer/InMemoryCache.js
|
||||
packages/renderer/MarkupToHtml.js
|
||||
packages/renderer/MdToHtml.js
|
||||
packages/renderer/MdToHtml/createEventHandlingAttrs.js
|
||||
packages/renderer/MdToHtml/createEventHandlingAttrs.test.js
|
||||
packages/renderer/MdToHtml/linkReplacement.js
|
||||
packages/renderer/MdToHtml/createEventHandlingAttrs.js
|
||||
packages/renderer/MdToHtml/linkReplacement.test.js
|
||||
packages/renderer/MdToHtml/linkReplacement.js
|
||||
packages/renderer/MdToHtml/renderMedia.js
|
||||
packages/renderer/MdToHtml/rules/checkbox.js
|
||||
packages/renderer/MdToHtml/rules/code_inline.js
|
||||
@@ -862,27 +902,28 @@ packages/renderer/MdToHtml/setupLinkify.js
|
||||
packages/renderer/MdToHtml/validateLinks.js
|
||||
packages/renderer/headerAnchor.js
|
||||
packages/renderer/highlight.js
|
||||
packages/renderer/htmlUtils.js
|
||||
packages/renderer/htmlUtils.test.js
|
||||
packages/renderer/htmlUtils.js
|
||||
packages/renderer/index.js
|
||||
packages/renderer/noteStyle.js
|
||||
packages/renderer/pathUtils.js
|
||||
packages/renderer/utils.js
|
||||
packages/tools/build-release-stats.js
|
||||
packages/tools/build-welcome.js
|
||||
packages/tools/buildServerDocker.js
|
||||
packages/tools/buildServerDocker.test.js
|
||||
packages/tools/bundleDefaultPlugins.js
|
||||
packages/tools/buildServerDocker.js
|
||||
packages/tools/bundleDefaultPlugins.test.js
|
||||
packages/tools/bundleDefaultPlugins.js
|
||||
packages/tools/checkIgnoredFiles.js
|
||||
packages/tools/checkLibPaths.js
|
||||
packages/tools/checkLibPaths.test.js
|
||||
packages/tools/checkLibPaths.js
|
||||
packages/tools/convertThemesToCss.js
|
||||
packages/tools/generate-database-types.js
|
||||
packages/tools/generate-images.js
|
||||
packages/tools/git-changelog.js
|
||||
packages/tools/git-changelog.test.js
|
||||
packages/tools/git-changelog.js
|
||||
packages/tools/licenseChecker.js
|
||||
packages/tools/packageJsonLint.js
|
||||
packages/tools/release-android.js
|
||||
packages/tools/release-cli.js
|
||||
packages/tools/release-electron.js
|
||||
@@ -893,8 +934,9 @@ packages/tools/setupNewRelease.js
|
||||
packages/tools/spellcheck.js
|
||||
packages/tools/tagServerLatest.js
|
||||
packages/tools/tool-utils.js
|
||||
packages/tools/update-readme-download.js
|
||||
packages/tools/update-readme-contributors.js
|
||||
packages/tools/update-readme-download.test.js
|
||||
packages/tools/update-readme-download.js
|
||||
packages/tools/update-readme-sponsors.js
|
||||
packages/tools/updateMarkdownDoc.js
|
||||
packages/tools/utils/discourse.js
|
||||
@@ -904,14 +946,14 @@ packages/tools/website/build.js
|
||||
packages/tools/website/buildTranslations.js
|
||||
packages/tools/website/updateDownloadPage.js
|
||||
packages/tools/website/updateNews.js
|
||||
packages/tools/website/utils/applyTranslations.js
|
||||
packages/tools/website/utils/applyTranslations.test.js
|
||||
packages/tools/website/utils/convertLinksToLocale.js
|
||||
packages/tools/website/utils/applyTranslations.js
|
||||
packages/tools/website/utils/convertLinksToLocale.test.js
|
||||
packages/tools/website/utils/convertLinksToLocale.js
|
||||
packages/tools/website/utils/frontMatter.js
|
||||
packages/tools/website/utils/news.js
|
||||
packages/tools/website/utils/openGraph.js
|
||||
packages/tools/website/utils/openGraph.test.js
|
||||
packages/tools/website/utils/openGraph.js
|
||||
packages/tools/website/utils/parser.js
|
||||
packages/tools/website/utils/pressCarousel.js
|
||||
packages/tools/website/utils/processTranslations.js
|
||||
|
||||
+2
-2
@@ -119,7 +119,7 @@ module.exports = {
|
||||
'objects': 'always-multiline',
|
||||
'imports': 'always-multiline',
|
||||
'exports': 'always-multiline',
|
||||
'functions': 'never',
|
||||
'functions': 'always-multiline',
|
||||
}],
|
||||
'comma-spacing': ['error', { 'before': false, 'after': true }],
|
||||
'no-trailing-spaces': 'error',
|
||||
@@ -209,7 +209,7 @@ module.exports = {
|
||||
'enums': 'always-multiline',
|
||||
'generics': 'always-multiline',
|
||||
'tuples': 'always-multiline',
|
||||
'functions': 'never',
|
||||
'functions': 'always-multiline',
|
||||
}],
|
||||
'@typescript-eslint/object-curly-spacing': ['error', 'always'],
|
||||
'@typescript-eslint/semi': ['error', 'always'],
|
||||
|
||||
@@ -171,6 +171,21 @@ if [ "$IS_PULL_REQUEST" == "1" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Check that the website still builds
|
||||
# =============================================================================
|
||||
|
||||
if [ "$IS_PULL_REQUEST" == "1" ] || [ "$IS_DEV_BRANCH" = "1" ]; then
|
||||
echo "Step: Check that the website still builds..."
|
||||
|
||||
mkdir -p ../joplin-website/docs
|
||||
SKIP_SPONSOR_PROCESSING=1 yarn run buildWebsite
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Find out if we should run the build or not. Electron-builder gets stuck when
|
||||
# building PRs so we disable it in this case. The Linux build should provide
|
||||
|
||||
@@ -25,17 +25,18 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update || true
|
||||
sudo apt-get install -y libsecret-1-dev
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Yarn
|
||||
run: |
|
||||
corepack enable
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install
|
||||
run: yarn install
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ jobs:
|
||||
# We need to pin the version to 18.15, because 18.16+ fails with this error:
|
||||
# https://github.com/facebook/react-native/issues/36440
|
||||
node-version: '18.15.0'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Yarn
|
||||
run: |
|
||||
|
||||
@@ -75,6 +75,7 @@ jobs:
|
||||
# We need to pin the version to 18.15, because 18.16+ fails with this error:
|
||||
# https://github.com/facebook/react-native/issues/36440
|
||||
node-version: '18.15.0'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Yarn
|
||||
run: |
|
||||
@@ -160,6 +161,7 @@ jobs:
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Yarn
|
||||
run: |
|
||||
@@ -183,7 +185,7 @@ jobs:
|
||||
docker run -p 22300:22300 joplin/server:0.0.0-beta node dist/app.js --env dev &
|
||||
|
||||
# Wait for server to start
|
||||
sleep 10
|
||||
sleep 30
|
||||
|
||||
# Check if status code is correct
|
||||
# if the actual_status DOES NOT include the expected_status
|
||||
|
||||
+130
-87
@@ -50,6 +50,7 @@ packages/tools/github_oauth_token.txt
|
||||
lerna-debug.log
|
||||
.env
|
||||
docs/**/*.mustache
|
||||
.idea
|
||||
|
||||
# Yarn stuff
|
||||
# https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored
|
||||
@@ -67,8 +68,8 @@ packages/app-cli/app/LinkSelector.js
|
||||
packages/app-cli/app/base-command.js
|
||||
packages/app-cli/app/command-done.test.js
|
||||
packages/app-cli/app/command-e2ee.js
|
||||
packages/app-cli/app/command-mkbook.js
|
||||
packages/app-cli/app/command-mkbook.test.js
|
||||
packages/app-cli/app/command-mkbook.js
|
||||
packages/app-cli/app/command-settingschema.js
|
||||
packages/app-cli/app/command-sync.js
|
||||
packages/app-cli/app/command-testing.js
|
||||
@@ -77,6 +78,7 @@ packages/app-cli/app/services/plugins/PluginRunner.js
|
||||
packages/app-cli/app/setupCommand.js
|
||||
packages/app-cli/app/utils/testUtils.js
|
||||
packages/app-cli/tests/HtmlToMd.js
|
||||
packages/app-cli/tests/MarkupToHtml.js
|
||||
packages/app-cli/tests/MdToHtml.js
|
||||
packages/app-cli/tests/services/keychain/KeychainService.js
|
||||
packages/app-cli/tests/services/plugins/PluginService.js
|
||||
@@ -90,9 +92,9 @@ packages/app-cli/tests/testUtils.js
|
||||
packages/app-cli/tools/populateDatabase.js
|
||||
packages/app-desktop/ElectronAppWrapper.js
|
||||
packages/app-desktop/InteropServiceHelper.js
|
||||
packages/app-desktop/app.js
|
||||
packages/app-desktop/app.reducer.js
|
||||
packages/app-desktop/app.reducer.test.js
|
||||
packages/app-desktop/app.reducer.js
|
||||
packages/app-desktop/app.js
|
||||
packages/app-desktop/bridge.js
|
||||
packages/app-desktop/checkForUpdates.js
|
||||
packages/app-desktop/commands/copyDevCommand.js
|
||||
@@ -117,11 +119,12 @@ packages/app-desktop/gui/ClipperConfigScreen.js
|
||||
packages/app-desktop/gui/ConfigScreen/ButtonBar.js
|
||||
packages/app-desktop/gui/ConfigScreen/ConfigScreen.js
|
||||
packages/app-desktop/gui/ConfigScreen/Sidebar.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/ToggleAdvancedSettingsButton.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginBox.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/SearchPlugins.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/useOnInstallHandler.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/useOnInstallHandler.test.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/useOnInstallHandler.js
|
||||
packages/app-desktop/gui/Dialog.js
|
||||
packages/app-desktop/gui/DialogButtonRow.js
|
||||
packages/app-desktop/gui/DialogButtonRow/useKeyboardHandler.js
|
||||
@@ -139,6 +142,7 @@ packages/app-desktop/gui/HelpButton.js
|
||||
packages/app-desktop/gui/IconButton.js
|
||||
packages/app-desktop/gui/ImportScreen.js
|
||||
packages/app-desktop/gui/ItemList.js
|
||||
packages/app-desktop/gui/JoplinCloudConfigScreen.js
|
||||
packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js
|
||||
packages/app-desktop/gui/KeymapConfig/ShortcutRecorder.js
|
||||
packages/app-desktop/gui/KeymapConfig/styles/index.js
|
||||
@@ -202,8 +206,8 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/Toolbar.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/styles/index.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/index.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.test.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearch.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useExternalPlugins.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinCommands.js
|
||||
@@ -231,16 +235,18 @@ packages/app-desktop/gui/NoteEditor/commands/showLocalSearch.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/showRevisions.js
|
||||
packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.js
|
||||
packages/app-desktop/gui/NoteEditor/styles/index.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/clipboardUtils.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/clipboardUtils.test.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/clipboardUtils.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/contextMenu.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/contextMenuUtils.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/index.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/resourceHandling.test.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/resourceHandling.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/types.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useDropHandler.js
|
||||
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
|
||||
@@ -249,13 +255,36 @@ packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useWindowCommandHandler.js
|
||||
packages/app-desktop/gui/NoteList/NoteList.js
|
||||
packages/app-desktop/gui/NoteList/NoteList2.js
|
||||
packages/app-desktop/gui/NoteList/NoteListSource.js
|
||||
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js
|
||||
packages/app-desktop/gui/NoteList/commands/index.js
|
||||
packages/app-desktop/gui/NoteList/types.js
|
||||
packages/app-desktop/gui/NoteList/utils/canManuallySortNotes.js
|
||||
packages/app-desktop/gui/NoteList/utils/defaultLeftToRightListRenderer.js
|
||||
packages/app-desktop/gui/NoteList/utils/defaultListRenderer.js
|
||||
packages/app-desktop/gui/NoteList/utils/getNoteTitleHtml.js
|
||||
packages/app-desktop/gui/NoteList/utils/prepareViewProps.js
|
||||
packages/app-desktop/gui/NoteList/utils/types.js
|
||||
packages/app-desktop/gui/NoteList/utils/useDragAndDrop.js
|
||||
packages/app-desktop/gui/NoteList/utils/useFocusNote.js
|
||||
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/useRenderedNotes.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
|
||||
packages/app-desktop/gui/NoteListControls/NoteListControls.js
|
||||
packages/app-desktop/gui/NoteListControls/commands/focusSearch.js
|
||||
packages/app-desktop/gui/NoteListControls/commands/index.js
|
||||
packages/app-desktop/gui/NoteListItem.js
|
||||
packages/app-desktop/gui/NoteListItem/NoteListItem.js
|
||||
packages/app-desktop/gui/NoteListItem/utils/types.js
|
||||
packages/app-desktop/gui/NoteListItem/utils/useItemElement.js
|
||||
packages/app-desktop/gui/NoteListItem/utils/useItemEventHandlers.js
|
||||
packages/app-desktop/gui/NoteListItem/utils/useOnContextMenu.js
|
||||
packages/app-desktop/gui/NoteListItem/utils/useRootElement.js
|
||||
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js
|
||||
packages/app-desktop/gui/NotePropertiesDialog.js
|
||||
packages/app-desktop/gui/NoteRevisionViewer.js
|
||||
@@ -273,17 +302,17 @@ packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/movements.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/movements.test.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/persist.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/movements.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/persist.test.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/persist.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/style.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/types.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js
|
||||
packages/app-desktop/gui/ResourceScreen.js
|
||||
@@ -342,14 +371,15 @@ packages/app-desktop/services/plugins/hooks/useViewIsReady.js
|
||||
packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js
|
||||
packages/app-desktop/services/restart.js
|
||||
packages/app-desktop/services/share/invitationRespond.js
|
||||
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.js
|
||||
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.js
|
||||
packages/app-desktop/services/sortOrder/notesSortOrderUtils.js
|
||||
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.js
|
||||
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.js
|
||||
packages/app-desktop/services/sortOrder/notesSortOrderUtils.js
|
||||
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.js
|
||||
packages/app-desktop/tools/notarizeMacApp.js
|
||||
packages/app-desktop/utils/checkForUpdatesUtils.js
|
||||
packages/app-desktop/utils/checkForUpdatesUtils.test.js
|
||||
packages/app-desktop/utils/checkForUpdatesUtils.js
|
||||
packages/app-desktop/utils/checkForUpdatesUtilsTestData.js
|
||||
packages/app-desktop/utils/markupLanguageUtils.js
|
||||
packages/app-mobile/PluginAssetsLoader.js
|
||||
packages/app-mobile/components/ActionButton.js
|
||||
@@ -364,17 +394,17 @@ packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js
|
||||
packages/app-mobile/components/NoteBodyViewer/hooks/useOnMessage.js
|
||||
packages/app-mobile/components/NoteBodyViewer/hooks/useOnResourceLongPress.js
|
||||
packages/app-mobile/components/NoteBodyViewer/hooks/useSource.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/decoratorExtension.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.bulletedVsChecklist.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.toggleList.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownCommands.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownMathParser.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.test.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/markdownReformatter.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/syntaxHighlightingLanguages.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/createEditor.js
|
||||
packages/app-mobile/components/NoteEditor/CodeMirror/testUtil/forceFullParse.js
|
||||
@@ -390,6 +420,7 @@ packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarButton.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarOverflowRows.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownToolbar/types.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.test.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.js
|
||||
packages/app-mobile/components/NoteEditor/SearchPanel.js
|
||||
packages/app-mobile/components/NoteEditor/SelectionFormatting.js
|
||||
@@ -405,9 +436,14 @@ packages/app-mobile/components/app-nav.js
|
||||
packages/app-mobile/components/biometrics/BiometricPopup.js
|
||||
packages/app-mobile/components/biometrics/biometricAuthenticate.js
|
||||
packages/app-mobile/components/biometrics/sensorInfo.js
|
||||
packages/app-mobile/components/getResponsiveValue.js
|
||||
packages/app-mobile/components/getResponsiveValue.test.js
|
||||
packages/app-mobile/components/screens/ConfigScreen.js
|
||||
packages/app-mobile/components/getResponsiveValue.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/ConfigScreen.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/ConfigScreenButton.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/NoteExportButton.test.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/NoteExportButton.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/exportAllFolders.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.js
|
||||
packages/app-mobile/components/screens/Note.js
|
||||
packages/app-mobile/components/screens/Notes.js
|
||||
packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js
|
||||
@@ -428,6 +464,7 @@ packages/app-mobile/tools/buildInjectedJs.js
|
||||
packages/app-mobile/utils/ShareExtension.js
|
||||
packages/app-mobile/utils/ShareUtils.js
|
||||
packages/app-mobile/utils/TlsUtils.js
|
||||
packages/app-mobile/utils/autodetectTheme.js
|
||||
packages/app-mobile/utils/checkPermissions.js
|
||||
packages/app-mobile/utils/createRootStyle.js
|
||||
packages/app-mobile/utils/debounce.js
|
||||
@@ -436,19 +473,19 @@ packages/app-mobile/utils/setupNotifications.js
|
||||
packages/app-mobile/utils/shareHandler.js
|
||||
packages/app-mobile/utils/types.js
|
||||
packages/fork-htmlparser2/src/CollectingHandler.js
|
||||
packages/fork-htmlparser2/src/FeedHandler.js
|
||||
packages/fork-htmlparser2/src/FeedHandler.spec.js
|
||||
packages/fork-htmlparser2/src/FeedHandler.js
|
||||
packages/fork-htmlparser2/src/MultiplexHandler.js
|
||||
packages/fork-htmlparser2/src/Parser.js
|
||||
packages/fork-htmlparser2/src/Parser.spec.js
|
||||
packages/fork-htmlparser2/src/Parser.js
|
||||
packages/fork-htmlparser2/src/Tokenizer.js
|
||||
packages/fork-htmlparser2/src/WritableStream.js
|
||||
packages/fork-htmlparser2/src/WritableStream.spec.js
|
||||
packages/fork-htmlparser2/src/WritableStream.js
|
||||
packages/fork-htmlparser2/src/__fixtures__/test-helper.js
|
||||
packages/fork-htmlparser2/src/__tests__/events.js
|
||||
packages/fork-htmlparser2/src/__tests__/stream.js
|
||||
packages/fork-htmlparser2/src/index.js
|
||||
packages/fork-htmlparser2/src/index.spec.js
|
||||
packages/fork-htmlparser2/src/index.js
|
||||
packages/generator-joplin/generators/app/templates/api/index.js
|
||||
packages/generator-joplin/generators/app/templates/api/types.js
|
||||
packages/generator-joplin/generators/app/templates/api_index.js
|
||||
@@ -461,19 +498,18 @@ packages/lib/BaseModel.js
|
||||
packages/lib/BaseSyncTarget.js
|
||||
packages/lib/ClipperServer.js
|
||||
packages/lib/CssUtils.js
|
||||
packages/lib/EventDispatcher.js
|
||||
packages/lib/EventDispatcher.test.js
|
||||
packages/lib/EventDispatcher.js
|
||||
packages/lib/HtmlToMd.js
|
||||
packages/lib/InMemoryCache.js
|
||||
packages/lib/InMemoryCache.test.js
|
||||
packages/lib/InMemoryCache.js
|
||||
packages/lib/JoplinDatabase.js
|
||||
packages/lib/JoplinError.js
|
||||
packages/lib/JoplinServerApi.js
|
||||
packages/lib/Logger.js
|
||||
packages/lib/ObjectUtils.js
|
||||
packages/lib/PoorManIntervals.js
|
||||
packages/lib/RotatingLogs.js
|
||||
packages/lib/RotatingLogs.test.js
|
||||
packages/lib/RotatingLogs.js
|
||||
packages/lib/SyncTargetJoplinCloud.js
|
||||
packages/lib/SyncTargetJoplinServer.js
|
||||
packages/lib/SyncTargetNone.js
|
||||
@@ -483,14 +519,16 @@ packages/lib/Synchronizer.js
|
||||
packages/lib/TaskQueue.js
|
||||
packages/lib/WelcomeUtils.js
|
||||
packages/lib/array.js
|
||||
packages/lib/callbackUrlUtils.js
|
||||
packages/lib/callbackUrlUtils.test.js
|
||||
packages/lib/callbackUrlUtils.js
|
||||
packages/lib/commands/historyBackward.js
|
||||
packages/lib/commands/historyForward.js
|
||||
packages/lib/commands/index.js
|
||||
packages/lib/commands/openMasterPasswordDialog.js
|
||||
packages/lib/commands/synchronize.js
|
||||
packages/lib/components/EncryptionConfigScreen/utils.js
|
||||
packages/lib/components/shared/config/shouldShowMissingPasswordWarning.test.js
|
||||
packages/lib/components/shared/config/shouldShowMissingPasswordWarning.js
|
||||
packages/lib/components/shared/note-screen-shared.js
|
||||
packages/lib/components/shared/reduxSharedMiddleware.js
|
||||
packages/lib/database-driver-better-sqlite.js
|
||||
@@ -512,41 +550,42 @@ packages/lib/geolocation-node.js
|
||||
packages/lib/hooks/useAsyncEffect.js
|
||||
packages/lib/hooks/useElementSize.js
|
||||
packages/lib/hooks/useEventListener.js
|
||||
packages/lib/htmlUtils.js
|
||||
packages/lib/htmlUtils.test.js
|
||||
packages/lib/htmlUtils.js
|
||||
packages/lib/htmlUtils2.test.js
|
||||
packages/lib/import-enex-md-gen.js
|
||||
packages/lib/import-enex-md-gen.test.js
|
||||
packages/lib/import-enex-md-gen.js
|
||||
packages/lib/import-enex.js
|
||||
packages/lib/locale.js
|
||||
packages/lib/initLib.js
|
||||
packages/lib/locale.test.js
|
||||
packages/lib/markdownUtils.js
|
||||
packages/lib/locale.js
|
||||
packages/lib/markdownUtils.test.js
|
||||
packages/lib/markdownUtils.js
|
||||
packages/lib/markdownUtils2.test.js
|
||||
packages/lib/markupLanguageUtils.js
|
||||
packages/lib/migrations/42.js
|
||||
packages/lib/models/Alarm.js
|
||||
packages/lib/models/BaseItem.js
|
||||
packages/lib/models/Folder.js
|
||||
packages/lib/models/Folder.sharing.test.js
|
||||
packages/lib/models/Folder.test.js
|
||||
packages/lib/models/ItemChange.js
|
||||
packages/lib/models/Folder.js
|
||||
packages/lib/models/ItemChange.test.js
|
||||
packages/lib/models/MasterKey.js
|
||||
packages/lib/models/ItemChange.js
|
||||
packages/lib/models/MasterKey.test.js
|
||||
packages/lib/models/MasterKey.js
|
||||
packages/lib/models/Migration.js
|
||||
packages/lib/models/Note.js
|
||||
packages/lib/models/Note.test.js
|
||||
packages/lib/models/Note.js
|
||||
packages/lib/models/NoteResource.js
|
||||
packages/lib/models/NoteTag.js
|
||||
packages/lib/models/Resource.js
|
||||
packages/lib/models/Resource.test.js
|
||||
packages/lib/models/Resource.js
|
||||
packages/lib/models/ResourceLocalState.js
|
||||
packages/lib/models/Revision.js
|
||||
packages/lib/models/Revision.test.js
|
||||
packages/lib/models/Revision.js
|
||||
packages/lib/models/Search.js
|
||||
packages/lib/models/Setting.js
|
||||
packages/lib/models/Setting.test.js
|
||||
packages/lib/models/Setting.js
|
||||
packages/lib/models/SmartFilter.js
|
||||
packages/lib/models/Tag.js
|
||||
packages/lib/models/dateTimeFormats.test.js
|
||||
@@ -556,20 +595,20 @@ packages/lib/models/utils/paginatedFeed.js
|
||||
packages/lib/models/utils/paginationToSql.js
|
||||
packages/lib/models/utils/readOnly.js
|
||||
packages/lib/models/utils/types.js
|
||||
packages/lib/models/utils/userData.js
|
||||
packages/lib/models/utils/userData.test.js
|
||||
packages/lib/models/utils/userData.js
|
||||
packages/lib/net-utils.js
|
||||
packages/lib/ntp.js
|
||||
packages/lib/onedrive-api.js
|
||||
packages/lib/path-utils.js
|
||||
packages/lib/reducer.js
|
||||
packages/lib/registry.js
|
||||
packages/lib/registry.test.js
|
||||
packages/lib/registry.js
|
||||
packages/lib/services/AlarmService.js
|
||||
packages/lib/services/AlarmServiceDriverNode.js
|
||||
packages/lib/services/BaseService.js
|
||||
packages/lib/services/CommandService.js
|
||||
packages/lib/services/CommandService.test.js
|
||||
packages/lib/services/CommandService.js
|
||||
packages/lib/services/DecryptionWorker.js
|
||||
packages/lib/services/ExternalEditWatcher.js
|
||||
packages/lib/services/ExternalEditWatcher/utils.js
|
||||
@@ -585,14 +624,14 @@ packages/lib/services/ReportService.js
|
||||
packages/lib/services/ResourceEditWatcher/index.js
|
||||
packages/lib/services/ResourceEditWatcher/reducer.js
|
||||
packages/lib/services/ResourceFetcher.js
|
||||
packages/lib/services/ResourceService.js
|
||||
packages/lib/services/ResourceService.test.js
|
||||
packages/lib/services/RevisionService.js
|
||||
packages/lib/services/ResourceService.js
|
||||
packages/lib/services/RevisionService.test.js
|
||||
packages/lib/services/RevisionService.js
|
||||
packages/lib/services/SettingUtils.js
|
||||
packages/lib/services/UndoRedoService.js
|
||||
packages/lib/services/WhenClause.js
|
||||
packages/lib/services/WhenClause.test.js
|
||||
packages/lib/services/WhenClause.js
|
||||
packages/lib/services/commands/MenuUtils.js
|
||||
packages/lib/services/commands/ToolbarButtonUtils.js
|
||||
packages/lib/services/commands/commandsToMarkdownTable.js
|
||||
@@ -603,40 +642,40 @@ packages/lib/services/commands/stateToWhenClauseContext.js
|
||||
packages/lib/services/contextkey/contextkey.js
|
||||
packages/lib/services/database/types.js
|
||||
packages/lib/services/debug/populateDatabase.js
|
||||
packages/lib/services/e2ee/EncryptionService.js
|
||||
packages/lib/services/e2ee/EncryptionService.test.js
|
||||
packages/lib/services/e2ee/EncryptionService.js
|
||||
packages/lib/services/e2ee/RSA.node.js
|
||||
packages/lib/services/e2ee/ppk.js
|
||||
packages/lib/services/e2ee/ppk.test.js
|
||||
packages/lib/services/e2ee/ppk.js
|
||||
packages/lib/services/e2ee/ppkTestUtils.js
|
||||
packages/lib/services/e2ee/types.js
|
||||
packages/lib/services/e2ee/utils.js
|
||||
packages/lib/services/e2ee/utils.test.js
|
||||
packages/lib/services/interop/InteropService.js
|
||||
packages/lib/services/e2ee/utils.js
|
||||
packages/lib/services/interop/InteropService.test.js
|
||||
packages/lib/services/interop/InteropService.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Base.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Custom.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.test.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Html.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Jex.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Md.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Md.test.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Md_frontmatter.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Md.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Md_frontmatter.test.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Md_frontmatter.js
|
||||
packages/lib/services/interop/InteropService_Exporter_Raw.js
|
||||
packages/lib/services/interop/InteropService_Importer_Base.js
|
||||
packages/lib/services/interop/InteropService_Importer_Custom.js
|
||||
packages/lib/services/interop/InteropService_Importer_EnexToHtml.js
|
||||
packages/lib/services/interop/InteropService_Importer_EnexToMd.js
|
||||
packages/lib/services/interop/InteropService_Importer_Jex.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md.test.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md_frontmatter.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md_frontmatter.test.js
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.js
|
||||
packages/lib/services/interop/InteropService_Importer_Md_frontmatter.js
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.test.js
|
||||
packages/lib/services/interop/Module.js
|
||||
packages/lib/services/interop/InteropService_Importer_Raw.js
|
||||
packages/lib/services/interop/Module.test.js
|
||||
packages/lib/services/interop/Module.js
|
||||
packages/lib/services/interop/types.js
|
||||
packages/lib/services/joplinServer/personalizedUserContentBaseUrl.js
|
||||
packages/lib/services/keychain/KeychainService.js
|
||||
@@ -683,23 +722,23 @@ packages/lib/services/plugins/utils/makeListener.js
|
||||
packages/lib/services/plugins/utils/manifestFromObject.js
|
||||
packages/lib/services/plugins/utils/mapEventHandlersToIds.js
|
||||
packages/lib/services/plugins/utils/types.js
|
||||
packages/lib/services/plugins/utils/validatePluginId.js
|
||||
packages/lib/services/plugins/utils/validatePluginId.test.js
|
||||
packages/lib/services/plugins/utils/validatePluginVersion.js
|
||||
packages/lib/services/plugins/utils/validatePluginId.js
|
||||
packages/lib/services/plugins/utils/validatePluginVersion.test.js
|
||||
packages/lib/services/profileConfig/index.js
|
||||
packages/lib/services/plugins/utils/validatePluginVersion.js
|
||||
packages/lib/services/profileConfig/index.test.js
|
||||
packages/lib/services/profileConfig/index.js
|
||||
packages/lib/services/profileConfig/initProfile.js
|
||||
packages/lib/services/profileConfig/mergeGlobalAndLocalSettings.js
|
||||
packages/lib/services/profileConfig/splitGlobalAndLocalSettings.js
|
||||
packages/lib/services/profileConfig/types.js
|
||||
packages/lib/services/rest/Api.js
|
||||
packages/lib/services/rest/Api.test.js
|
||||
packages/lib/services/rest/Api.js
|
||||
packages/lib/services/rest/ApiResponse.js
|
||||
packages/lib/services/rest/actionApi.desktop.js
|
||||
packages/lib/services/rest/routes/auth.js
|
||||
packages/lib/services/rest/routes/events.js
|
||||
packages/lib/services/rest/routes/events.test.js
|
||||
packages/lib/services/rest/routes/events.js
|
||||
packages/lib/services/rest/routes/folders.js
|
||||
packages/lib/services/rest/routes/master_keys.js
|
||||
packages/lib/services/rest/routes/notes.js
|
||||
@@ -717,26 +756,26 @@ packages/lib/services/rest/utils/readonlyProperties.js
|
||||
packages/lib/services/rest/utils/requestFields.js
|
||||
packages/lib/services/rest/utils/requestPaginationOptions.js
|
||||
packages/lib/services/searchengine/SearchEngine.js
|
||||
packages/lib/services/searchengine/SearchEngineUtils.js
|
||||
packages/lib/services/searchengine/SearchEngineUtils.test.js
|
||||
packages/lib/services/searchengine/SearchEngineUtils.js
|
||||
packages/lib/services/searchengine/SearchFilter.test.js
|
||||
packages/lib/services/searchengine/filterParser.js
|
||||
packages/lib/services/searchengine/filterParser.test.js
|
||||
packages/lib/services/searchengine/gotoAnythingStyleQuery.js
|
||||
packages/lib/services/searchengine/filterParser.js
|
||||
packages/lib/services/searchengine/gotoAnythingStyleQuery.test.js
|
||||
packages/lib/services/searchengine/gotoAnythingStyleQuery.js
|
||||
packages/lib/services/searchengine/queryBuilder.js
|
||||
packages/lib/services/share/ShareService.js
|
||||
packages/lib/services/share/ShareService.test.js
|
||||
packages/lib/services/share/ShareService.js
|
||||
packages/lib/services/share/reducer.js
|
||||
packages/lib/services/spellChecker/SpellCheckerService.js
|
||||
packages/lib/services/spellChecker/SpellCheckerServiceDriverBase.js
|
||||
packages/lib/services/style/cssToTheme.js
|
||||
packages/lib/services/style/cssToTheme.test.js
|
||||
packages/lib/services/style/cssToTheme.js
|
||||
packages/lib/services/style/loadCssToTheme.js
|
||||
packages/lib/services/style/themeToCss.js
|
||||
packages/lib/services/style/themeToCss.test.js
|
||||
packages/lib/services/synchronizer/ItemUploader.js
|
||||
packages/lib/services/style/themeToCss.js
|
||||
packages/lib/services/synchronizer/ItemUploader.test.js
|
||||
packages/lib/services/synchronizer/ItemUploader.js
|
||||
packages/lib/services/synchronizer/LockHandler.js
|
||||
packages/lib/services/synchronizer/MigrationHandler.js
|
||||
packages/lib/services/synchronizer/Synchronizer.basics.test.js
|
||||
@@ -753,8 +792,8 @@ packages/lib/services/synchronizer/migrations/1.js
|
||||
packages/lib/services/synchronizer/migrations/2.js
|
||||
packages/lib/services/synchronizer/migrations/3.js
|
||||
packages/lib/services/synchronizer/syncDebugLog.js
|
||||
packages/lib/services/synchronizer/syncInfoUtils.js
|
||||
packages/lib/services/synchronizer/syncInfoUtils.test.js
|
||||
packages/lib/services/synchronizer/syncInfoUtils.js
|
||||
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js
|
||||
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js
|
||||
packages/lib/services/synchronizer/tools.js
|
||||
@@ -780,11 +819,12 @@ packages/lib/themes/type.js
|
||||
packages/lib/time.js
|
||||
packages/lib/utils/credentialFiles.js
|
||||
packages/lib/utils/joplinCloud.js
|
||||
packages/lib/utils/webDAVUtils.js
|
||||
packages/lib/utils/userFetcher.js
|
||||
packages/lib/utils/webDAVUtils.test.js
|
||||
packages/lib/utils/webDAVUtils.js
|
||||
packages/lib/uuid.js
|
||||
packages/lib/versionInfo.js
|
||||
packages/lib/versionInfo.test.js
|
||||
packages/lib/versionInfo.js
|
||||
packages/pdf-viewer/FullViewer.js
|
||||
packages/pdf-viewer/Page.js
|
||||
packages/pdf-viewer/PdfDocument.js
|
||||
@@ -805,30 +845,31 @@ packages/pdf-viewer/ui/IconButtons.js
|
||||
packages/pdf-viewer/ui/ZoomControls.js
|
||||
packages/plugin-repo-cli/commands/updateRelease.js
|
||||
packages/plugin-repo-cli/index.js
|
||||
packages/plugin-repo-cli/lib/checkIfPluginCanBeAdded.js
|
||||
packages/plugin-repo-cli/lib/checkIfPluginCanBeAdded.test.js
|
||||
packages/plugin-repo-cli/lib/errorsHaveChanged.js
|
||||
packages/plugin-repo-cli/lib/checkIfPluginCanBeAdded.js
|
||||
packages/plugin-repo-cli/lib/errorsHaveChanged.test.js
|
||||
packages/plugin-repo-cli/lib/gitCompareUrl.js
|
||||
packages/plugin-repo-cli/lib/errorsHaveChanged.js
|
||||
packages/plugin-repo-cli/lib/gitCompareUrl.test.js
|
||||
packages/plugin-repo-cli/lib/overrideUtils.js
|
||||
packages/plugin-repo-cli/lib/gitCompareUrl.js
|
||||
packages/plugin-repo-cli/lib/overrideUtils.test.js
|
||||
packages/plugin-repo-cli/lib/overrideUtils.js
|
||||
packages/plugin-repo-cli/lib/types.js
|
||||
packages/plugin-repo-cli/lib/updateReadme.js
|
||||
packages/plugin-repo-cli/lib/updateReadme.test.js
|
||||
packages/plugin-repo-cli/lib/updateReadme.js
|
||||
packages/plugin-repo-cli/lib/utils.js
|
||||
packages/plugins/ToggleSidebars/api/index.js
|
||||
packages/plugins/ToggleSidebars/api/types.js
|
||||
packages/plugins/ToggleSidebars/src/index.js
|
||||
packages/react-native-saf-x/src/index.js
|
||||
packages/renderer/HtmlToHtml.test.js
|
||||
packages/renderer/HtmlToHtml.js
|
||||
packages/renderer/InMemoryCache.js
|
||||
packages/renderer/MarkupToHtml.js
|
||||
packages/renderer/MdToHtml.js
|
||||
packages/renderer/MdToHtml/createEventHandlingAttrs.js
|
||||
packages/renderer/MdToHtml/createEventHandlingAttrs.test.js
|
||||
packages/renderer/MdToHtml/linkReplacement.js
|
||||
packages/renderer/MdToHtml/createEventHandlingAttrs.js
|
||||
packages/renderer/MdToHtml/linkReplacement.test.js
|
||||
packages/renderer/MdToHtml/linkReplacement.js
|
||||
packages/renderer/MdToHtml/renderMedia.js
|
||||
packages/renderer/MdToHtml/rules/checkbox.js
|
||||
packages/renderer/MdToHtml/rules/code_inline.js
|
||||
@@ -847,27 +888,28 @@ packages/renderer/MdToHtml/setupLinkify.js
|
||||
packages/renderer/MdToHtml/validateLinks.js
|
||||
packages/renderer/headerAnchor.js
|
||||
packages/renderer/highlight.js
|
||||
packages/renderer/htmlUtils.js
|
||||
packages/renderer/htmlUtils.test.js
|
||||
packages/renderer/htmlUtils.js
|
||||
packages/renderer/index.js
|
||||
packages/renderer/noteStyle.js
|
||||
packages/renderer/pathUtils.js
|
||||
packages/renderer/utils.js
|
||||
packages/tools/build-release-stats.js
|
||||
packages/tools/build-welcome.js
|
||||
packages/tools/buildServerDocker.js
|
||||
packages/tools/buildServerDocker.test.js
|
||||
packages/tools/bundleDefaultPlugins.js
|
||||
packages/tools/buildServerDocker.js
|
||||
packages/tools/bundleDefaultPlugins.test.js
|
||||
packages/tools/bundleDefaultPlugins.js
|
||||
packages/tools/checkIgnoredFiles.js
|
||||
packages/tools/checkLibPaths.js
|
||||
packages/tools/checkLibPaths.test.js
|
||||
packages/tools/checkLibPaths.js
|
||||
packages/tools/convertThemesToCss.js
|
||||
packages/tools/generate-database-types.js
|
||||
packages/tools/generate-images.js
|
||||
packages/tools/git-changelog.js
|
||||
packages/tools/git-changelog.test.js
|
||||
packages/tools/git-changelog.js
|
||||
packages/tools/licenseChecker.js
|
||||
packages/tools/packageJsonLint.js
|
||||
packages/tools/release-android.js
|
||||
packages/tools/release-cli.js
|
||||
packages/tools/release-electron.js
|
||||
@@ -878,8 +920,9 @@ packages/tools/setupNewRelease.js
|
||||
packages/tools/spellcheck.js
|
||||
packages/tools/tagServerLatest.js
|
||||
packages/tools/tool-utils.js
|
||||
packages/tools/update-readme-download.js
|
||||
packages/tools/update-readme-contributors.js
|
||||
packages/tools/update-readme-download.test.js
|
||||
packages/tools/update-readme-download.js
|
||||
packages/tools/update-readme-sponsors.js
|
||||
packages/tools/updateMarkdownDoc.js
|
||||
packages/tools/utils/discourse.js
|
||||
@@ -889,14 +932,14 @@ packages/tools/website/build.js
|
||||
packages/tools/website/buildTranslations.js
|
||||
packages/tools/website/updateDownloadPage.js
|
||||
packages/tools/website/updateNews.js
|
||||
packages/tools/website/utils/applyTranslations.js
|
||||
packages/tools/website/utils/applyTranslations.test.js
|
||||
packages/tools/website/utils/convertLinksToLocale.js
|
||||
packages/tools/website/utils/applyTranslations.js
|
||||
packages/tools/website/utils/convertLinksToLocale.test.js
|
||||
packages/tools/website/utils/convertLinksToLocale.js
|
||||
packages/tools/website/utils/frontMatter.js
|
||||
packages/tools/website/utils/news.js
|
||||
packages/tools/website/utils/openGraph.js
|
||||
packages/tools/website/utils/openGraph.test.js
|
||||
packages/tools/website/utils/openGraph.js
|
||||
packages/tools/website/utils/parser.js
|
||||
packages/tools/website/utils/pressCarousel.js
|
||||
packages/tools/website/utils/processTranslations.js
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
|
||||
index 0f52b73c61625db2a3081c0950b6bdd2b06e3d40..b0fc3de4be0b3a26b638683613c63c783c2739bb 100644
|
||||
--- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
|
||||
+++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
|
||||
@@ -38,7 +38,7 @@ import com.facebook.react.uimanager.common.ViewUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
-import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
+import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
@@ -151,7 +151,10 @@ public class NativeAnimatedModule extends NativeAnimatedModuleSpec
|
||||
}
|
||||
|
||||
private class ConcurrentOperationQueue {
|
||||
- private final Queue<UIThreadOperation> mQueue = new ConcurrentLinkedQueue<>();
|
||||
+ // Patch: Use LinkedBlockingQueue instead of ConcurrentLinkedQueue.
|
||||
+ // In some versions of Android, ConcurrentLinkedQueue is known to drop
|
||||
+ // items, causing crashing. See https://github.com/laurent22/joplin/issues/8425
|
||||
+ private final Queue<UIThreadOperation> mQueue = new LinkedBlockingQueue<>();
|
||||
@Nullable private UIThreadOperation mPeekedOperation = null;
|
||||
|
||||
@AnyThread
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
diff --git a/android/src/main/java/com/swmansion/reanimated/NodesManager.java b/android/src/main/java/com/swmansion/reanimated/NodesManager.java
|
||||
index e974f8eb827a35be4d7e5fa9b096af9387c595dd..bc9e5ff566c9484274e8eacefc88327a5ff30def 100644
|
||||
--- a/android/src/main/java/com/swmansion/reanimated/NodesManager.java
|
||||
+++ b/android/src/main/java/com/swmansion/reanimated/NodesManager.java
|
||||
@@ -34,7 +34,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
-import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
+import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -80,7 +80,12 @@ public class NodesManager implements EventDispatcherListener {
|
||||
private ReactApplicationContext mReactApplicationContext;
|
||||
private RCTEventEmitter mCustomEventHandler;
|
||||
private List<OnAnimationFrame> mFrameCallbacks = new ArrayList<>();
|
||||
- private ConcurrentLinkedQueue<CopiedEvent> mEventQueue = new ConcurrentLinkedQueue<>();
|
||||
+
|
||||
+ // Patch: On some versions of Android, ConcurrentLinkedQueue is known to
|
||||
+ // drop items. LinkedBlockingQueue is a potentially-slower alternative that
|
||||
+ // should not drop items.
|
||||
+ // See https://github.com/laurent22/joplin/issues/8425
|
||||
+ private LinkedBlockingQueue<CopiedEvent> mEventQueue = new LinkedBlockingQueue<>();
|
||||
public double currentFrameTimeMs;
|
||||
public Set<String> uiProps = Collections.emptySet();
|
||||
public Set<String> nativeProps = Collections.emptySet();
|
||||
@@ -24,3 +24,9 @@ logFilters:
|
||||
# eg "string.prototype.matchall@npm:4.0.8 can't be found in the cache and will be fetched from the remote registry"
|
||||
- code: YN0013
|
||||
level: discard
|
||||
|
||||
# To avoid the error "The remote archive doesn't match the expected checksum".
|
||||
# Yarn offers no way to understand what the issue is when it happens, or what
|
||||
# needs to be done so it's just security theater that we have no choice but to
|
||||
# disable.
|
||||
checksumBehavior: update
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -789,7 +789,7 @@ footer .bottom-links-row p {
|
||||
}
|
||||
|
||||
#menu-mobile .social-links .social-link-mastodon,
|
||||
#menu-mobile .social-links .social-link-reddit,
|
||||
#menu-mobile .social-links .social-link-lemmy,
|
||||
#menu-mobile .social-links .social-link-linkedin,
|
||||
#menu-mobile .social-links .social-link-patreon {
|
||||
display: none;
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 83 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -5,7 +5,7 @@
|
||||
<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>
|
||||
<a class="social-link-linkedin" href="https://www.linkedin.com/company/joplin" title="Joplin LinkedIn Feed"><i class="fab fa-linkedin"></i></a>
|
||||
<a class="social-link-reddit" href="https://www.reddit.com/r/joplinapp/" title="Joplin Subreddit"><i class="fab fa-reddit"></i></a>
|
||||
<a class="social-link-lemmy" href="https://sopuli.xyz/c/joplinapp" title="Joplin Lemmy Community"><i class="fas fa-otter"></i></a>
|
||||
<a class="social-link-github" href="https://github.com/laurent22/joplin/" title="Joplin GitHub repository"><i class="fab fa-github"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
}).then(async function(result) {
|
||||
if (!result.ok) {
|
||||
console.error('Could not create Stripe checkout session', await result.text());
|
||||
alert('The checkout session could not be created. Please contact support@joplincloud.com for support.');
|
||||
alert('The checkout session could not be created. Please contact us on the forum for support.');
|
||||
} else {
|
||||
return result.json();
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ A community maintained list of these distributions can be found here: [Unofficia
|
||||
# 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://residence-greece.com/"><img title="Greece Golden Visa" width="256" src="https://joplinapp.org/images/sponsors/ResidenceGreece.jpg"/></a> <a href="https://grundstueckspreise.info/"><img title="SP Software GmbH" width="256" src="https://joplinapp.org/images/sponsors/Grundstueckspreise.png"/></a> <a href="https://tranio.com/spain/"><img title="Property for sale in Spain" width="256" src="https://joplinapp.org/images/sponsors/TranioOverseasProperty.jpg"/></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://residence-greece.com/"><img title="Greece Golden Visa" width="256" src="https://joplinapp.org/images/sponsors/ResidenceGreece.jpg"/></a> <a href="https://grundstueckspreise.info/"><img title="SP Software GmbH" width="256" src="https://joplinapp.org/images/sponsors/Grundstueckspreise.png"/></a> <a href="https://tranio.com/spain/"><img title="Property for sale in Spain" width="256" src="https://joplinapp.org/images/sponsors/TranioOverseasProperty.jpg"/></a> <a href="https://citricsheep.com"><img title="Citric Sheep" width="256" src="https://joplinapp.org/images/sponsors/CitricSheep.png"/></a>
|
||||
<!-- SPONSORS-ORG -->
|
||||
|
||||
* * *
|
||||
@@ -75,11 +75,10 @@ A community maintained list of these distributions can be found here: [Unofficia
|
||||
| | | | |
|
||||
| :---: | :---: | :---: | :---: |
|
||||
| <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/2793530?s=96&v=4"/></br>[CyberXZT](https://github.com/CyberXZT) | <img width="50" src="https://avatars2.githubusercontent.com/u/1307332?s=96&v=4"/></br>[dbrandonjohnson](https://github.com/dbrandonjohnson) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/56287?s=96&v=4"/></br>[fats](https://github.com/fats) | <img width="50" src="https://avatars2.githubusercontent.com/u/49439044?s=96&v=4"/></br>[fourstepper](https://github.com/fourstepper) | <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/3266447?s=96&v=4"/></br>[iamwillbar](https://github.com/iamwillbar) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/1310474?s=96&v=4"/></br>[jknowles](https://github.com/jknowles) | <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/5588131?s=96&v=4"/></br>[kianenigma](https://github.com/kianenigma) | <img width="50" src="https://avatars2.githubusercontent.com/u/24908652?s=96&v=4"/></br>[konishi-t](https://github.com/konishi-t) |
|
||||
| <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/29300939?s=96&v=4"/></br>[mcejp](https://github.com/mcejp) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/31054972?s=96&v=4"/></br>[saarantras](https://github.com/saarantras) | <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/333944?s=96&v=4"/></br>[tateisu](https://github.com/tateisu) |
|
||||
| | | | |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/14873877?s=96&v=4"/></br>[dchecks](https://github.com/dchecks) | <img width="50" src="https://avatars2.githubusercontent.com/u/56287?s=96&v=4"/></br>[fats](https://github.com/fats) | <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/1310474?s=96&v=4"/></br>[jknowles](https://github.com/jknowles) | <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/24908652?s=96&v=4"/></br>[konishi-t](https://github.com/konishi-t) | <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/31054972?s=96&v=4"/></br>[saarantras](https://github.com/saarantras) | <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/333944?s=96&v=4"/></br>[tateisu](https://github.com/tateisu) | | |
|
||||
<!-- SPONSORS-GITHUB -->
|
||||
|
||||
<!-- TOC -->
|
||||
@@ -107,6 +106,7 @@ A community maintained list of these distributions can be found here: [Unofficia
|
||||
|
||||
- [Sharing a notebook](https://github.com/laurent22/joplin/blob/dev/readme/share_notebook.md)
|
||||
- [Publishing a note](https://github.com/laurent22/joplin/blob/dev/readme/publish_note.md)
|
||||
- [Email to Note](https://github.com/laurent22/joplin/blob/dev/readme/email_to_note.md)
|
||||
|
||||
- Joplin API - Get Started
|
||||
|
||||
@@ -158,6 +158,7 @@ A community maintained list of these distributions can be found here: [Unofficia
|
||||
- [Guiding principles](https://github.com/laurent22/joplin/blob/dev/readme/principles.md)
|
||||
- [Stats](https://github.com/laurent22/joplin/blob/dev/readme/stats.md)
|
||||
- [Brand guidelines](https://joplinapp.org/brand)
|
||||
- [Release cycle](https://github.com/laurent22/joplin/blob/dev/readme/release_cycle.md)
|
||||
- [Donate](https://github.com/laurent22/joplin/blob/dev/readme/donate.md)
|
||||
<!-- TOC -->
|
||||
|
||||
@@ -231,9 +232,9 @@ Joplin is also capable of exporting to a number of other formats including HTML
|
||||
|
||||
# Synchronisation
|
||||
|
||||
One of the goals of Joplin is to avoid being tied to any particular company or service, whether it is Evernote, Google or Microsoft. As such the synchronisation is designed without any hard dependency to any particular service. Most of the synchronisation process is done at an abstract level and access to external services, such as Nextcloud or Dropbox, is done via lightweight drivers. It is easy to support new services by creating simple drivers that provide a filesystem-like interface, i.e. the ability to read, write, delete and list items. It is also simple to switch from one service to another or to even sync to multiple services at once. Each note, notebook, tags, as well as the relation between items is transmitted as plain text files during synchronisation, which means the data can also be moved to a different application, can be easily backed up, inspected, etc.
|
||||
One of the goals of Joplin is to avoid being tied to any particular company or service, whether it is Evernote, Google or Microsoft. As such the synchronisation is designed without any hard dependency to any particular service. Most of the synchronisation process is done at an abstract level and access to external services, such as Nextcloud or Dropbox, is done via lightweight drivers. It is easy to support new services by creating simple drivers that provide a filesystem-like interface, i.e. the ability to read, write, delete and list items. It is also simple to switch from one service to another.
|
||||
|
||||
Currently, synchronisation is possible with Nextcloud, WebDAV, Dropbox, OneDrive or the local filesystem. To enable synchronisation please follow the instructions below. After that, the application will synchronise in the background whenever it is running, or you can click on "Synchronise" to start a synchronisation manually. Joplin will background sync automatically after any content change is made on the local application.
|
||||
Currently, synchronisation is possible with Joplin Cloud, Nextcloud, S3, WebDAV, Dropbox, OneDrive or the local filesystem. To enable synchronisation please follow the instructions below. After that, the application will synchronise in the background whenever it is running, or you can click on "Synchronise" to start a synchronisation manually. Joplin will background sync automatically after any content change is made on the local application.
|
||||
|
||||
If the **terminal client** has been installed, it is possible to also synchronise outside of the user interface by typing `joplin sync` from the terminal. This can be used to setup a cron script to synchronise at a regular interval. For example, this would do it every 30 minutes:
|
||||
|
||||
@@ -511,7 +512,7 @@ Name | Description
|
||||
[Patreon page](https://www.patreon.com/joplin) |The latest news are often posted there
|
||||
[Discord server](https://discord.gg/VSj7AFHvpq) | Our chat server
|
||||
[LinkedIn](https://www.linkedin.com/company/joplin) | Our LinkedIn page
|
||||
[Sub-reddit](https://www.reddit.com/r/joplinapp/) | Also a good place to get help
|
||||
[Lemmy Community](https://sopuli.xyz/c/joplinapp) | Also a good place to get help
|
||||
|
||||
# Contributing
|
||||
|
||||
@@ -536,47 +537,47 @@ Current translations:
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
| Language | Po File | Last translator | Percent done
|
||||
---|---|---|---|---
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/arableague.png" width="16px"/> | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 79%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/arableague.png" width="16px"/> | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 77%
|
||||
<img src="https://joplinapp.org/images/flags/es/basque_country.png" width="16px"/> | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 22%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ba.png" width="16px"/> | Bosnian (Bosna i Hercegovina) | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 57%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/bg.png" width="16px"/> | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 45%
|
||||
<img src="https://joplinapp.org/images/flags/es/catalonia.png" width="16px"/> | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | [Xavi Ivars](mailto:xavi.ivars@gmail.com) | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hr.png" width="16px"/> | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 99%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cz.png" width="16px"/> | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | Fejby | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/dk.png" width="16px"/> | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | ERYpTION | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/de.png" width="16px"/> | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [MrKanister](mailto:pueblos_spatulas@aleeas.com) | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ee.png" width="16px"/> | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 44%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ba.png" width="16px"/> | Bosnian (Bosna i Hercegovina) | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 56%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/bg.png" width="16px"/> | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 44%
|
||||
<img src="https://joplinapp.org/images/flags/es/catalonia.png" width="16px"/> | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | [Xavi Ivars](mailto:xavi.ivars@gmail.com) | 86%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hr.png" width="16px"/> | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cz.png" width="16px"/> | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | Fejby | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/dk.png" width="16px"/> | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | ERYpTION | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/de.png" width="16px"/> | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [Mr-Kanister](mailto:viger_gtrc@simplelogin.com) | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ee.png" width="16px"/> | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 43%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/gb.png" width="16px"/> | English (United Kingdom) | [en_GB](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_GB.po) | | 100%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/us.png" width="16px"/> | English (United States of America) | [en_US](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_US.po) | | 100%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/es.png" width="16px"/> | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Francisco Villaverde](mailto:teko.gr@gmail.com) | 97%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/es.png" width="16px"/> | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Francisco Villaverde](mailto:teko.gr@gmail.com) | 95%
|
||||
<img src="https://joplinapp.org/images/flags/esperanto.png" width="16px"/> | Esperanto | [eo](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eo.po) | Marton Paulo | 25%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/fi.png" width="16px"/> | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato0 | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/fi.png" width="16px"/> | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato0 | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/fr.png" width="16px"/> | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Laurent Cozic | 100%
|
||||
<img src="https://joplinapp.org/images/flags/es/galicia.png" width="16px"/> | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 29%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/id.png" width="16px"/> | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [Wisnu Adi Santoso](mailto:waditos@gmail.com) | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/it.png" width="16px"/> | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Manuel Tassi](mailto:mannivuwiki@gmail.com) | 80%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hu.png" width="16px"/> | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Magyari Balázs](mailto:balmag@gmail.com) | 77%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/be.png" width="16px"/> | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 78%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/nl.png" width="16px"/> | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MHolkamp](mailto:mholkamp@users.noreply.github.com) | 87%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/no.png" width="16px"/> | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | [Mats Estensen](mailto:code@mxe.no) | 87%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ir.png" width="16px"/> | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 54%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pl.png" width="16px"/> | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [X3NO](mailto:X3NO@disroot.org) | 89%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/br.png" width="16px"/> | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Douglas Leão](mailto:djlsplays@gmail.com) | 87%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pt.png" width="16px"/> | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 72%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ro.png" width="16px"/> | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 50%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/si.png" width="16px"/> | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | [Martin Korelič](mailto:martin.korelic@protonmail.com) | 79%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/se.png" width="16px"/> | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 99%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/th.png" width="16px"/> | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 36%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/vn.png" width="16px"/> | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 77%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tr.png" width="16px"/> | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 99%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ua.png" width="16px"/> | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 71%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/gr.png" width="16px"/> | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 87%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ru.png" width="16px"/> | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Dmitriy K](mailto:dmitry@atsip.ru) | 99%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/rs.png" width="16px"/> | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 64%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cn.png" width="16px"/> | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [wh201906](mailto:wh201906@yandex.com) | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tw.png" width="16px"/> | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Kevin Hsu](mailto:kevin.hsu.hws@gmail.com) | 88%
|
||||
<img src="https://joplinapp.org/images/flags/es/galicia.png" width="16px"/> | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 28%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/id.png" width="16px"/> | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [Wisnu Adi Santoso](mailto:waditos@gmail.com) | 86%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/it.png" width="16px"/> | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Manuel Tassi](mailto:mannivuwiki@gmail.com) | 78%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hu.png" width="16px"/> | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Magyari Balázs](mailto:balmag@gmail.com) | 75%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/be.png" width="16px"/> | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 76%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/nl.png" width="16px"/> | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MHolkamp](mailto:mholkamp@users.noreply.github.com) | 86%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/no.png" width="16px"/> | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | [Mats Estensen](mailto:code@mxe.no) | 85%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ir.png" width="16px"/> | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 53%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pl.png" width="16px"/> | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [X3NO](mailto:X3NO@disroot.org) | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/br.png" width="16px"/> | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Douglas Leão](mailto:djlsplays@gmail.com) | 85%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pt.png" width="16px"/> | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 70%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ro.png" width="16px"/> | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 49%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/si.png" width="16px"/> | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | [Martin Korelič](mailto:martin.korelic@protonmail.com) | 78%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/se.png" width="16px"/> | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 97%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/th.png" width="16px"/> | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 35%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/vn.png" width="16px"/> | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 75%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tr.png" width="16px"/> | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 97%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ua.png" width="16px"/> | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/gr.png" width="16px"/> | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 97%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ru.png" width="16px"/> | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Dmitriy K](mailto:dmitry@atsip.ru) | 97%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/rs.png" width="16px"/> | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 63%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cn.png" width="16px"/> | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [qx100](mailto:ztymaxwell@gmail.com) | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tw.png" width="16px"/> | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Kevin Hsu](mailto:kevin.hsu.hws@gmail.com) | 86%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/jp.png" width="16px"/> | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/kr.png" width="16px"/> | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/kr.png" width="16px"/> | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 86%
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
|
||||
# Contributors
|
||||
@@ -586,78 +587,93 @@ Thank you to everyone who've contributed to Joplin's source code!
|
||||
<!-- CONTRIBUTORS-TABLE-AUTO-GENERATED -->
|
||||
| | | | | |
|
||||
| :---: | :---: | :---: | :---: | :---: |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/1285584?v=4"/></br>[laurent22](https://github.com/laurent22) | <img width="50" src="https://avatars.githubusercontent.com/u/223439?v=4"/></br>[tessus](https://github.com/tessus) | <img width="50" src="https://avatars.githubusercontent.com/u/2179547?v=4"/></br>[CalebJohn](https://github.com/CalebJohn) | <img width="50" src="https://avatars.githubusercontent.com/u/1732810?v=4"/></br>[mic704b](https://github.com/mic704b) | <img width="50" src="https://avatars.githubusercontent.com/u/995612?v=4"/></br>[roman-r-m](https://github.com/roman-r-m) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/29672555?v=4"/></br>[genneko](https://github.com/genneko) | <img width="50" src="https://avatars.githubusercontent.com/u/63491353?v=4"/></br>[j-krl](https://github.com/j-krl) | <img width="50" src="https://avatars.githubusercontent.com/u/4553672?v=4"/></br>[tanrax](https://github.com/tanrax) | <img width="50" src="https://avatars.githubusercontent.com/u/30305957?v=4"/></br>[naviji](https://github.com/naviji) | <img width="50" src="https://avatars.githubusercontent.com/u/3542031?v=4"/></br>[PackElend](https://github.com/PackElend) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/8701534?v=4"/></br>[rtmkrlv](https://github.com/rtmkrlv) | <img width="50" src="https://avatars.githubusercontent.com/u/10997189?v=4"/></br>[fmrtn](https://github.com/fmrtn) | <img width="50" src="https://avatars.githubusercontent.com/u/4374338?v=4"/></br>[potatogim](https://github.com/potatogim) | <img width="50" src="https://avatars.githubusercontent.com/u/6979755?v=4"/></br>[devonzuegel](https://github.com/devonzuegel) | <img width="50" src="https://avatars.githubusercontent.com/u/26695184?v=4"/></br>[anjulalk](https://github.com/anjulalk) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/16101778?v=4"/></br>[gabcoh](https://github.com/gabcoh) | <img width="50" src="https://avatars.githubusercontent.com/u/10927304?v=4"/></br>[matsest](https://github.com/matsest) | <img width="50" src="https://avatars.githubusercontent.com/u/6319051?v=4"/></br>[abonte](https://github.com/abonte) | <img width="50" src="https://avatars.githubusercontent.com/u/1685517?v=4"/></br>[Abijeet](https://github.com/Abijeet) | <img width="50" src="https://avatars.githubusercontent.com/u/27751740?v=4"/></br>[ishantgupta777](https://github.com/ishantgupta777) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/24863925?v=4"/></br>[JackGruber](https://github.com/JackGruber) | <img width="50" src="https://avatars.githubusercontent.com/u/2063957?v=4"/></br>[Ardakilic](https://github.com/Ardakilic) | <img width="50" src="https://avatars.githubusercontent.com/u/44024553?v=4"/></br>[rabeehrz](https://github.com/rabeehrz) | <img width="50" src="https://avatars.githubusercontent.com/u/35633575?v=4"/></br>[coderrsid](https://github.com/coderrsid) | <img width="50" src="https://avatars.githubusercontent.com/u/208212?v=4"/></br>[foxmask](https://github.com/foxmask) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/6557454?v=4"/></br>[innocuo](https://github.com/innocuo) | <img width="50" src="https://avatars.githubusercontent.com/u/54268438?v=4"/></br>[Rahulm2310](https://github.com/Rahulm2310) | <img width="50" src="https://avatars.githubusercontent.com/u/1904967?v=4"/></br>[readingsnail](https://github.com/readingsnail) | <img width="50" src="https://avatars.githubusercontent.com/u/7415668?v=4"/></br>[mablin7](https://github.com/mablin7) | <img width="50" src="https://avatars.githubusercontent.com/u/3985557?v=4"/></br>[XarisA](https://github.com/XarisA) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/49979415?v=4"/></br>[jonath92](https://github.com/jonath92) | <img width="50" src="https://avatars.githubusercontent.com/u/4237724?v=4"/></br>[alexdevero](https://github.com/alexdevero) | <img width="50" src="https://avatars.githubusercontent.com/u/35904727?v=4"/></br>[Runo-saduwa](https://github.com/Runo-saduwa) | <img width="50" src="https://avatars.githubusercontent.com/u/5365582?v=4"/></br>[marcosvega91](https://github.com/marcosvega91) | <img width="50" src="https://avatars.githubusercontent.com/u/37639389?v=4"/></br>[petrz12](https://github.com/petrz12) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/51550769?v=4"/></br>[rnbastos](https://github.com/rnbastos) | <img width="50" src="https://avatars.githubusercontent.com/u/32396?v=4"/></br>[ProgramFan](https://github.com/ProgramFan) | <img width="50" src="https://avatars.githubusercontent.com/u/4245227?v=4"/></br>[zblesk](https://github.com/zblesk) | <img width="50" src="https://avatars.githubusercontent.com/u/5730052?v=4"/></br>[vsimkus](https://github.com/vsimkus) | <img width="50" src="https://avatars.githubusercontent.com/u/3194829?v=4"/></br>[moltenform](https://github.com/moltenform) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/36989112?v=4"/></br>[nishantwrp](https://github.com/nishantwrp) | <img width="50" src="https://avatars.githubusercontent.com/u/5199995?v=4"/></br>[zuphilip](https://github.com/zuphilip) | <img width="50" src="https://avatars.githubusercontent.com/u/54576074?v=4"/></br>[Rishabh-malhotraa](https://github.com/Rishabh-malhotraa) | <img width="50" src="https://avatars.githubusercontent.com/u/559346?v=4"/></br>[metbril](https://github.com/metbril) | <img width="50" src="https://avatars.githubusercontent.com/u/47623588?v=4"/></br>[WhiredPlanck](https://github.com/WhiredPlanck) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/43657314?v=4"/></br>[milotype](https://github.com/milotype) | <img width="50" src="https://avatars.githubusercontent.com/u/32196447?v=4"/></br>[yaozeye](https://github.com/yaozeye) | <img width="50" src="https://avatars.githubusercontent.com/u/12264626?v=4"/></br>[ylc395](https://github.com/ylc395) | <img width="50" src="https://avatars.githubusercontent.com/u/17768566?v=4"/></br>[RenatoXSR](https://github.com/RenatoXSR) | <img width="50" src="https://avatars.githubusercontent.com/u/54888685?v=4"/></br>[RedDocMD](https://github.com/RedDocMD) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/31567272?v=4"/></br>[q1011](https://github.com/q1011) | <img width="50" src="https://avatars.githubusercontent.com/u/12906090?v=4"/></br>[amitsin6h](https://github.com/amitsin6h) | <img width="50" src="https://avatars.githubusercontent.com/u/628474?v=4"/></br>[Atalanttore](https://github.com/Atalanttore) | <img width="50" src="https://avatars.githubusercontent.com/u/42747216?v=4"/></br>[Mannivu](https://github.com/Mannivu) | <img width="50" src="https://avatars.githubusercontent.com/u/23281486?v=4"/></br>[martonpaulo](https://github.com/martonpaulo) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/390889?v=4"/></br>[mmahmoudian](https://github.com/mmahmoudian) | <img width="50" src="https://avatars.githubusercontent.com/u/4497566?v=4"/></br>[rccavalcanti](https://github.com/rccavalcanti) | <img width="50" src="https://avatars.githubusercontent.com/u/1540054?v=4"/></br>[ShaneKilkelly](https://github.com/ShaneKilkelly) | <img width="50" src="https://avatars.githubusercontent.com/u/7091080?v=4"/></br>[sinkuu](https://github.com/sinkuu) | <img width="50" src="https://avatars.githubusercontent.com/u/6734573?v=4"/></br>[stweil](https://github.com/stweil) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/692072?v=4"/></br>[conyx](https://github.com/conyx) | <img width="50" src="https://avatars.githubusercontent.com/u/49116134?v=4"/></br>[anihm136](https://github.com/anihm136) | <img width="50" src="https://avatars.githubusercontent.com/u/937861?v=4"/></br>[archont00](https://github.com/archont00) | <img width="50" src="https://avatars.githubusercontent.com/u/32770029?v=4"/></br>[bradmcl](https://github.com/bradmcl) | <img width="50" src="https://avatars.githubusercontent.com/u/22592201?v=4"/></br>[tfinnberg](https://github.com/tfinnberg) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/8716226?v=4"/></br>[amandamcg](https://github.com/amandamcg) | <img width="50" src="https://avatars.githubusercontent.com/u/3870964?v=4"/></br>[marcushill](https://github.com/marcushill) | <img width="50" src="https://avatars.githubusercontent.com/u/102242?v=4"/></br>[nathanleiby](https://github.com/nathanleiby) | <img width="50" src="https://avatars.githubusercontent.com/u/226708?v=4"/></br>[RaphaelKimmig](https://github.com/RaphaelKimmig) | <img width="50" src="https://avatars.githubusercontent.com/u/20461071?v=4"/></br>[Vaso3](https://github.com/Vaso3) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/36303913?v=4"/></br>[sensor-freak](https://github.com/sensor-freak) | <img width="50" src="https://avatars.githubusercontent.com/u/63918341?v=4"/></br>[lkiThakur](https://github.com/lkiThakur) | <img width="50" src="https://avatars.githubusercontent.com/u/28987176?v=4"/></br>[infinity052](https://github.com/infinity052) | <img width="50" src="https://avatars.githubusercontent.com/u/21161146?v=4"/></br>[BartBucknill](https://github.com/BartBucknill) | <img width="50" src="https://avatars.githubusercontent.com/u/2494769?v=4"/></br>[mrwulf](https://github.com/mrwulf) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/560571?v=4"/></br>[chrisb86](https://github.com/chrisb86) | <img width="50" src="https://avatars.githubusercontent.com/u/1686759?v=4"/></br>[chrmoritz](https://github.com/chrmoritz) | <img width="50" src="https://avatars.githubusercontent.com/u/58074586?v=4"/></br>[Daeraxa](https://github.com/Daeraxa) | <img width="50" src="https://avatars.githubusercontent.com/u/71190696?v=4"/></br>[Elaborendum](https://github.com/Elaborendum) | <img width="50" src="https://avatars.githubusercontent.com/u/5001259?v=4"/></br>[ethan42411](https://github.com/ethan42411) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/2733783?v=4"/></br>[JOJ0](https://github.com/JOJ0) | <img width="50" src="https://avatars.githubusercontent.com/u/17108695?v=4"/></br>[jalajcodes](https://github.com/jalajcodes) | <img width="50" src="https://avatars.githubusercontent.com/u/238088?v=4"/></br>[jblunck](https://github.com/jblunck) | <img width="50" src="https://avatars.githubusercontent.com/u/3140223?v=4"/></br>[jdrobertso](https://github.com/jdrobertso) | <img width="50" src="https://avatars.githubusercontent.com/u/37297218?v=4"/></br>[Jesssullivan](https://github.com/Jesssullivan) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/339645?v=4"/></br>[jmontane](https://github.com/jmontane) | <img width="50" src="https://avatars.githubusercontent.com/u/69011?v=4"/></br>[johanhammar](https://github.com/johanhammar) | <img width="50" src="https://avatars.githubusercontent.com/u/4168339?v=4"/></br>[solariz](https://github.com/solariz) | <img width="50" src="https://avatars.githubusercontent.com/u/25288?v=4"/></br>[maicki](https://github.com/maicki) | <img width="50" src="https://avatars.githubusercontent.com/u/2136373?v=4"/></br>[mjjzf](https://github.com/mjjzf) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/27608187?v=4"/></br>[rt-oliveira](https://github.com/rt-oliveira) | <img width="50" src="https://avatars.githubusercontent.com/u/2486806?v=4"/></br>[sebastienjust](https://github.com/sebastienjust) | <img width="50" src="https://avatars.githubusercontent.com/u/28362310?v=4"/></br>[sealch](https://github.com/sealch) | <img width="50" src="https://avatars.githubusercontent.com/u/34258070?v=4"/></br>[StarFang208](https://github.com/StarFang208) | <img width="50" src="https://avatars.githubusercontent.com/u/59690052?v=4"/></br>[Subhra264](https://github.com/Subhra264) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/1782292?v=4"/></br>[SubodhDahal](https://github.com/SubodhDahal) | <img width="50" src="https://avatars.githubusercontent.com/u/5912371?v=4"/></br>[TobiasDev](https://github.com/TobiasDev) | <img width="50" src="https://avatars.githubusercontent.com/u/13502069?v=4"/></br>[Whaell](https://github.com/Whaell) | <img width="50" src="https://avatars.githubusercontent.com/u/29891001?v=4"/></br>[jyuvaraj03](https://github.com/jyuvaraj03) | <img width="50" src="https://avatars.githubusercontent.com/u/15380913?v=4"/></br>[kowalskidev](https://github.com/kowalskidev) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/337455?v=4"/></br>[alexchee](https://github.com/alexchee) | <img width="50" src="https://avatars.githubusercontent.com/u/5077221?v=4"/></br>[axq](https://github.com/axq) | <img width="50" src="https://avatars.githubusercontent.com/u/8808502?v=4"/></br>[barbowza](https://github.com/barbowza) | <img width="50" src="https://avatars.githubusercontent.com/u/42007357?v=4"/></br>[eresytter](https://github.com/eresytter) | <img width="50" src="https://avatars.githubusercontent.com/u/4316805?v=4"/></br>[lightray22](https://github.com/lightray22) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/11711053?v=4"/></br>[lscolombo](https://github.com/lscolombo) | <img width="50" src="https://avatars.githubusercontent.com/u/36228623?v=4"/></br>[mrkaato](https://github.com/mrkaato) | <img width="50" src="https://avatars.githubusercontent.com/u/17399340?v=4"/></br>[pf-siedler](https://github.com/pf-siedler) | <img width="50" src="https://avatars.githubusercontent.com/u/17232523?v=4"/></br>[ruuti](https://github.com/ruuti) | <img width="50" src="https://avatars.githubusercontent.com/u/23638148?v=4"/></br>[s1nceri7y](https://github.com/s1nceri7y) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/10117386?v=4"/></br>[kornava](https://github.com/kornava) | <img width="50" src="https://avatars.githubusercontent.com/u/7471938?v=4"/></br>[ShuiHuo](https://github.com/ShuiHuo) | <img width="50" src="https://avatars.githubusercontent.com/u/11596277?v=4"/></br>[ikunya](https://github.com/ikunya) | <img width="50" src="https://avatars.githubusercontent.com/u/8184424?v=4"/></br>[Ahmad45123](https://github.com/Ahmad45123) | <img width="50" src="https://avatars.githubusercontent.com/u/59133880?v=4"/></br>[bedwardly-down](https://github.com/bedwardly-down) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/50335724?v=4"/></br>[dcaveiro](https://github.com/dcaveiro) | <img width="50" src="https://avatars.githubusercontent.com/u/47456195?v=4"/></br>[hexclover](https://github.com/hexclover) | <img width="50" src="https://avatars.githubusercontent.com/u/45535789?v=4"/></br>[2jaeyeol](https://github.com/2jaeyeol) | <img width="50" src="https://avatars.githubusercontent.com/u/25622825?v=4"/></br>[thackeraaron](https://github.com/thackeraaron) | <img width="50" src="https://avatars.githubusercontent.com/u/15862474?v=4"/></br>[aaronxn](https://github.com/aaronxn) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/40672207?v=4"/></br>[xUser5000](https://github.com/xUser5000) | <img width="50" src="https://avatars.githubusercontent.com/u/56785486?v=4"/></br>[iamabhi222](https://github.com/iamabhi222) | <img width="50" src="https://avatars.githubusercontent.com/u/63443657?v=4"/></br>[Aksh-Konda](https://github.com/Aksh-Konda) | <img width="50" src="https://avatars.githubusercontent.com/u/3660978?v=4"/></br>[alanfortlink](https://github.com/alanfortlink) | <img width="50" src="https://avatars.githubusercontent.com/u/53372753?v=4"/></br>[AverageUser2](https://github.com/AverageUser2) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/4056990?v=4"/></br>[afischer211](https://github.com/afischer211) | <img width="50" src="https://avatars.githubusercontent.com/u/26230870?v=4"/></br>[a13xk](https://github.com/a13xk) | <img width="50" src="https://avatars.githubusercontent.com/u/14836659?v=4"/></br>[apankratov](https://github.com/apankratov) | <img width="50" src="https://avatars.githubusercontent.com/u/7045739?v=4"/></br>[teterkin](https://github.com/teterkin) | <img width="50" src="https://avatars.githubusercontent.com/u/215668?v=4"/></br>[avanderberg](https://github.com/avanderberg) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/41290751?v=4"/></br>[serenitatis](https://github.com/serenitatis) | <img width="50" src="https://avatars.githubusercontent.com/u/4408379?v=4"/></br>[lex111](https://github.com/lex111) | <img width="50" src="https://avatars.githubusercontent.com/u/60134194?v=4"/></br>[Alkindi42](https://github.com/Alkindi42) | <img width="50" src="https://avatars.githubusercontent.com/u/7129815?v=4"/></br>[Jumanjii](https://github.com/Jumanjii) | <img width="50" src="https://avatars.githubusercontent.com/u/19962243?v=4"/></br>[AlphaJack](https://github.com/AlphaJack) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/65647302?v=4"/></br>[Lord-Aman](https://github.com/Lord-Aman) | <img width="50" src="https://avatars.githubusercontent.com/u/14096959?v=4"/></br>[richtwin567](https://github.com/richtwin567) | <img width="50" src="https://avatars.githubusercontent.com/u/487182?v=4"/></br>[ajilderda](https://github.com/ajilderda) | <img width="50" src="https://avatars.githubusercontent.com/u/922429?v=4"/></br>[adrynov](https://github.com/adrynov) | <img width="50" src="https://avatars.githubusercontent.com/u/94937?v=4"/></br>[andrewperry](https://github.com/andrewperry) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/5417051?v=4"/></br>[tekdel](https://github.com/tekdel) | <img width="50" src="https://avatars.githubusercontent.com/u/54475686?v=4"/></br>[anshuman9999](https://github.com/anshuman9999) | <img width="50" src="https://avatars.githubusercontent.com/u/25694659?v=4"/></br>[rasklaad](https://github.com/rasklaad) | <img width="50" src="https://avatars.githubusercontent.com/u/17809291?v=4"/></br>[Technik-J](https://github.com/Technik-J) | <img width="50" src="https://avatars.githubusercontent.com/u/498326?v=4"/></br>[Shaxine](https://github.com/Shaxine) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/9095073?v=4"/></br>[antonio-ramadas](https://github.com/antonio-ramadas) | <img width="50" src="https://avatars.githubusercontent.com/u/28067395?v=4"/></br>[heyapoorva](https://github.com/heyapoorva) | <img width="50" src="https://avatars.githubusercontent.com/u/201215?v=4"/></br>[assimd](https://github.com/assimd) | <img width="50" src="https://avatars.githubusercontent.com/u/26827848?v=4"/></br>[Atrate](https://github.com/Atrate) | <img width="50" src="https://avatars.githubusercontent.com/u/60288895?v=4"/></br>[Beowulf2](https://github.com/Beowulf2) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/7034200?v=4"/></br>[bimlas](https://github.com/bimlas) | <img width="50" src="https://avatars.githubusercontent.com/u/47641641?v=4"/></br>[brenobaptista](https://github.com/brenobaptista) | <img width="50" src="https://avatars.githubusercontent.com/u/60824?v=4"/></br>[brttbndr](https://github.com/brttbndr) | <img width="50" src="https://avatars.githubusercontent.com/u/16287077?v=4"/></br>[carlbordum](https://github.com/carlbordum) | <img width="50" src="https://avatars.githubusercontent.com/u/20382?v=4"/></br>[carlosedp](https://github.com/carlosedp) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/105843?v=4"/></br>[chaifeng](https://github.com/chaifeng) | <img width="50" src="https://avatars.githubusercontent.com/u/549349?v=4"/></br>[charles-e](https://github.com/charles-e) | <img width="50" src="https://avatars.githubusercontent.com/u/19870089?v=4"/></br>[cyy5358](https://github.com/cyy5358) | <img width="50" src="https://avatars.githubusercontent.com/u/32337926?v=4"/></br>[Chillu1](https://github.com/Chillu1) | <img width="50" src="https://avatars.githubusercontent.com/u/2348463?v=4"/></br>[Techwolf12](https://github.com/Techwolf12) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/2282880?v=4"/></br>[cloudtrends](https://github.com/cloudtrends) | <img width="50" src="https://avatars.githubusercontent.com/u/17257053?v=4"/></br>[idcristi](https://github.com/idcristi) | <img width="50" src="https://avatars.githubusercontent.com/u/15956322?v=4"/></br>[damienmascre](https://github.com/damienmascre) | <img width="50" src="https://avatars.githubusercontent.com/u/1044056?v=4"/></br>[daniellandau](https://github.com/daniellandau) | <img width="50" src="https://avatars.githubusercontent.com/u/12847693?v=4"/></br>[danil-tolkachev](https://github.com/danil-tolkachev) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/7279100?v=4"/></br>[darshani28](https://github.com/darshani28) | <img width="50" src="https://avatars.githubusercontent.com/u/26189247?v=4"/></br>[daukadolt](https://github.com/daukadolt) | <img width="50" src="https://avatars.githubusercontent.com/u/28535750?v=4"/></br>[NeverMendel](https://github.com/NeverMendel) | <img width="50" src="https://avatars.githubusercontent.com/u/26790323?v=4"/></br>[dervist](https://github.com/dervist) | <img width="50" src="https://avatars.githubusercontent.com/u/11378282?v=4"/></br>[diego-betto](https://github.com/diego-betto) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/215270?v=4"/></br>[erdody](https://github.com/erdody) | <img width="50" src="https://avatars.githubusercontent.com/u/10371667?v=4"/></br>[domgoodwin](https://github.com/domgoodwin) | <img width="50" src="https://avatars.githubusercontent.com/u/72066?v=4"/></br>[b4mboo](https://github.com/b4mboo) | <img width="50" src="https://avatars.githubusercontent.com/u/5131923?v=4"/></br>[donbowman](https://github.com/donbowman) | <img width="50" src="https://avatars.githubusercontent.com/u/579727?v=4"/></br>[sirnacnud](https://github.com/sirnacnud) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/47756?v=4"/></br>[dflock](https://github.com/dflock) | <img width="50" src="https://avatars.githubusercontent.com/u/7990534?v=4"/></br>[drobilica](https://github.com/drobilica) | <img width="50" src="https://avatars.githubusercontent.com/u/21699905?v=4"/></br>[educbraga](https://github.com/educbraga) | <img width="50" src="https://avatars.githubusercontent.com/u/67867099?v=4"/></br>[eduardokimmel](https://github.com/eduardokimmel) | <img width="50" src="https://avatars.githubusercontent.com/u/30393516?v=4"/></br>[VodeniZeko](https://github.com/VodeniZeko) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/17415256?v=4"/></br>[ei-ke](https://github.com/ei-ke) | <img width="50" src="https://avatars.githubusercontent.com/u/1962738?v=4"/></br>[einverne](https://github.com/einverne) | <img width="50" src="https://avatars.githubusercontent.com/u/16492558?v=4"/></br>[eodeluga](https://github.com/eodeluga) | <img width="50" src="https://avatars.githubusercontent.com/u/16875937?v=4"/></br>[fathyar](https://github.com/fathyar) | <img width="50" src="https://avatars.githubusercontent.com/u/3057302?v=4"/></br>[fer22f](https://github.com/fer22f) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/43272148?v=4"/></br>[fpindado](https://github.com/fpindado) | <img width="50" src="https://avatars.githubusercontent.com/u/1714374?v=4"/></br>[FleischKarussel](https://github.com/FleischKarussel) | <img width="50" src="https://avatars.githubusercontent.com/u/18525376?v=4"/></br>[talkdirty](https://github.com/talkdirty) | <img width="50" src="https://avatars.githubusercontent.com/u/19814827?v=4"/></br>[gmaubach](https://github.com/gmaubach) | <img width="50" src="https://avatars.githubusercontent.com/u/6190183?v=4"/></br>[gmag11](https://github.com/gmag11) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/6209647?v=4"/></br>[Jackymancs4](https://github.com/Jackymancs4) | <img width="50" src="https://avatars.githubusercontent.com/u/297578?v=4"/></br>[Glandos](https://github.com/Glandos) | <img width="50" src="https://avatars.githubusercontent.com/u/24235344?v=4"/></br>[vibraniumdev](https://github.com/vibraniumdev) | <img width="50" src="https://avatars.githubusercontent.com/u/2257024?v=4"/></br>[gusbemacbe](https://github.com/gusbemacbe) | <img width="50" src="https://avatars.githubusercontent.com/u/64917442?v=4"/></br>[HOLLYwyh](https://github.com/HOLLYwyh) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/18524580?v=4"/></br>[Fvbor](https://github.com/Fvbor) | <img width="50" src="https://avatars.githubusercontent.com/u/22606250?v=4"/></br>[bennetthanna](https://github.com/bennetthanna) | <img width="50" src="https://avatars.githubusercontent.com/u/67231570?v=4"/></br>[harshitkathuria](https://github.com/harshitkathuria) | <img width="50" src="https://avatars.githubusercontent.com/u/1716229?v=4"/></br>[Vistaus](https://github.com/Vistaus) | <img width="50" src="https://avatars.githubusercontent.com/u/6509881?v=4"/></br>[ianjs](https://github.com/ianjs) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/19862172?v=4"/></br>[iahmedbacha](https://github.com/iahmedbacha) | <img width="50" src="https://avatars.githubusercontent.com/u/1533624?v=4"/></br>[IrvinDominin](https://github.com/IrvinDominin) | <img width="50" src="https://avatars.githubusercontent.com/u/33200024?v=4"/></br>[ishammahajan](https://github.com/ishammahajan) | <img width="50" src="https://avatars.githubusercontent.com/u/6916297?v=4"/></br>[ffadilaputra](https://github.com/ffadilaputra) | <img width="50" src="https://avatars.githubusercontent.com/u/19985741?v=4"/></br>[JRaiden16](https://github.com/JRaiden16) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/11466782?v=4"/></br>[jacobherrington](https://github.com/jacobherrington) | <img width="50" src="https://avatars.githubusercontent.com/u/9365179?v=4"/></br>[jamesadjinwa](https://github.com/jamesadjinwa) | <img width="50" src="https://avatars.githubusercontent.com/u/20801821?v=4"/></br>[jrwrigh](https://github.com/jrwrigh) | <img width="50" src="https://avatars.githubusercontent.com/u/4995433?v=4"/></br>[jaredcrowe](https://github.com/jaredcrowe) | <img width="50" src="https://avatars.githubusercontent.com/u/4087105?v=4"/></br>[volatilevar](https://github.com/volatilevar) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/47724360?v=4"/></br>[innkuika](https://github.com/innkuika) | <img width="50" src="https://avatars.githubusercontent.com/u/163555?v=4"/></br>[JoelRSimpson](https://github.com/JoelRSimpson) | <img width="50" src="https://avatars.githubusercontent.com/u/6965062?v=4"/></br>[joeltaylor](https://github.com/joeltaylor) | <img width="50" src="https://avatars.githubusercontent.com/u/242107?v=4"/></br>[exic](https://github.com/exic) | <img width="50" src="https://avatars.githubusercontent.com/u/13716151?v=4"/></br>[JonathanPlasse](https://github.com/JonathanPlasse) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/1248504?v=4"/></br>[joesfer](https://github.com/joesfer) | <img width="50" src="https://avatars.githubusercontent.com/u/6048003?v=4"/></br>[joybinchen](https://github.com/joybinchen) | <img width="50" src="https://avatars.githubusercontent.com/u/37601331?v=4"/></br>[kaustubhsh](https://github.com/kaustubhsh) | <img width="50" src="https://avatars.githubusercontent.com/u/1560189?v=4"/></br>[y-usuzumi](https://github.com/y-usuzumi) | <img width="50" src="https://avatars.githubusercontent.com/u/1660460?v=4"/></br>[xuhcc](https://github.com/xuhcc) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/16933735?v=4"/></br>[kirtanprht](https://github.com/kirtanprht) | <img width="50" src="https://avatars.githubusercontent.com/u/37491732?v=4"/></br>[k0ur0x](https://github.com/k0ur0x) | <img width="50" src="https://avatars.githubusercontent.com/u/7824233?v=4"/></br>[kklas](https://github.com/kklas) | <img width="50" src="https://avatars.githubusercontent.com/u/8622992?v=4"/></br>[xmlangel](https://github.com/xmlangel) | <img width="50" src="https://avatars.githubusercontent.com/u/1055100?v=4"/></br>[troilus](https://github.com/troilus) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/2599210?v=4"/></br>[lboullo0](https://github.com/lboullo0) | <img width="50" src="https://avatars.githubusercontent.com/u/1562062?v=4"/></br>[dbinary](https://github.com/dbinary) | <img width="50" src="https://avatars.githubusercontent.com/u/15436007?v=4"/></br>[marc-bouvier](https://github.com/marc-bouvier) | <img width="50" src="https://avatars.githubusercontent.com/u/5699725?v=4"/></br>[mvonmaltitz](https://github.com/mvonmaltitz) | <img width="50" src="https://avatars.githubusercontent.com/u/11036464?v=4"/></br>[mlkood](https://github.com/mlkood) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/2480960?v=4"/></br>[plextoriano](https://github.com/plextoriano) | <img width="50" src="https://avatars.githubusercontent.com/u/5788516?v=4"/></br>[Marmo](https://github.com/Marmo) | <img width="50" src="https://avatars.githubusercontent.com/u/29300939?v=4"/></br>[mcejp](https://github.com/mcejp) | <img width="50" src="https://avatars.githubusercontent.com/u/640949?v=4"/></br>[freaktechnik](https://github.com/freaktechnik) | <img width="50" src="https://avatars.githubusercontent.com/u/79802125?v=4"/></br>[martinkorelic](https://github.com/martinkorelic) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/287105?v=4"/></br>[Petemir](https://github.com/Petemir) | <img width="50" src="https://avatars.githubusercontent.com/u/5218859?v=4"/></br>[matsair](https://github.com/matsair) | <img width="50" src="https://avatars.githubusercontent.com/u/12831489?v=4"/></br>[mgroth0](https://github.com/mgroth0) | <img width="50" src="https://avatars.githubusercontent.com/u/21796?v=4"/></br>[silentmatt](https://github.com/silentmatt) | <img width="50" src="https://avatars.githubusercontent.com/u/76700192?v=4"/></br>[maxs-test](https://github.com/maxs-test) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/59669349?v=4"/></br>[MichBoi](https://github.com/MichBoi) | <img width="50" src="https://avatars.githubusercontent.com/u/51273874?v=4"/></br>[MichipX](https://github.com/MichipX) | <img width="50" src="https://avatars.githubusercontent.com/u/53177864?v=4"/></br>[MrTraduttore](https://github.com/MrTraduttore) | <img width="50" src="https://avatars.githubusercontent.com/u/48156230?v=4"/></br>[sanjarcode](https://github.com/sanjarcode) | <img width="50" src="https://avatars.githubusercontent.com/u/43955099?v=4"/></br>[Mustafa-ALD](https://github.com/Mustafa-ALD) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/9076687?v=4"/></br>[NJannasch](https://github.com/NJannasch) | <img width="50" src="https://avatars.githubusercontent.com/u/8016073?v=4"/></br>[zomglings](https://github.com/zomglings) | <img width="50" src="https://avatars.githubusercontent.com/u/10386884?v=4"/></br>[Frichetten](https://github.com/Frichetten) | <img width="50" src="https://avatars.githubusercontent.com/u/5541611?v=4"/></br>[nicolas-suzuki](https://github.com/nicolas-suzuki) | <img width="50" src="https://avatars.githubusercontent.com/u/12369770?v=4"/></br>[Ouvill](https://github.com/Ouvill) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/43815417?v=4"/></br>[shorty2380](https://github.com/shorty2380) | <img width="50" src="https://avatars.githubusercontent.com/u/15014287?v=4"/></br>[dist3r](https://github.com/dist3r) | <img width="50" src="https://avatars.githubusercontent.com/u/19418601?v=4"/></br>[rakleed](https://github.com/rakleed) | <img width="50" src="https://avatars.githubusercontent.com/u/7881932?v=4"/></br>[idle-code](https://github.com/idle-code) | <img width="50" src="https://avatars.githubusercontent.com/u/168931?v=4"/></br>[bobchao](https://github.com/bobchao) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/6306608?v=4"/></br>[Diadlo](https://github.com/Diadlo) | <img width="50" src="https://avatars.githubusercontent.com/u/42793024?v=4"/></br>[pranavmodx](https://github.com/pranavmodx) | <img width="50" src="https://avatars.githubusercontent.com/u/50834839?v=4"/></br>[R3dError](https://github.com/R3dError) | <img width="50" src="https://avatars.githubusercontent.com/u/42652941?v=4"/></br>[rajprakash00](https://github.com/rajprakash00) | <img width="50" src="https://avatars.githubusercontent.com/u/32304956?v=4"/></br>[rahil1304](https://github.com/rahil1304) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/8257474?v=4"/></br>[rasulkireev](https://github.com/rasulkireev) | <img width="50" src="https://avatars.githubusercontent.com/u/17312341?v=4"/></br>[reinhart1010](https://github.com/reinhart1010) | <img width="50" src="https://avatars.githubusercontent.com/u/60484714?v=4"/></br>[Retew](https://github.com/Retew) | <img width="50" src="https://avatars.githubusercontent.com/u/10456131?v=4"/></br>[ambrt](https://github.com/ambrt) | <img width="50" src="https://avatars.githubusercontent.com/u/15892014?v=4"/></br>[Derkades](https://github.com/Derkades) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/49439044?v=4"/></br>[fourstepper](https://github.com/fourstepper) | <img width="50" src="https://avatars.githubusercontent.com/u/54365?v=4"/></br>[rodgco](https://github.com/rodgco) | <img width="50" src="https://avatars.githubusercontent.com/u/96014?v=4"/></br>[Ronnie76er](https://github.com/Ronnie76er) | <img width="50" src="https://avatars.githubusercontent.com/u/79168?v=4"/></br>[roryokane](https://github.com/roryokane) | <img width="50" src="https://avatars.githubusercontent.com/u/744655?v=4"/></br>[ruzaq](https://github.com/ruzaq) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/20490839?v=4"/></br>[szokesandor](https://github.com/szokesandor) | <img width="50" src="https://avatars.githubusercontent.com/u/19328605?v=4"/></br>[SamuelBlickle](https://github.com/SamuelBlickle) | <img width="50" src="https://avatars.githubusercontent.com/u/80849457?v=4"/></br>[livingc0l0ur](https://github.com/livingc0l0ur) | <img width="50" src="https://avatars.githubusercontent.com/u/1776?v=4"/></br>[bronson](https://github.com/bronson) | <img width="50" src="https://avatars.githubusercontent.com/u/24606935?v=4"/></br>[semperor](https://github.com/semperor) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/607938?v=4"/></br>[shawnaxsom](https://github.com/shawnaxsom) | <img width="50" src="https://avatars.githubusercontent.com/u/9937486?v=4"/></br>[SFoskitt](https://github.com/SFoskitt) | <img width="50" src="https://avatars.githubusercontent.com/u/505011?v=4"/></br>[kcrt](https://github.com/kcrt) | <img width="50" src="https://avatars.githubusercontent.com/u/538584?v=4"/></br>[xissy](https://github.com/xissy) | <img width="50" src="https://avatars.githubusercontent.com/u/164962?v=4"/></br>[tams](https://github.com/tams) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/466122?v=4"/></br>[Tekki](https://github.com/Tekki) | <img width="50" src="https://avatars.githubusercontent.com/u/2112477?v=4"/></br>[ThatcherC](https://github.com/ThatcherC) | <img width="50" src="https://avatars.githubusercontent.com/u/21969426?v=4"/></br>[TheoDutch](https://github.com/TheoDutch) | <img width="50" src="https://avatars.githubusercontent.com/u/8731922?v=4"/></br>[tbroadley](https://github.com/tbroadley) | <img width="50" src="https://avatars.githubusercontent.com/u/114300?v=4"/></br>[Kriechi](https://github.com/Kriechi) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/3457339?v=4"/></br>[tkilaker](https://github.com/tkilaker) | <img width="50" src="https://avatars.githubusercontent.com/u/802148?v=4"/></br>[Tim-Erwin](https://github.com/Tim-Erwin) | <img width="50" src="https://avatars.githubusercontent.com/u/4201229?v=4"/></br>[tcyrus](https://github.com/tcyrus) | <img width="50" src="https://avatars.githubusercontent.com/u/834914?v=4"/></br>[tobias-grasse](https://github.com/tobias-grasse) | <img width="50" src="https://avatars.githubusercontent.com/u/6691273?v=4"/></br>[strobeltobias](https://github.com/strobeltobias) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/1677578?v=4"/></br>[kostegit](https://github.com/kostegit) | <img width="50" src="https://avatars.githubusercontent.com/u/70296?v=4"/></br>[tbergeron](https://github.com/tbergeron) | <img width="50" src="https://avatars.githubusercontent.com/u/10265443?v=4"/></br>[Ullas-Aithal](https://github.com/Ullas-Aithal) | <img width="50" src="https://avatars.githubusercontent.com/u/6104498?v=4"/></br>[MyTheValentinus](https://github.com/MyTheValentinus) | <img width="50" src="https://avatars.githubusercontent.com/u/2830093?v=4"/></br>[vassudanagunta](https://github.com/vassudanagunta) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/54314949?v=4"/></br>[vijayjoshi16](https://github.com/vijayjoshi16) | <img width="50" src="https://avatars.githubusercontent.com/u/59287619?v=4"/></br>[max-keviv](https://github.com/max-keviv) | <img width="50" src="https://avatars.githubusercontent.com/u/598576?v=4"/></br>[vandreykiv](https://github.com/vandreykiv) | <img width="50" src="https://avatars.githubusercontent.com/u/26511487?v=4"/></br>[WisdomCode](https://github.com/WisdomCode) | <img width="50" src="https://avatars.githubusercontent.com/u/1921957?v=4"/></br>[xsak](https://github.com/xsak) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/11031696?v=4"/></br>[ymitsos](https://github.com/ymitsos) | <img width="50" src="https://avatars.githubusercontent.com/u/63324960?v=4"/></br>[abolishallprivateproperty](https://github.com/abolishallprivateproperty) | <img width="50" src="https://avatars.githubusercontent.com/u/11336076?v=4"/></br>[aerotog](https://github.com/aerotog) | <img width="50" src="https://avatars.githubusercontent.com/u/39854348?v=4"/></br>[albertopasqualetto](https://github.com/albertopasqualetto) | <img width="50" src="https://avatars.githubusercontent.com/u/44570278?v=4"/></br>[asrient](https://github.com/asrient) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/621360?v=4"/></br>[bestlibre](https://github.com/bestlibre) | <img width="50" src="https://avatars.githubusercontent.com/u/35600612?v=4"/></br>[boring10](https://github.com/boring10) | <img width="50" src="https://avatars.githubusercontent.com/u/13894820?v=4"/></br>[cadolphs](https://github.com/cadolphs) | <img width="50" src="https://avatars.githubusercontent.com/u/12461043?v=4"/></br>[colorchestra](https://github.com/colorchestra) | <img width="50" src="https://avatars.githubusercontent.com/u/30935096?v=4"/></br>[cybertramp](https://github.com/cybertramp) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/15824892?v=4"/></br>[dartero](https://github.com/dartero) | <img width="50" src="https://avatars.githubusercontent.com/u/9694906?v=4"/></br>[delta-emil](https://github.com/delta-emil) | <img width="50" src="https://avatars.githubusercontent.com/u/926263?v=4"/></br>[doc75](https://github.com/doc75) | <img width="50" src="https://avatars.githubusercontent.com/u/5589253?v=4"/></br>[dsp77](https://github.com/dsp77) | <img width="50" src="https://avatars.githubusercontent.com/u/2903013?v=4"/></br>[ebayer](https://github.com/ebayer) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/9206310?v=4"/></br>[elsiehupp](https://github.com/elsiehupp) | <img width="50" src="https://avatars.githubusercontent.com/u/701050?v=4"/></br>[espinosa](https://github.com/espinosa) | <img width="50" src="https://avatars.githubusercontent.com/u/18619090?v=4"/></br>[exponentactivity](https://github.com/exponentactivity) | <img width="50" src="https://avatars.githubusercontent.com/u/16708935?v=4"/></br>[exprez135](https://github.com/exprez135) | <img width="50" src="https://avatars.githubusercontent.com/u/9768112?v=4"/></br>[fab4x](https://github.com/fab4x) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/47755037?v=4"/></br>[fabianski7](https://github.com/fabianski7) | <img width="50" src="https://avatars.githubusercontent.com/u/14201321?v=4"/></br>[rasperepodvipodvert](https://github.com/rasperepodvipodvert) | <img width="50" src="https://avatars.githubusercontent.com/u/748808?v=4"/></br>[gasolin](https://github.com/gasolin) | <img width="50" src="https://avatars.githubusercontent.com/u/47191051?v=4"/></br>[githubaccount073](https://github.com/githubaccount073) | <img width="50" src="https://avatars.githubusercontent.com/u/43672033?v=4"/></br>[hms5232](https://github.com/hms5232) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/11388094?v=4"/></br>[hydrandt](https://github.com/hydrandt) | <img width="50" src="https://avatars.githubusercontent.com/u/61012185?v=4"/></br>[iamtalwinder](https://github.com/iamtalwinder) | <img width="50" src="https://avatars.githubusercontent.com/u/557540?v=4"/></br>[jabdoa2](https://github.com/jabdoa2) | <img width="50" src="https://avatars.githubusercontent.com/u/29166402?v=4"/></br>[jduar](https://github.com/jduar) | <img width="50" src="https://avatars.githubusercontent.com/u/2678545?v=4"/></br>[jibedoubleve](https://github.com/jibedoubleve) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/53862536?v=4"/></br>[johanvanheusden](https://github.com/johanvanheusden) | <img width="50" src="https://avatars.githubusercontent.com/u/38327267?v=4"/></br>[jtagcat](https://github.com/jtagcat) | <img width="50" src="https://avatars.githubusercontent.com/u/61631665?v=4"/></br>[konhi](https://github.com/konhi) | <img width="50" src="https://avatars.githubusercontent.com/u/54991735?v=4"/></br>[krzysiekwie](https://github.com/krzysiekwie) | <img width="50" src="https://avatars.githubusercontent.com/u/12849008?v=4"/></br>[lighthousebulb](https://github.com/lighthousebulb) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/4140247?v=4"/></br>[luzpaz](https://github.com/luzpaz) | <img width="50" src="https://avatars.githubusercontent.com/u/29355048?v=4"/></br>[majsterkovic](https://github.com/majsterkovic) | <img width="50" src="https://avatars.githubusercontent.com/u/77744862?v=4"/></br>[mak2002](https://github.com/mak2002) | <img width="50" src="https://avatars.githubusercontent.com/u/30428258?v=4"/></br>[nmiquan](https://github.com/nmiquan) | <img width="50" src="https://avatars.githubusercontent.com/u/31123054?v=4"/></br>[nullpointer666](https://github.com/nullpointer666) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/2979926?v=4"/></br>[oscaretu](https://github.com/oscaretu) | <img width="50" src="https://avatars.githubusercontent.com/u/36965591?v=4"/></br>[oskarsh](https://github.com/oskarsh) | <img width="50" src="https://avatars.githubusercontent.com/u/52031346?v=4"/></br>[osso73](https://github.com/osso73) | <img width="50" src="https://avatars.githubusercontent.com/u/29743024?v=4"/></br>[over-soul](https://github.com/over-soul) | <img width="50" src="https://avatars.githubusercontent.com/u/42961947?v=4"/></br>[pensierocrea](https://github.com/pensierocrea) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/45542782?v=4"/></br>[pomeloy](https://github.com/pomeloy) | <img width="50" src="https://avatars.githubusercontent.com/u/10206967?v=4"/></br>[rhtenhove](https://github.com/rhtenhove) | <img width="50" src="https://avatars.githubusercontent.com/u/16728217?v=4"/></br>[rikanotank1](https://github.com/rikanotank1) | <img width="50" src="https://avatars.githubusercontent.com/u/24560368?v=4"/></br>[rxliuli](https://github.com/rxliuli) | <img width="50" src="https://avatars.githubusercontent.com/u/14062932?v=4"/></br>[simonsan](https://github.com/simonsan) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/5004545?v=4"/></br>[stellarpower](https://github.com/stellarpower) | <img width="50" src="https://avatars.githubusercontent.com/u/20983267?v=4"/></br>[suixinio](https://github.com/suixinio) | <img width="50" src="https://avatars.githubusercontent.com/u/12995773?v=4"/></br>[sumomo-99](https://github.com/sumomo-99) | <img width="50" src="https://avatars.githubusercontent.com/u/367170?v=4"/></br>[xtatsux](https://github.com/xtatsux) | <img width="50" src="https://avatars.githubusercontent.com/u/6908872?v=4"/></br>[taw00](https://github.com/taw00) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/10956653?v=4"/></br>[tcassaert](https://github.com/tcassaert) | <img width="50" src="https://avatars.githubusercontent.com/u/46327531?v=4"/></br>[victante](https://github.com/victante) | <img width="50" src="https://avatars.githubusercontent.com/u/7252567?v=4"/></br>[Voltinus](https://github.com/Voltinus) | <img width="50" src="https://avatars.githubusercontent.com/u/2216902?v=4"/></br>[xcffl](https://github.com/xcffl) | <img width="50" src="https://avatars.githubusercontent.com/u/46404814?v=4"/></br>[yourcontact](https://github.com/yourcontact) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/37692927?v=4"/></br>[zaoyifan](https://github.com/zaoyifan) | <img width="50" src="https://avatars.githubusercontent.com/u/10813608?v=4"/></br>[zawnk](https://github.com/zawnk) | <img width="50" src="https://avatars.githubusercontent.com/u/55245068?v=4"/></br>[zen-quo](https://github.com/zen-quo) | <img width="50" src="https://avatars.githubusercontent.com/u/23507174?v=4"/></br>[zozolina123](https://github.com/zozolina123) | <img width="50" src="https://avatars.githubusercontent.com/u/25315?v=4"/></br>[xcession](https://github.com/xcession) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/34542665?v=4"/></br>[paventyang](https://github.com/paventyang) | <img width="50" src="https://avatars.githubusercontent.com/u/608014?v=4"/></br>[jackytsu](https://github.com/jackytsu) | <img width="50" src="https://avatars.githubusercontent.com/u/1308646?v=4"/></br>[zhangmx](https://github.com/zhangmx) | | |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/1285584?v=4"/></br>[laurent22](https://github.com/laurent22) | <img width="50" src="https://avatars.githubusercontent.com/u/223439?v=4"/></br>[tessus](https://github.com/tessus) | <img width="50" src="https://avatars.githubusercontent.com/u/2179547?v=4"/></br>[CalebJohn](https://github.com/CalebJohn) | <img width="50" src="https://avatars.githubusercontent.com/u/46334387?v=4"/></br>[personalizedrefrigerator](https://github.com/personalizedrefrigerator) | <img width="50" src="https://avatars.githubusercontent.com/u/995612?v=4"/></br>[roman-r-m](https://github.com/roman-r-m) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/1732810?v=4"/></br>[miciasto](https://github.com/miciasto) | <img width="50" src="https://avatars.githubusercontent.com/u/16041683?v=4"/></br>[ken1kob](https://github.com/ken1kob) | <img width="50" src="https://avatars.githubusercontent.com/u/29672555?v=4"/></br>[genneko](https://github.com/genneko) | <img width="50" src="https://avatars.githubusercontent.com/u/58074586?v=4"/></br>[Daeraxa](https://github.com/Daeraxa) | <img width="50" src="https://avatars.githubusercontent.com/u/4553672?v=4"/></br>[tanrax](https://github.com/tanrax) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/63491353?v=4"/></br>[j-krl](https://github.com/j-krl) | <img width="50" src="https://avatars.githubusercontent.com/u/62299611?v=4"/></br>[wh201906](https://github.com/wh201906) | <img width="50" src="https://avatars.githubusercontent.com/u/24863925?v=4"/></br>[JackGruber](https://github.com/JackGruber) | <img width="50" src="https://avatars.githubusercontent.com/u/30305957?v=4"/></br>[naviji](https://github.com/naviji) | <img width="50" src="https://avatars.githubusercontent.com/u/3542031?v=4"/></br>[PackElend](https://github.com/PackElend) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/32807437?v=4"/></br>[julien-me](https://github.com/julien-me) | <img width="50" src="https://avatars.githubusercontent.com/u/5051088?v=4"/></br>[pedr](https://github.com/pedr) | <img width="50" src="https://avatars.githubusercontent.com/u/4374338?v=4"/></br>[potatogim](https://github.com/potatogim) | <img width="50" src="https://avatars.githubusercontent.com/u/84130654?v=4"/></br>[JonatanWick](https://github.com/JonatanWick) | <img width="50" src="https://avatars.githubusercontent.com/u/2063957?v=4"/></br>[Ardakilic](https://github.com/Ardakilic) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/43657314?v=4"/></br>[milotype](https://github.com/milotype) | <img width="50" src="https://avatars.githubusercontent.com/u/44570278?v=4"/></br>[asrient](https://github.com/asrient) | <img width="50" src="https://avatars.githubusercontent.com/u/8701534?v=4"/></br>[rtmkrlv](https://github.com/rtmkrlv) | <img width="50" src="https://avatars.githubusercontent.com/u/10997189?v=4"/></br>[fmrtn](https://github.com/fmrtn) | <img width="50" src="https://avatars.githubusercontent.com/u/68117355?v=4"/></br>[Mr-Kanister](https://github.com/Mr-Kanister) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/4299398?v=4"/></br>[palerdot](https://github.com/palerdot) | <img width="50" src="https://avatars.githubusercontent.com/u/10927304?v=4"/></br>[matsest](https://github.com/matsest) | <img width="50" src="https://avatars.githubusercontent.com/u/6979755?v=4"/></br>[devonzuegel](https://github.com/devonzuegel) | <img width="50" src="https://avatars.githubusercontent.com/u/26695184?v=4"/></br>[anjulalk](https://github.com/anjulalk) | <img width="50" src="https://avatars.githubusercontent.com/u/16101778?v=4"/></br>[gabcoh](https://github.com/gabcoh) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/19213902?v=4"/></br>[hubertfilho](https://github.com/hubertfilho) | <img width="50" src="https://avatars.githubusercontent.com/u/6319051?v=4"/></br>[abonte](https://github.com/abonte) | <img width="50" src="https://avatars.githubusercontent.com/u/1685517?v=4"/></br>[Abijeet](https://github.com/Abijeet) | <img width="50" src="https://avatars.githubusercontent.com/u/27751740?v=4"/></br>[ishantgupta777](https://github.com/ishantgupta777) | <img width="50" src="https://avatars.githubusercontent.com/u/63025323?v=4"/></br>[ScriptInfra](https://github.com/ScriptInfra) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/6196533?v=4"/></br>[jd1378](https://github.com/jd1378) | <img width="50" src="https://avatars.githubusercontent.com/u/44024553?v=4"/></br>[rabeehrz](https://github.com/rabeehrz) | <img width="50" src="https://avatars.githubusercontent.com/u/35633575?v=4"/></br>[coderrsid](https://github.com/coderrsid) | <img width="50" src="https://avatars.githubusercontent.com/u/7415668?v=4"/></br>[mablin7](https://github.com/mablin7) | <img width="50" src="https://avatars.githubusercontent.com/u/608014?v=4"/></br>[jackytsu](https://github.com/jackytsu) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/77744862?v=4"/></br>[mak2002](https://github.com/mak2002) | <img width="50" src="https://avatars.githubusercontent.com/u/3985557?v=4"/></br>[XarisA](https://github.com/XarisA) | <img width="50" src="https://avatars.githubusercontent.com/u/208212?v=4"/></br>[foxmask](https://github.com/foxmask) | <img width="50" src="https://avatars.githubusercontent.com/u/6557454?v=4"/></br>[innocuo](https://github.com/innocuo) | <img width="50" src="https://avatars.githubusercontent.com/u/54268438?v=4"/></br>[Rahulm2310](https://github.com/Rahulm2310) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/8184424?v=4"/></br>[Ahmad45123](https://github.com/Ahmad45123) | <img width="50" src="https://avatars.githubusercontent.com/u/49979415?v=4"/></br>[jonath92](https://github.com/jonath92) | <img width="50" src="https://avatars.githubusercontent.com/u/1904967?v=4"/></br>[readingsnail](https://github.com/readingsnail) | <img width="50" src="https://avatars.githubusercontent.com/u/134083?v=4"/></br>[xavivars](https://github.com/xavivars) | <img width="50" src="https://avatars.githubusercontent.com/u/51550769?v=4"/></br>[rnbastos](https://github.com/rnbastos) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/4237724?v=4"/></br>[alexdevero](https://github.com/alexdevero) | <img width="50" src="https://avatars.githubusercontent.com/u/71190696?v=4"/></br>[Elaborendum](https://github.com/Elaborendum) | <img width="50" src="https://avatars.githubusercontent.com/u/42747216?v=4"/></br>[Mannivu](https://github.com/Mannivu) | <img width="50" src="https://avatars.githubusercontent.com/u/36989112?v=4"/></br>[nishantwrp](https://github.com/nishantwrp) | <img width="50" src="https://avatars.githubusercontent.com/u/35904727?v=4"/></br>[Runo-saduwa](https://github.com/Runo-saduwa) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/3250983?v=4"/></br>[shinglyu](https://github.com/shinglyu) | <img width="50" src="https://avatars.githubusercontent.com/u/30352484?v=4"/></br>[Tolu-Mals](https://github.com/Tolu-Mals) | <img width="50" src="https://avatars.githubusercontent.com/u/5365582?v=4"/></br>[marcosvega91](https://github.com/marcosvega91) | <img width="50" src="https://avatars.githubusercontent.com/u/99097412?v=4"/></br>[mrkaato0](https://github.com/mrkaato0) | <img width="50" src="https://avatars.githubusercontent.com/u/37639389?v=4"/></br>[petrz12](https://github.com/petrz12) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/4245227?v=4"/></br>[zblesk](https://github.com/zblesk) | <img width="50" src="https://avatars.githubusercontent.com/u/5730052?v=4"/></br>[vsimkus](https://github.com/vsimkus) | <img width="50" src="https://avatars.githubusercontent.com/u/20461071?v=4"/></br>[Vaso3](https://github.com/Vaso3) | <img width="50" src="https://avatars.githubusercontent.com/u/3194829?v=4"/></br>[moltenform](https://github.com/moltenform) | <img width="50" src="https://avatars.githubusercontent.com/u/33229141?v=4"/></br>[marph91](https://github.com/marph91) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/5199995?v=4"/></br>[zuphilip](https://github.com/zuphilip) | <img width="50" src="https://avatars.githubusercontent.com/u/10289737?v=4"/></br>[Retr0ve](https://github.com/Retr0ve) | <img width="50" src="https://avatars.githubusercontent.com/u/54576074?v=4"/></br>[Rishabh-malhotraa](https://github.com/Rishabh-malhotraa) | <img width="50" src="https://avatars.githubusercontent.com/u/559346?v=4"/></br>[metbril](https://github.com/metbril) | <img width="50" src="https://avatars.githubusercontent.com/u/36622934?v=4"/></br>[SFulpius](https://github.com/SFulpius) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/531704?v=4"/></br>[TaoK](https://github.com/TaoK) | <img width="50" src="https://avatars.githubusercontent.com/u/47623588?v=4"/></br>[WhiredPlanck](https://github.com/WhiredPlanck) | <img width="50" src="https://avatars.githubusercontent.com/u/32396?v=4"/></br>[ProgramFan](https://github.com/ProgramFan) | <img width="50" src="https://avatars.githubusercontent.com/u/32196447?v=4"/></br>[yaozeye](https://github.com/yaozeye) | <img width="50" src="https://avatars.githubusercontent.com/u/12264626?v=4"/></br>[ylc395](https://github.com/ylc395) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/8716226?v=4"/></br>[amandamcg](https://github.com/amandamcg) | <img width="50" src="https://avatars.githubusercontent.com/u/359140?v=4"/></br>[leematos](https://github.com/leematos) | <img width="50" src="https://avatars.githubusercontent.com/u/17768566?v=4"/></br>[RenatoXSR](https://github.com/RenatoXSR) | <img width="50" src="https://avatars.githubusercontent.com/u/54888685?v=4"/></br>[RedDocMD](https://github.com/RedDocMD) | <img width="50" src="https://avatars.githubusercontent.com/u/31567272?v=4"/></br>[t1011](https://github.com/t1011) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/44198148?v=4"/></br>[whalehub](https://github.com/whalehub) | <img width="50" src="https://avatars.githubusercontent.com/u/12906090?v=4"/></br>[amitsin6h](https://github.com/amitsin6h) | <img width="50" src="https://avatars.githubusercontent.com/u/628474?v=4"/></br>[Atalanttore](https://github.com/Atalanttore) | <img width="50" src="https://avatars.githubusercontent.com/u/5058349?v=4"/></br>[hieuthi](https://github.com/hieuthi) | <img width="50" src="https://avatars.githubusercontent.com/u/23281486?v=4"/></br>[martonpaulo](https://github.com/martonpaulo) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/390889?v=4"/></br>[mmahmoudian](https://github.com/mmahmoudian) | <img width="50" src="https://avatars.githubusercontent.com/u/168931?v=4"/></br>[bobchao](https://github.com/bobchao) | <img width="50" src="https://avatars.githubusercontent.com/u/4497566?v=4"/></br>[rc2dev](https://github.com/rc2dev) | <img width="50" src="https://avatars.githubusercontent.com/u/43534227?v=4"/></br>[Rishabhraghwendra18](https://github.com/Rishabhraghwendra18) | <img width="50" src="https://avatars.githubusercontent.com/u/7091080?v=4"/></br>[sinkuu](https://github.com/sinkuu) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/6734573?v=4"/></br>[stweil](https://github.com/stweil) | <img width="50" src="https://avatars.githubusercontent.com/u/59690052?v=4"/></br>[Subhra264](https://github.com/Subhra264) | <img width="50" src="https://avatars.githubusercontent.com/u/692072?v=4"/></br>[conyx](https://github.com/conyx) | <img width="50" src="https://avatars.githubusercontent.com/u/49116134?v=4"/></br>[anihm136](https://github.com/anihm136) | <img width="50" src="https://avatars.githubusercontent.com/u/937861?v=4"/></br>[archont00](https://github.com/archont00) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/32770029?v=4"/></br>[bradmcl](https://github.com/bradmcl) | <img width="50" src="https://avatars.githubusercontent.com/u/1340627?v=4"/></br>[jcgurango](https://github.com/jcgurango) | <img width="50" src="https://avatars.githubusercontent.com/u/36228623?v=4"/></br>[mrkaato](https://github.com/mrkaato) | <img width="50" src="https://avatars.githubusercontent.com/u/22592201?v=4"/></br>[tfinnberg](https://github.com/tfinnberg) | <img width="50" src="https://avatars.githubusercontent.com/u/63918341?v=4"/></br>[adarsh-sgh](https://github.com/adarsh-sgh) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/3870964?v=4"/></br>[marcushill](https://github.com/marcushill) | <img width="50" src="https://avatars.githubusercontent.com/u/102242?v=4"/></br>[nathanleiby](https://github.com/nathanleiby) | <img width="50" src="https://avatars.githubusercontent.com/u/13251?v=4"/></br>[piotrb](https://github.com/piotrb) | <img width="50" src="https://avatars.githubusercontent.com/u/226708?v=4"/></br>[RaphaelKimmig](https://github.com/RaphaelKimmig) | <img width="50" src="https://avatars.githubusercontent.com/u/10060747?v=4"/></br>[Wartijn](https://github.com/Wartijn) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/40672207?v=4"/></br>[xUser5000](https://github.com/xUser5000) | <img width="50" src="https://avatars.githubusercontent.com/u/41290751?v=4"/></br>[serenitatis](https://github.com/serenitatis) | <img width="50" src="https://avatars.githubusercontent.com/u/81777961?v=4"/></br>[k33pn3xtlvl](https://github.com/k33pn3xtlvl) | <img width="50" src="https://avatars.githubusercontent.com/u/17809291?v=4"/></br>[antontkv](https://github.com/antontkv) | <img width="50" src="https://avatars.githubusercontent.com/u/28987176?v=4"/></br>[infinity052](https://github.com/infinity052) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/55127997?v=4"/></br>[entrymaster](https://github.com/entrymaster) | <img width="50" src="https://avatars.githubusercontent.com/u/21161146?v=4"/></br>[BartBucknill](https://github.com/BartBucknill) | <img width="50" src="https://avatars.githubusercontent.com/u/94234459?v=4"/></br>[betty-alagwu](https://github.com/betty-alagwu) | <img width="50" src="https://avatars.githubusercontent.com/u/2494769?v=4"/></br>[mrwulf](https://github.com/mrwulf) | <img width="50" src="https://avatars.githubusercontent.com/u/60824?v=4"/></br>[brttbndr](https://github.com/brttbndr) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/606038?v=4"/></br>[cas--](https://github.com/cas--) | <img width="50" src="https://avatars.githubusercontent.com/u/560571?v=4"/></br>[chrisb86](https://github.com/chrisb86) | <img width="50" src="https://avatars.githubusercontent.com/u/1686759?v=4"/></br>[chrmoritz](https://github.com/chrmoritz) | <img width="50" src="https://avatars.githubusercontent.com/u/11857950?v=4"/></br>[djunho](https://github.com/djunho) | <img width="50" src="https://avatars.githubusercontent.com/u/1044056?v=4"/></br>[daniellandau](https://github.com/daniellandau) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/40627944?v=4"/></br>[krote5k](https://github.com/krote5k) | <img width="50" src="https://avatars.githubusercontent.com/u/5001259?v=4"/></br>[ethan42411](https://github.com/ethan42411) | <img width="50" src="https://avatars.githubusercontent.com/u/2733783?v=4"/></br>[JOJ0](https://github.com/JOJ0) | <img width="50" src="https://avatars.githubusercontent.com/u/17108695?v=4"/></br>[jalajcodes](https://github.com/jalajcodes) | <img width="50" src="https://avatars.githubusercontent.com/u/238088?v=4"/></br>[jblunck](https://github.com/jblunck) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/3140223?v=4"/></br>[jdrobertso](https://github.com/jdrobertso) | <img width="50" src="https://avatars.githubusercontent.com/u/37297218?v=4"/></br>[Jesssullivan](https://github.com/Jesssullivan) | <img width="50" src="https://avatars.githubusercontent.com/u/339645?v=4"/></br>[jmontane](https://github.com/jmontane) | <img width="50" src="https://avatars.githubusercontent.com/u/69011?v=4"/></br>[johanhammar](https://github.com/johanhammar) | <img width="50" src="https://avatars.githubusercontent.com/u/71817691?v=4"/></br>[krishna8421](https://github.com/krishna8421) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/118282653?v=4"/></br>[Linkosred](https://github.com/Linkosred) | <img width="50" src="https://avatars.githubusercontent.com/u/4168339?v=4"/></br>[solariz](https://github.com/solariz) | <img width="50" src="https://avatars.githubusercontent.com/u/25288?v=4"/></br>[maicki](https://github.com/maicki) | <img width="50" src="https://avatars.githubusercontent.com/u/2136373?v=4"/></br>[mjjzf](https://github.com/mjjzf) | <img width="50" src="https://avatars.githubusercontent.com/u/7561827?v=4"/></br>[popovoleksandr](https://github.com/popovoleksandr) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/2501211?v=4"/></br>[Philipp91](https://github.com/Philipp91) | <img width="50" src="https://avatars.githubusercontent.com/u/27608187?v=4"/></br>[rt-oliveira](https://github.com/rt-oliveira) | <img width="50" src="https://avatars.githubusercontent.com/u/2486806?v=4"/></br>[sebastienjust](https://github.com/sebastienjust) | <img width="50" src="https://avatars.githubusercontent.com/u/28362310?v=4"/></br>[sealch](https://github.com/sealch) | <img width="50" src="https://avatars.githubusercontent.com/u/34258070?v=4"/></br>[StarFang208](https://github.com/StarFang208) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/1782292?v=4"/></br>[SubodhDahal](https://github.com/SubodhDahal) | <img width="50" src="https://avatars.githubusercontent.com/u/5912371?v=4"/></br>[TobiasDev](https://github.com/TobiasDev) | <img width="50" src="https://avatars.githubusercontent.com/u/53571657?v=4"/></br>[tmclo](https://github.com/tmclo) | <img width="50" src="https://avatars.githubusercontent.com/u/13502069?v=4"/></br>[Whaell](https://github.com/Whaell) | <img width="50" src="https://avatars.githubusercontent.com/u/29891001?v=4"/></br>[jyuvaraj03](https://github.com/jyuvaraj03) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/15380913?v=4"/></br>[kowalskidev](https://github.com/kowalskidev) | <img width="50" src="https://avatars.githubusercontent.com/u/337455?v=4"/></br>[alexchee](https://github.com/alexchee) | <img width="50" src="https://avatars.githubusercontent.com/u/5077221?v=4"/></br>[axq](https://github.com/axq) | <img width="50" src="https://avatars.githubusercontent.com/u/15636584?v=4"/></br>[balmag](https://github.com/balmag) | <img width="50" src="https://avatars.githubusercontent.com/u/8808502?v=4"/></br>[barbowza](https://github.com/barbowza) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/42007357?v=4"/></br>[eresytter](https://github.com/eresytter) | <img width="50" src="https://avatars.githubusercontent.com/u/4346449?v=4"/></br>[kik0220](https://github.com/kik0220) | <img width="50" src="https://avatars.githubusercontent.com/u/4316805?v=4"/></br>[stingray-11](https://github.com/stingray-11) | <img width="50" src="https://avatars.githubusercontent.com/u/11711053?v=4"/></br>[lscolombo](https://github.com/lscolombo) | <img width="50" src="https://avatars.githubusercontent.com/u/29355048?v=4"/></br>[majsterkovic](https://github.com/majsterkovic) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/17399340?v=4"/></br>[pf-siedler](https://github.com/pf-siedler) | <img width="50" src="https://avatars.githubusercontent.com/u/17232523?v=4"/></br>[ruuti](https://github.com/ruuti) | <img width="50" src="https://avatars.githubusercontent.com/u/23638148?v=4"/></br>[s1nceri7y](https://github.com/s1nceri7y) | <img width="50" src="https://avatars.githubusercontent.com/u/10117386?v=4"/></br>[kornava](https://github.com/kornava) | <img width="50" src="https://avatars.githubusercontent.com/u/36303913?v=4"/></br>[sensor-freak](https://github.com/sensor-freak) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/34542665?v=4"/></br>[paventyang](https://github.com/paventyang) | <img width="50" src="https://avatars.githubusercontent.com/u/7471938?v=4"/></br>[ShuiHuo](https://github.com/ShuiHuo) | <img width="50" src="https://avatars.githubusercontent.com/u/11596277?v=4"/></br>[ikunya](https://github.com/ikunya) | <img width="50" src="https://avatars.githubusercontent.com/u/59133880?v=4"/></br>[bedwardly-down](https://github.com/bedwardly-down) | <img width="50" src="https://avatars.githubusercontent.com/u/250887?v=4"/></br>[fstanis](https://github.com/fstanis) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/116026761?v=4"/></br>[sammyhori](https://github.com/sammyhori) | <img width="50" src="https://avatars.githubusercontent.com/u/47456195?v=4"/></br>[hexclover](https://github.com/hexclover) | <img width="50" src="https://avatars.githubusercontent.com/u/45535789?v=4"/></br>[2jaeyeol](https://github.com/2jaeyeol) | <img width="50" src="https://avatars.githubusercontent.com/u/25622825?v=4"/></br>[thackeraaron](https://github.com/thackeraaron) | <img width="50" src="https://avatars.githubusercontent.com/u/32984653?v=4"/></br>[AIbnuHIbban](https://github.com/AIbnuHIbban) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/8325984?v=4"/></br>[asalthobaity](https://github.com/asalthobaity) | <img width="50" src="https://avatars.githubusercontent.com/u/63901956?v=4"/></br>[abhi-bhatra](https://github.com/abhi-bhatra) | <img width="50" src="https://avatars.githubusercontent.com/u/56785486?v=4"/></br>[iamabhi222](https://github.com/iamabhi222) | <img width="50" src="https://avatars.githubusercontent.com/u/69760168?v=4"/></br>[waditos](https://github.com/waditos) | <img width="50" src="https://avatars.githubusercontent.com/u/61756360?v=4"/></br>[sandstone991](https://github.com/sandstone991) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/63443657?v=4"/></br>[Aksh-Konda](https://github.com/Aksh-Konda) | <img width="50" src="https://avatars.githubusercontent.com/u/3660978?v=4"/></br>[alanfortlink](https://github.com/alanfortlink) | <img width="50" src="https://avatars.githubusercontent.com/u/32174181?v=4"/></br>[alecmaly](https://github.com/alecmaly) | <img width="50" src="https://avatars.githubusercontent.com/u/53372753?v=4"/></br>[AverageUser2](https://github.com/AverageUser2) | <img width="50" src="https://avatars.githubusercontent.com/u/51818821?v=4"/></br>[adw2019](https://github.com/adw2019) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/4056990?v=4"/></br>[afischer211](https://github.com/afischer211) | <img width="50" src="https://avatars.githubusercontent.com/u/61735677?v=4"/></br>[bablecopherye](https://github.com/bablecopherye) | <img width="50" src="https://avatars.githubusercontent.com/u/26230870?v=4"/></br>[a13xk](https://github.com/a13xk) | <img width="50" src="https://avatars.githubusercontent.com/u/14836659?v=4"/></br>[apankratov](https://github.com/apankratov) | <img width="50" src="https://avatars.githubusercontent.com/u/7045739?v=4"/></br>[teterkin](https://github.com/teterkin) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/215668?v=4"/></br>[avanderberg](https://github.com/avanderberg) | <img width="50" src="https://avatars.githubusercontent.com/u/4408379?v=4"/></br>[lex111](https://github.com/lex111) | <img width="50" src="https://avatars.githubusercontent.com/u/60134194?v=4"/></br>[Alkindi42](https://github.com/Alkindi42) | <img width="50" src="https://avatars.githubusercontent.com/u/7129815?v=4"/></br>[Jumanjii](https://github.com/Jumanjii) | <img width="50" src="https://avatars.githubusercontent.com/u/19962243?v=4"/></br>[AlphaJack](https://github.com/AlphaJack) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/65647302?v=4"/></br>[Lord-Aman](https://github.com/Lord-Aman) | <img width="50" src="https://avatars.githubusercontent.com/u/12948692?v=4"/></br>[aminvakil](https://github.com/aminvakil) | <img width="50" src="https://avatars.githubusercontent.com/u/14096959?v=4"/></br>[richtwin567](https://github.com/richtwin567) | <img width="50" src="https://avatars.githubusercontent.com/u/487182?v=4"/></br>[andrejilderda](https://github.com/andrejilderda) | <img width="50" src="https://avatars.githubusercontent.com/u/18169566?v=4"/></br>[deining](https://github.com/deining) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/922429?v=4"/></br>[adrynov](https://github.com/adrynov) | <img width="50" src="https://avatars.githubusercontent.com/u/94937?v=4"/></br>[andrewperry](https://github.com/andrewperry) | <img width="50" src="https://avatars.githubusercontent.com/u/5417051?v=4"/></br>[tekdel](https://github.com/tekdel) | <img width="50" src="https://avatars.githubusercontent.com/u/4471821?v=4"/></br>[fobo66](https://github.com/fobo66) | <img width="50" src="https://avatars.githubusercontent.com/u/7015947?v=4"/></br>[andzs](https://github.com/andzs) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/54475686?v=4"/></br>[pandeymangg](https://github.com/pandeymangg) | <img width="50" src="https://avatars.githubusercontent.com/u/25694659?v=4"/></br>[rasklaad](https://github.com/rasklaad) | <img width="50" src="https://avatars.githubusercontent.com/u/498326?v=4"/></br>[Shaxine](https://github.com/Shaxine) | <img width="50" src="https://avatars.githubusercontent.com/u/9095073?v=4"/></br>[antonio-ramadas](https://github.com/antonio-ramadas) | <img width="50" src="https://avatars.githubusercontent.com/u/28067395?v=4"/></br>[aprvsh](https://github.com/aprvsh) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/75756768?v=4"/></br>[aynp](https://github.com/aynp) | <img width="50" src="https://avatars.githubusercontent.com/u/201215?v=4"/></br>[assimd](https://github.com/assimd) | <img width="50" src="https://avatars.githubusercontent.com/u/26827848?v=4"/></br>[Atrate](https://github.com/Atrate) | <img width="50" src="https://avatars.githubusercontent.com/u/794314?v=4"/></br>[austindoupnik](https://github.com/austindoupnik) | <img width="50" src="https://avatars.githubusercontent.com/u/110668146?v=4"/></br>[BeeverTeeth](https://github.com/BeeverTeeth) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/16528381?v=4"/></br>[be-we](https://github.com/be-we) | <img width="50" src="https://avatars.githubusercontent.com/u/1899506?v=4"/></br>[ei8fdb](https://github.com/ei8fdb) | <img width="50" src="https://avatars.githubusercontent.com/u/7034200?v=4"/></br>[bimlas](https://github.com/bimlas) | <img width="50" src="https://avatars.githubusercontent.com/u/58605547?v=4"/></br>[bishoy-magdy](https://github.com/bishoy-magdy) | <img width="50" src="https://avatars.githubusercontent.com/u/1614?v=4"/></br>[brad](https://github.com/brad) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/47641641?v=4"/></br>[brenobaptista](https://github.com/brenobaptista) | <img width="50" src="https://avatars.githubusercontent.com/u/1001769?v=4"/></br>[CandleCandle](https://github.com/CandleCandle) | <img width="50" src="https://avatars.githubusercontent.com/u/16287077?v=4"/></br>[carlbordum](https://github.com/carlbordum) | <img width="50" src="https://avatars.githubusercontent.com/u/36130773?v=4"/></br>[carlosngo](https://github.com/carlosngo) | <img width="50" src="https://avatars.githubusercontent.com/u/20382?v=4"/></br>[carlosedp](https://github.com/carlosedp) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/105843?v=4"/></br>[chaifeng](https://github.com/chaifeng) | <img width="50" src="https://avatars.githubusercontent.com/u/549349?v=4"/></br>[charles-e](https://github.com/charles-e) | <img width="50" src="https://avatars.githubusercontent.com/u/19870089?v=4"/></br>[cyy53589](https://github.com/cyy53589) | <img width="50" src="https://avatars.githubusercontent.com/u/32337926?v=4"/></br>[Chillu1](https://github.com/Chillu1) | <img width="50" src="https://avatars.githubusercontent.com/u/2348463?v=4"/></br>[Techwolf12](https://github.com/Techwolf12) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/22433652?v=4"/></br>[christopher-o-toole](https://github.com/christopher-o-toole) | <img width="50" src="https://avatars.githubusercontent.com/u/2282880?v=4"/></br>[cloudtrends](https://github.com/cloudtrends) | <img width="50" src="https://avatars.githubusercontent.com/u/17257053?v=4"/></br>[idcristi](https://github.com/idcristi) | <img width="50" src="https://avatars.githubusercontent.com/u/15956322?v=4"/></br>[damienmascre](https://github.com/damienmascre) | <img width="50" src="https://avatars.githubusercontent.com/u/1102886?v=4"/></br>[da2x](https://github.com/da2x) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/26678?v=4"/></br>[danielb2](https://github.com/danielb2) | <img width="50" src="https://avatars.githubusercontent.com/u/12847693?v=4"/></br>[danil-tolkachev](https://github.com/danil-tolkachev) | <img width="50" src="https://avatars.githubusercontent.com/u/7279100?v=4"/></br>[darshani28](https://github.com/darshani28) | <img width="50" src="https://avatars.githubusercontent.com/u/26189247?v=4"/></br>[daukadolt](https://github.com/daukadolt) | <img width="50" src="https://avatars.githubusercontent.com/u/3041566?v=4"/></br>[DavidBeale](https://github.com/DavidBeale) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/28535750?v=4"/></br>[NeverMendel](https://github.com/NeverMendel) | <img width="50" src="https://avatars.githubusercontent.com/u/81250703?v=4"/></br>[Mr-DG-Wick](https://github.com/Mr-DG-Wick) | <img width="50" src="https://avatars.githubusercontent.com/u/2138893?v=4"/></br>[DG0lden](https://github.com/DG0lden) | <img width="50" src="https://avatars.githubusercontent.com/u/54697735?v=4"/></br>[deunlee](https://github.com/deunlee) | <img width="50" src="https://avatars.githubusercontent.com/u/11378282?v=4"/></br>[diego-betto](https://github.com/diego-betto) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/215270?v=4"/></br>[erdody](https://github.com/erdody) | <img width="50" src="https://avatars.githubusercontent.com/u/36959928?v=4"/></br>[diragb](https://github.com/diragb) | <img width="50" src="https://avatars.githubusercontent.com/u/10371667?v=4"/></br>[domgoodwin](https://github.com/domgoodwin) | <img width="50" src="https://avatars.githubusercontent.com/u/72066?v=4"/></br>[b4mboo](https://github.com/b4mboo) | <img width="50" src="https://avatars.githubusercontent.com/u/5131923?v=4"/></br>[donbowman](https://github.com/donbowman) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/60024671?v=4"/></br>[DeeJayLSP](https://github.com/DeeJayLSP) | <img width="50" src="https://avatars.githubusercontent.com/u/579727?v=4"/></br>[sirnacnud](https://github.com/sirnacnud) | <img width="50" src="https://avatars.githubusercontent.com/u/47756?v=4"/></br>[dflock](https://github.com/dflock) | <img width="50" src="https://avatars.githubusercontent.com/u/7990534?v=4"/></br>[drobilica](https://github.com/drobilica) | <img width="50" src="https://avatars.githubusercontent.com/u/21699905?v=4"/></br>[educbraga](https://github.com/educbraga) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/92958867?v=4"/></br>[eduebernal](https://github.com/eduebernal) | <img width="50" src="https://avatars.githubusercontent.com/u/67867099?v=4"/></br>[eduardokimmel](https://github.com/eduardokimmel) | <img width="50" src="https://avatars.githubusercontent.com/u/17415256?v=4"/></br>[ei-ke](https://github.com/ei-ke) | <img width="50" src="https://avatars.githubusercontent.com/u/1962738?v=4"/></br>[einverne](https://github.com/einverne) | <img width="50" src="https://avatars.githubusercontent.com/u/15069703?v=4"/></br>[etho201](https://github.com/etho201) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/16492558?v=4"/></br>[eodeluga](https://github.com/eodeluga) | <img width="50" src="https://avatars.githubusercontent.com/u/16875937?v=4"/></br>[fathyar](https://github.com/fathyar) | <img width="50" src="https://avatars.githubusercontent.com/u/73366988?v=4"/></br>[Fejby](https://github.com/Fejby) | <img width="50" src="https://avatars.githubusercontent.com/u/126302554?v=4"/></br>[fkinoshita](https://github.com/fkinoshita) | <img width="50" src="https://avatars.githubusercontent.com/u/3057302?v=4"/></br>[fer22f](https://github.com/fer22f) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/43272148?v=4"/></br>[fpindado](https://github.com/fpindado) | <img width="50" src="https://avatars.githubusercontent.com/u/1714374?v=4"/></br>[FleischKarussel](https://github.com/FleischKarussel) | <img width="50" src="https://avatars.githubusercontent.com/u/23738961?v=4"/></br>[easyteacher](https://github.com/easyteacher) | <img width="50" src="https://avatars.githubusercontent.com/u/110087?v=4"/></br>[halkeye](https://github.com/halkeye) | <img width="50" src="https://avatars.githubusercontent.com/u/19814827?v=4"/></br>[gmaubach](https://github.com/gmaubach) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/6190183?v=4"/></br>[gmag11](https://github.com/gmag11) | <img width="50" src="https://avatars.githubusercontent.com/u/6209647?v=4"/></br>[Jackymancs4](https://github.com/Jackymancs4) | <img width="50" src="https://avatars.githubusercontent.com/u/1501599?v=4"/></br>[gitstart](https://github.com/gitstart) | <img width="50" src="https://avatars.githubusercontent.com/u/297578?v=4"/></br>[Glandos](https://github.com/Glandos) | <img width="50" src="https://avatars.githubusercontent.com/u/24235344?v=4"/></br>[ggteixeira](https://github.com/ggteixeira) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/2257024?v=4"/></br>[gusbemacbe](https://github.com/gusbemacbe) | <img width="50" src="https://avatars.githubusercontent.com/u/64917442?v=4"/></br>[HOLLYwyh](https://github.com/HOLLYwyh) | <img width="50" src="https://avatars.githubusercontent.com/u/18524580?v=4"/></br>[Fvbor](https://github.com/Fvbor) | <img width="50" src="https://avatars.githubusercontent.com/u/16725441?v=4"/></br>[hamishmb](https://github.com/hamishmb) | <img width="50" src="https://avatars.githubusercontent.com/u/22606250?v=4"/></br>[bennetthanna](https://github.com/bennetthanna) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/78360007?v=4"/></br>[graueneko](https://github.com/graueneko) | <img width="50" src="https://avatars.githubusercontent.com/u/67231570?v=4"/></br>[harshitkathuria](https://github.com/harshitkathuria) | <img width="50" src="https://avatars.githubusercontent.com/u/1716229?v=4"/></br>[Vistaus](https://github.com/Vistaus) | <img width="50" src="https://avatars.githubusercontent.com/u/47787284?v=4"/></br>[gtlsgamr](https://github.com/gtlsgamr) | <img width="50" src="https://avatars.githubusercontent.com/u/32321396?v=4"/></br>[horaceyoung](https://github.com/horaceyoung) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/6509881?v=4"/></br>[ianjs](https://github.com/ianjs) | <img width="50" src="https://avatars.githubusercontent.com/u/19862172?v=4"/></br>[iahmedbacha](https://github.com/iahmedbacha) | <img width="50" src="https://avatars.githubusercontent.com/u/76095?v=4"/></br>[caseycs](https://github.com/caseycs) | <img width="50" src="https://avatars.githubusercontent.com/u/1533624?v=4"/></br>[IrvinDominin](https://github.com/IrvinDominin) | <img width="50" src="https://avatars.githubusercontent.com/u/33200024?v=4"/></br>[ishammahajan](https://github.com/ishammahajan) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/6916297?v=4"/></br>[ffadilaputra](https://github.com/ffadilaputra) | <img width="50" src="https://avatars.githubusercontent.com/u/64505041?v=4"/></br>[Iwantgreencard](https://github.com/Iwantgreencard) | <img width="50" src="https://avatars.githubusercontent.com/u/16137232?v=4"/></br>[j0hn-mc-clane](https://github.com/j0hn-mc-clane) | <img width="50" src="https://avatars.githubusercontent.com/u/19985741?v=4"/></br>[JRaiden16](https://github.com/JRaiden16) | <img width="50" src="https://avatars.githubusercontent.com/u/11466782?v=4"/></br>[jacobherrington](https://github.com/jacobherrington) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/9365179?v=4"/></br>[jamesadjinwa](https://github.com/jamesadjinwa) | <img width="50" src="https://avatars.githubusercontent.com/u/20801821?v=4"/></br>[jrwrigh](https://github.com/jrwrigh) | <img width="50" src="https://avatars.githubusercontent.com/u/7652978?v=4"/></br>[analogist](https://github.com/analogist) | <img width="50" src="https://avatars.githubusercontent.com/u/4995433?v=4"/></br>[jaredcrowe](https://github.com/jaredcrowe) | <img width="50" src="https://avatars.githubusercontent.com/u/936006?v=4"/></br>[jasonwilliams](https://github.com/jasonwilliams) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/4087105?v=4"/></br>[volatilevar](https://github.com/volatilevar) | <img width="50" src="https://avatars.githubusercontent.com/u/47724360?v=4"/></br>[innkuika](https://github.com/innkuika) | <img width="50" src="https://avatars.githubusercontent.com/u/163555?v=4"/></br>[JoelRSimpson](https://github.com/JoelRSimpson) | <img width="50" src="https://avatars.githubusercontent.com/u/6965062?v=4"/></br>[joeltaylor](https://github.com/joeltaylor) | <img width="50" src="https://avatars.githubusercontent.com/u/1133852?v=4"/></br>[thejohnfreeman](https://github.com/thejohnfreeman) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/242107?v=4"/></br>[exic](https://github.com/exic) | <img width="50" src="https://avatars.githubusercontent.com/u/13716151?v=4"/></br>[JonathanPlasse](https://github.com/JonathanPlasse) | <img width="50" src="https://avatars.githubusercontent.com/u/2520458?v=4"/></br>[joschaschmiedt](https://github.com/joschaschmiedt) | <img width="50" src="https://avatars.githubusercontent.com/u/1248504?v=4"/></br>[joesfer](https://github.com/joesfer) | <img width="50" src="https://avatars.githubusercontent.com/u/1586795?v=4"/></br>[joserebelo](https://github.com/joserebelo) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/6048003?v=4"/></br>[joybinchen](https://github.com/joybinchen) | <img width="50" src="https://avatars.githubusercontent.com/u/31921499?v=4"/></br>[Juvecu](https://github.com/Juvecu) | <img width="50" src="https://avatars.githubusercontent.com/u/20700283?v=4"/></br>[KaneGreen](https://github.com/KaneGreen) | <img width="50" src="https://avatars.githubusercontent.com/u/37601331?v=4"/></br>[kaustubhsh](https://github.com/kaustubhsh) | <img width="50" src="https://avatars.githubusercontent.com/u/1560189?v=4"/></br>[y-usuzumi](https://github.com/y-usuzumi) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/56685204?v=4"/></br>[kevinshu1995](https://github.com/kevinshu1995) | <img width="50" src="https://avatars.githubusercontent.com/u/98966350?v=4"/></br>[Kevin-vdberg](https://github.com/Kevin-vdberg) | <img width="50" src="https://avatars.githubusercontent.com/u/11942650?v=4"/></br>[kkoyung](https://github.com/kkoyung) | <img width="50" src="https://avatars.githubusercontent.com/u/1660460?v=4"/></br>[xuhcc](https://github.com/xuhcc) | <img width="50" src="https://avatars.githubusercontent.com/u/16933735?v=4"/></br>[kirtanprht](https://github.com/kirtanprht) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/37491732?v=4"/></br>[k0ur0x](https://github.com/k0ur0x) | <img width="50" src="https://avatars.githubusercontent.com/u/7824233?v=4"/></br>[kklas](https://github.com/kklas) | <img width="50" src="https://avatars.githubusercontent.com/u/8622992?v=4"/></br>[xmlangel](https://github.com/xmlangel) | <img width="50" src="https://avatars.githubusercontent.com/u/465678?v=4"/></br>[Letty](https://github.com/Letty) | <img width="50" src="https://avatars.githubusercontent.com/u/1055100?v=4"/></br>[troilus](https://github.com/troilus) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/47911535?v=4"/></br>[LightAPIs](https://github.com/LightAPIs) | <img width="50" src="https://avatars.githubusercontent.com/u/35413451?v=4"/></br>[Longhao-Chen](https://github.com/Longhao-Chen) | <img width="50" src="https://avatars.githubusercontent.com/u/50335724?v=4"/></br>[diogocaveiro](https://github.com/diogocaveiro) | <img width="50" src="https://avatars.githubusercontent.com/u/2599210?v=4"/></br>[lboullo0](https://github.com/lboullo0) | <img width="50" src="https://avatars.githubusercontent.com/u/1562062?v=4"/></br>[luisperezmarin](https://github.com/luisperezmarin) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/18382454?v=4"/></br>[MHolkamp](https://github.com/MHolkamp) | <img width="50" src="https://avatars.githubusercontent.com/u/15436007?v=4"/></br>[marc-bouvier](https://github.com/marc-bouvier) | <img width="50" src="https://avatars.githubusercontent.com/u/5699725?v=4"/></br>[mvonmaltitz](https://github.com/mvonmaltitz) | <img width="50" src="https://avatars.githubusercontent.com/u/11036464?v=4"/></br>[mlkood](https://github.com/mlkood) | <img width="50" src="https://avatars.githubusercontent.com/u/2480960?v=4"/></br>[plextoriano](https://github.com/plextoriano) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/5788516?v=4"/></br>[Marmo](https://github.com/Marmo) | <img width="50" src="https://avatars.githubusercontent.com/u/29300939?v=4"/></br>[mcejp](https://github.com/mcejp) | <img width="50" src="https://avatars.githubusercontent.com/u/640949?v=4"/></br>[freaktechnik](https://github.com/freaktechnik) | <img width="50" src="https://avatars.githubusercontent.com/u/79802125?v=4"/></br>[martinkorelic](https://github.com/martinkorelic) | <img width="50" src="https://avatars.githubusercontent.com/u/287105?v=4"/></br>[Petemir](https://github.com/Petemir) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/5218859?v=4"/></br>[matsair](https://github.com/matsair) | <img width="50" src="https://avatars.githubusercontent.com/u/7098804?v=4"/></br>[MattDemers](https://github.com/MattDemers) | <img width="50" src="https://avatars.githubusercontent.com/u/12831489?v=4"/></br>[mgroth0](https://github.com/mgroth0) | <img width="50" src="https://avatars.githubusercontent.com/u/21796?v=4"/></br>[silentmatt](https://github.com/silentmatt) | <img width="50" src="https://avatars.githubusercontent.com/u/76700192?v=4"/></br>[maxs-test](https://github.com/maxs-test) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/59669349?v=4"/></br>[MichBoi](https://github.com/MichBoi) | <img width="50" src="https://avatars.githubusercontent.com/u/3941344?v=4"/></br>[MikkCZ](https://github.com/MikkCZ) | <img width="50" src="https://avatars.githubusercontent.com/u/51273874?v=4"/></br>[MichipX](https://github.com/MichipX) | <img width="50" src="https://avatars.githubusercontent.com/u/59350?v=4"/></br>[Elleo](https://github.com/Elleo) | <img width="50" src="https://avatars.githubusercontent.com/u/14942380?v=4"/></br>[phucbm](https://github.com/phucbm) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/72992390?v=4"/></br>[miucci](https://github.com/miucci) | <img width="50" src="https://avatars.githubusercontent.com/u/40818895?v=4"/></br>[MovingEarth](https://github.com/MovingEarth) | <img width="50" src="https://avatars.githubusercontent.com/u/53177864?v=4"/></br>[MrTraduttore](https://github.com/MrTraduttore) | <img width="50" src="https://avatars.githubusercontent.com/u/48156230?v=4"/></br>[sanjarcode](https://github.com/sanjarcode) | <img width="50" src="https://avatars.githubusercontent.com/u/43955099?v=4"/></br>[Mustafa-ALD](https://github.com/Mustafa-ALD) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/1592048?v=4"/></br>[LeMyst](https://github.com/LeMyst) | <img width="50" src="https://avatars.githubusercontent.com/u/66901039?v=4"/></br>[matmolni](https://github.com/matmolni) | <img width="50" src="https://avatars.githubusercontent.com/u/9076687?v=4"/></br>[NJannasch](https://github.com/NJannasch) | <img width="50" src="https://avatars.githubusercontent.com/u/77619?v=4"/></br>[kna](https://github.com/kna) | <img width="50" src="https://avatars.githubusercontent.com/u/8016073?v=4"/></br>[zomglings](https://github.com/zomglings) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/63354547?v=4"/></br>[nicholas-10](https://github.com/nicholas-10) | <img width="50" src="https://avatars.githubusercontent.com/u/11778560?v=4"/></br>[nickhobbs94](https://github.com/nickhobbs94) | <img width="50" src="https://avatars.githubusercontent.com/u/10386884?v=4"/></br>[Frichetten](https://github.com/Frichetten) | <img width="50" src="https://avatars.githubusercontent.com/u/5541611?v=4"/></br>[nicolas-suzuki](https://github.com/nicolas-suzuki) | <img width="50" src="https://avatars.githubusercontent.com/u/15157120?v=4"/></br>[Nicryc](https://github.com/Nicryc) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/40800932?v=4"/></br>[nik-gautam](https://github.com/nik-gautam) | <img width="50" src="https://avatars.githubusercontent.com/u/48302704?v=4"/></br>[noah-nash](https://github.com/noah-nash) | <img width="50" src="https://avatars.githubusercontent.com/u/90026187?v=4"/></br>[OmGole](https://github.com/OmGole) | <img width="50" src="https://avatars.githubusercontent.com/u/12369770?v=4"/></br>[Ouvill](https://github.com/Ouvill) | <img width="50" src="https://avatars.githubusercontent.com/u/43815417?v=4"/></br>[shorty2380](https://github.com/shorty2380) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/15014287?v=4"/></br>[dist3r](https://github.com/dist3r) | <img width="50" src="https://avatars.githubusercontent.com/u/19418601?v=4"/></br>[rakleed](https://github.com/rakleed) | <img width="50" src="https://avatars.githubusercontent.com/u/7881932?v=4"/></br>[idle-code](https://github.com/idle-code) | <img width="50" src="https://avatars.githubusercontent.com/u/13076552?v=4"/></br>[Oaklight](https://github.com/Oaklight) | <img width="50" src="https://avatars.githubusercontent.com/u/53913724?v=4"/></br>[Perkolator](https://github.com/Perkolator) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/24394304?v=4"/></br>[petzi53](https://github.com/petzi53) | <img width="50" src="https://avatars.githubusercontent.com/u/29787?v=4"/></br>[phitsc](https://github.com/phitsc) | <img width="50" src="https://avatars.githubusercontent.com/u/56399446?v=4"/></br>[KowalskiPiotr98](https://github.com/KowalskiPiotr98) | <img width="50" src="https://avatars.githubusercontent.com/u/64375061?v=4"/></br>[Polaris66](https://github.com/Polaris66) | <img width="50" src="https://avatars.githubusercontent.com/u/6306608?v=4"/></br>[Diadlo](https://github.com/Diadlo) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/42793024?v=4"/></br>[pranavmodx](https://github.com/pranavmodx) | <img width="50" src="https://avatars.githubusercontent.com/u/50834839?v=4"/></br>[R3dError](https://github.com/R3dError) | <img width="50" src="https://avatars.githubusercontent.com/u/42652941?v=4"/></br>[rajprakash00](https://github.com/rajprakash00) | <img width="50" src="https://avatars.githubusercontent.com/u/32304956?v=4"/></br>[rahil1304](https://github.com/rahil1304) | <img width="50" src="https://avatars.githubusercontent.com/u/8257474?v=4"/></br>[rasulkireev](https://github.com/rasulkireev) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/17312341?v=4"/></br>[reinhart1010](https://github.com/reinhart1010) | <img width="50" src="https://avatars.githubusercontent.com/u/60484714?v=4"/></br>[Retew](https://github.com/Retew) | <img width="50" src="https://avatars.githubusercontent.com/u/10456131?v=4"/></br>[ambrt](https://github.com/ambrt) | <img width="50" src="https://avatars.githubusercontent.com/u/791713?v=4"/></br>[rio-codes](https://github.com/rio-codes) | <img width="50" src="https://avatars.githubusercontent.com/u/568673?v=4"/></br>[robmoffat](https://github.com/robmoffat) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/15892014?v=4"/></br>[Derkades](https://github.com/Derkades) | <img width="50" src="https://avatars.githubusercontent.com/u/49439044?v=4"/></br>[fourstepper](https://github.com/fourstepper) | <img width="50" src="https://avatars.githubusercontent.com/u/54365?v=4"/></br>[rodgco](https://github.com/rodgco) | <img width="50" src="https://avatars.githubusercontent.com/u/96014?v=4"/></br>[Ronnie76er](https://github.com/Ronnie76er) | <img width="50" src="https://avatars.githubusercontent.com/u/79168?v=4"/></br>[roryokane](https://github.com/roryokane) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/744655?v=4"/></br>[ruzaq](https://github.com/ruzaq) | <img width="50" src="https://avatars.githubusercontent.com/u/20490839?v=4"/></br>[szokesandor](https://github.com/szokesandor) | <img width="50" src="https://avatars.githubusercontent.com/u/10775512?v=4"/></br>[forsh4w](https://github.com/forsh4w) | <img width="50" src="https://avatars.githubusercontent.com/u/19328605?v=4"/></br>[SamuelBlickle](https://github.com/SamuelBlickle) | <img width="50" src="https://avatars.githubusercontent.com/u/80849457?v=4"/></br>[livingc0l0ur](https://github.com/livingc0l0ur) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/1776?v=4"/></br>[bronson](https://github.com/bronson) | <img width="50" src="https://avatars.githubusercontent.com/u/426959?v=4"/></br>[sebthom](https://github.com/sebthom) | <img width="50" src="https://avatars.githubusercontent.com/u/24606935?v=4"/></br>[semperor](https://github.com/semperor) | <img width="50" src="https://avatars.githubusercontent.com/u/18042424?v=4"/></br>[SeptemberHX](https://github.com/SeptemberHX) | <img width="50" src="https://avatars.githubusercontent.com/u/607938?v=4"/></br>[shawnaxsom](https://github.com/shawnaxsom) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/2786333?v=4"/></br>[hurutoriya](https://github.com/hurutoriya) | <img width="50" src="https://avatars.githubusercontent.com/u/60000624?v=4"/></br>[siddharthmagadum16](https://github.com/siddharthmagadum16) | <img width="50" src="https://avatars.githubusercontent.com/u/30827929?v=4"/></br>[5idereal](https://github.com/5idereal) | <img width="50" src="https://avatars.githubusercontent.com/u/43588516?v=4"/></br>[stephan-dev](https://github.com/stephan-dev) | <img width="50" src="https://avatars.githubusercontent.com/u/9937486?v=4"/></br>[SFoskitt](https://github.com/SFoskitt) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/24674127?v=4"/></br>[stephanoskomnenos](https://github.com/stephanoskomnenos) | <img width="50" src="https://avatars.githubusercontent.com/u/94064167?v=4"/></br>[WebSnke](https://github.com/WebSnke) | <img width="50" src="https://avatars.githubusercontent.com/u/505011?v=4"/></br>[kcrt](https://github.com/kcrt) | <img width="50" src="https://avatars.githubusercontent.com/u/538584?v=4"/></br>[xissy](https://github.com/xissy) | <img width="50" src="https://avatars.githubusercontent.com/u/164962?v=4"/></br>[tams](https://github.com/tams) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/466122?v=4"/></br>[Tekki](https://github.com/Tekki) | <img width="50" src="https://avatars.githubusercontent.com/u/13370356?v=4"/></br>[Teko-uy](https://github.com/Teko-uy) | <img width="50" src="https://avatars.githubusercontent.com/u/2112477?v=4"/></br>[ThatcherC](https://github.com/ThatcherC) | <img width="50" src="https://avatars.githubusercontent.com/u/21969426?v=4"/></br>[TheoDutch](https://github.com/TheoDutch) | <img width="50" src="https://avatars.githubusercontent.com/u/19636565?v=4"/></br>[Theta-Dev](https://github.com/Theta-Dev) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/12467511?v=4"/></br>[ThibaultJanBeyer](https://github.com/ThibaultJanBeyer) | <img width="50" src="https://avatars.githubusercontent.com/u/8731922?v=4"/></br>[tbroadley](https://github.com/tbroadley) | <img width="50" src="https://avatars.githubusercontent.com/u/114300?v=4"/></br>[Kriechi](https://github.com/Kriechi) | <img width="50" src="https://avatars.githubusercontent.com/u/3457339?v=4"/></br>[tkilaker](https://github.com/tkilaker) | <img width="50" src="https://avatars.githubusercontent.com/u/802148?v=4"/></br>[Archelyst](https://github.com/Archelyst) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/4201229?v=4"/></br>[tcyrus](https://github.com/tcyrus) | <img width="50" src="https://avatars.githubusercontent.com/u/834914?v=4"/></br>[tobias-grasse](https://github.com/tobias-grasse) | <img width="50" src="https://avatars.githubusercontent.com/u/6691273?v=4"/></br>[strobeltobias](https://github.com/strobeltobias) | <img width="50" src="https://avatars.githubusercontent.com/u/1677578?v=4"/></br>[kostegit](https://github.com/kostegit) | <img width="50" src="https://avatars.githubusercontent.com/u/9092682?v=4"/></br>[TomBursch](https://github.com/TomBursch) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/70296?v=4"/></br>[tbergeron](https://github.com/tbergeron) | <img width="50" src="https://avatars.githubusercontent.com/u/1117052?v=4"/></br>[tbjers](https://github.com/tbjers) | <img width="50" src="https://avatars.githubusercontent.com/u/238296?v=4"/></br>[trentlarson](https://github.com/trentlarson) | <img width="50" src="https://avatars.githubusercontent.com/u/10265443?v=4"/></br>[Ullas-Aithal](https://github.com/Ullas-Aithal) | <img width="50" src="https://avatars.githubusercontent.com/u/6104498?v=4"/></br>[vdeville](https://github.com/vdeville) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/2830093?v=4"/></br>[vassudanagunta](https://github.com/vassudanagunta) | <img width="50" src="https://avatars.githubusercontent.com/u/54314949?v=4"/></br>[vijayjoshi16](https://github.com/vijayjoshi16) | <img width="50" src="https://avatars.githubusercontent.com/u/13400593?v=4"/></br>[vjocw](https://github.com/vjocw) | <img width="50" src="https://avatars.githubusercontent.com/u/59287619?v=4"/></br>[max-keviv](https://github.com/max-keviv) | <img width="50" src="https://avatars.githubusercontent.com/u/598576?v=4"/></br>[vandreykiv](https://github.com/vandreykiv) |
|
||||
| <img width="50" src="https://avatars.githubusercontent.com/u/4094814?v=4"/></br>[warddr](https://github.com/warddr) | <img width="50" src="https://avatars.githubusercontent.com/u/22241609?v=4"/></br>[westfalenyeti](https://github.com/westfalenyeti) | <img width="50" src="https://avatars.githubusercontent.com/u/26511487?v=4"/></br>[WisdomCode](https://github.com/WisdomCode) | <img width="50" src="https://avatars.githubusercontent.com/u/48159366?v=4"/></br>[X3NOOO](https://github.com/X3NOOO) | <img width="50" src="https://avatars.githubusercontent.com/u/1921957?v=4"/></br>[xsak](https://github.com/xsak) |
|
||||
| | | | | |
|
||||
<!-- CONTRIBUTORS-TABLE-AUTO-GENERATED -->
|
||||
|
||||
@@ -11,8 +11,9 @@ module.exports = {
|
||||
//
|
||||
// '**/*.ts?(x)': () => 'npm run tsc',
|
||||
'*.{js,jsx,ts,tsx}': [
|
||||
'yarn run linter-precommit',
|
||||
'yarn run checkIgnoredFiles',
|
||||
'yarn run checkLibPaths',
|
||||
'node packages/tools/checkIgnoredFiles.js',
|
||||
'yarn run packageJsonLint',
|
||||
'yarn run linter-precommit',
|
||||
],
|
||||
};
|
||||
|
||||
+18
-14
@@ -24,6 +24,7 @@
|
||||
"buildWebsiteTranslations": "node packages/tools/website/buildTranslations.js",
|
||||
"buildWebsite": "node ./packages/tools/website/build.js && yarn run buildPluginDoc && yarn run buildSettingJsonSchema",
|
||||
"checkLibPaths": "node ./packages/tools/checkLibPaths.js",
|
||||
"checkIgnoredFiles": "node ./packages/tools/checkIgnoredFiles.js",
|
||||
"circularDependencyCheck": "madge --warning --circular --extensions js ./",
|
||||
"clean": "npm run clean --workspaces --if-present && node packages/tools/clean && yarn cache clean",
|
||||
"dependencyTree": "madge",
|
||||
@@ -33,7 +34,7 @@
|
||||
"linter-precommit": "eslint --resolve-plugins-relative-to . --fix --ext .js --ext .jsx --ext .ts --ext .tsx",
|
||||
"linter": "eslint --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
|
||||
"linter-interactive": "eslint-interactive --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
|
||||
"packageJsonLint": "npmPkgJsonLint --configFile .npmpackagejsonlintrc.json --quiet .",
|
||||
"packageJsonLint": "node ./packages/tools/packageJsonLint.js",
|
||||
"postinstall": "gulp build",
|
||||
"publishAll": "git pull && yarn run buildParallel && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll",
|
||||
"releaseAndroid": "PATH=\"/usr/local/opt/openjdk@11/bin:$PATH\" node packages/tools/release-android.js",
|
||||
@@ -60,45 +61,48 @@
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged && yarn run packageJsonLint"
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@joplin/utils": "~2.11",
|
||||
"@joplin/utils": "~2.12",
|
||||
"@seiyab/eslint-plugin-react-hooks": "4.5.1-beta.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.59.0",
|
||||
"@typescript-eslint/parser": "5.59.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.60.0",
|
||||
"@typescript-eslint/parser": "5.60.0",
|
||||
"cspell": "5.21.2",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-interactive": "10.7.0",
|
||||
"eslint": "8.43.0",
|
||||
"eslint-interactive": "10.8.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-plugin-jest": "27.2.1",
|
||||
"eslint-plugin-jest": "27.2.2",
|
||||
"eslint-plugin-promise": "6.1.1",
|
||||
"eslint-plugin-react": "7.32.2",
|
||||
"execa": "5.1.1",
|
||||
"fs-extra": "11.1.1",
|
||||
"glob": "10.2.7",
|
||||
"glob": "10.3.3",
|
||||
"gulp": "4.0.2",
|
||||
"husky": "3.1.0",
|
||||
"lerna": "3.22.1",
|
||||
"lint-staged": "13.2.2",
|
||||
"lint-staged": "13.2.3",
|
||||
"madge": "6.1.0",
|
||||
"npm-package-json-lint": "6.4.0",
|
||||
"typescript": "5.0.2"
|
||||
"typescript": "5.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/fs-extra": "11.0.1",
|
||||
"eslint-plugin-github": "4.8.0",
|
||||
"http-server": "14.1.1",
|
||||
"node-gyp": "9.3.1",
|
||||
"node-gyp": "9.4.0",
|
||||
"nodemon": "2.0.22"
|
||||
},
|
||||
"packageManager": "yarn@3.5.0",
|
||||
"packageManager": "yarn@3.6.0",
|
||||
"resolutions": {
|
||||
"react-native-camera@4.2.1": "patch:react-native-camera@npm%3A4.2.1#./.yarn/patches/react-native-camera-npm-4.2.1-24b2600a7e.patch",
|
||||
"react-native-vosk@0.1.12": "patch:react-native-vosk@npm%3A0.1.12#./.yarn/patches/react-native-vosk-npm-0.1.12-76b1caaae8.patch",
|
||||
"eslint@8.39.0": "patch:eslint@npm%3A8.39.0#./.yarn/patches/eslint-npm-8.39.0-d92bace04d.patch",
|
||||
"eslint@8.43.0": "patch:eslint@npm%3A8.39.0#./.yarn/patches/eslint-npm-8.39.0-d92bace04d.patch",
|
||||
"eslint@^8.13.0": "patch:eslint@npm%3A8.39.0#./.yarn/patches/eslint-npm-8.39.0-d92bace04d.patch",
|
||||
"app-builder-lib@24.4.0": "patch:app-builder-lib@npm%3A24.4.0#./.yarn/patches/app-builder-lib-npm-24.4.0-05322ff057.patch"
|
||||
"app-builder-lib@24.4.0": "patch:app-builder-lib@npm%3A24.4.0#./.yarn/patches/app-builder-lib-npm-24.4.0-05322ff057.patch",
|
||||
"react-native@0.71.10": "patch:react-native@npm%3A0.71.10#./.yarn/patches/react-native-animation-fix/react-native-npm-0.71.10-f9c32562d8.patch",
|
||||
"react-native-reanimated@3.3.0": "patch:react-native-reanimated@npm%3A3.3.0#./.yarn/patches/react-native-animation-fix/react-native-reanimated-npm-3.3.0-fb4272741c.patch"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class LinkSelector {
|
||||
link: matches[n][0],
|
||||
noteX: matches[n].index,
|
||||
noteY: i,
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const Logger = require('@joplin/lib/Logger').default;
|
||||
const Logger = require('@joplin/utils/Logger').default;
|
||||
const { findAvailablePort } = require('@joplin/lib/net-utils');
|
||||
|
||||
const http = require('http');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const Logger = require('@joplin/lib/Logger').default;
|
||||
const Logger = require('@joplin/utils/Logger').default;
|
||||
const Folder = require('@joplin/lib/models/Folder').default;
|
||||
const BaseItem = require('@joplin/lib/models/BaseItem').default;
|
||||
const Tag = require('@joplin/lib/models/Tag').default;
|
||||
@@ -482,7 +482,7 @@ class AppGui {
|
||||
if (this.linkSelector_.link) {
|
||||
this.term_.moveTo(
|
||||
this.linkSelector_.noteX + cursorOffsetX,
|
||||
this.linkSelector_.noteY + cursorOffsetY
|
||||
this.linkSelector_.noteY + cursorOffsetY,
|
||||
);
|
||||
shim.setTimeout(() => this.term_.term().inverse(this.linkSelector_.link), 50);
|
||||
}
|
||||
|
||||
@@ -452,6 +452,8 @@ class Application extends BaseApplication {
|
||||
type: 'FOLDER_SELECT',
|
||||
id: Setting.value('activeFolderId'),
|
||||
});
|
||||
|
||||
this.startRotatingLogMaintenance(Setting.value('profileDir'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const Logger = require('@joplin/lib/Logger').default;
|
||||
const Logger = require('@joplin/utils/Logger').default;
|
||||
const { dirname } = require('@joplin/lib/path-utils');
|
||||
const { DatabaseDriverNode } = require('@joplin/lib/database-driver-node.js');
|
||||
const JoplinDatabase = require('@joplin/lib/JoplinDatabase').default;
|
||||
|
||||
@@ -2,7 +2,7 @@ const yargParser = require('yargs-parser');
|
||||
const { _ } = require('@joplin/lib/locale');
|
||||
const time = require('@joplin/lib/time').default;
|
||||
const stringPadding = require('string-padding');
|
||||
const Logger = require('@joplin/lib/Logger').default;
|
||||
const Logger = require('@joplin/utils/Logger').default;
|
||||
|
||||
const cliUtils = {};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const BaseCommand = require('./base-command').default;
|
||||
const { _ } = require('@joplin/lib/locale');
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const Logger = require('@joplin/lib/Logger').default;
|
||||
const Logger = require('@joplin/utils/Logger').default;
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
@@ -173,7 +173,7 @@ class Command extends BaseCommand {
|
||||
reg.db(),
|
||||
sync.lockHandler(),
|
||||
appTypeToLockType(Setting.value('appType')),
|
||||
Setting.value('clientId')
|
||||
Setting.value('clientId'),
|
||||
);
|
||||
|
||||
migrationHandler.setLogger(cliUtils.stdoutLogger(this.stdout.bind(this)));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const time = require('@joplin/lib/time').default;
|
||||
const Logger = require('@joplin/lib/Logger').default;
|
||||
const Logger = require('@joplin/utils/Logger').default;
|
||||
const Resource = require('@joplin/lib/models/Resource').default;
|
||||
const { dirname } = require('@joplin/lib/path-utils');
|
||||
const FsDriverNode = require('@joplin/lib/fs-driver-node').default;
|
||||
@@ -39,7 +39,7 @@ async function createClients() {
|
||||
// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
|
||||
execCommand(client, 'config sync.target 2').then(() => {
|
||||
return execCommand(client, `config sync.2.path ${syncDir}`);
|
||||
})
|
||||
}),
|
||||
);
|
||||
output.push(client);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ const NoteTag = require('@joplin/lib/models/NoteTag').default;
|
||||
const MasterKey = require('@joplin/lib/models/MasterKey').default;
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const Revision = require('@joplin/lib/models/Revision').default;
|
||||
const Logger = require('@joplin/lib/Logger').default;
|
||||
const Logger = require('@joplin/utils/Logger').default;
|
||||
const FsDriverNode = require('@joplin/lib/fs-driver-node').default;
|
||||
const sharp = require('sharp');
|
||||
const { shimInit } = require('@joplin/lib/shim-init-node.js');
|
||||
@@ -30,6 +30,7 @@ const { FileApiDriverLocal } = require('@joplin/lib/file-api-driver-local');
|
||||
const EncryptionService = require('@joplin/lib/services/e2ee/EncryptionService').default;
|
||||
const envFromArgs = require('@joplin/lib/envFromArgs');
|
||||
const nodeSqlite = require('sqlite3');
|
||||
const initLib = require('@joplin/lib/initLib').default;
|
||||
|
||||
const env = envFromArgs(process.argv);
|
||||
|
||||
@@ -67,6 +68,10 @@ function appVersion() {
|
||||
|
||||
shimInit({ sharp, keytar, appVersion, nodeSqlite });
|
||||
|
||||
const logger = new Logger();
|
||||
Logger.initializeGlobalLogger(logger);
|
||||
initLib(logger);
|
||||
|
||||
const application = app();
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"proper-lockfile": "4.1.2",
|
||||
"read-chunk": "2.1.0",
|
||||
"server-destroy": "1.0.1",
|
||||
"sharp": "0.32.1",
|
||||
"sharp": "0.32.4",
|
||||
"sprintf-js": "1.1.2",
|
||||
"sqlite3": "5.1.6",
|
||||
"string-padding": "1.0.2",
|
||||
@@ -66,18 +66,18 @@
|
||||
"terminal-kit": "3.0.0",
|
||||
"tkwidgets": "0.5.27",
|
||||
"url-parse": "1.5.10",
|
||||
"word-wrap": "1.2.3",
|
||||
"word-wrap": "1.2.5",
|
||||
"yargs-parser": "21.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@joplin/tools": "~2.12",
|
||||
"@types/fs-extra": "11.0.1",
|
||||
"@types/jest": "29.5.1",
|
||||
"@types/node": "18.15.13",
|
||||
"@types/jest": "29.5.3",
|
||||
"@types/node": "18.16.18",
|
||||
"@types/proper-lockfile": "^4.1.2",
|
||||
"gulp": "4.0.2",
|
||||
"jest": "29.5.0",
|
||||
"temp": "0.9.4",
|
||||
"typescript": "5.0.2"
|
||||
"typescript": "5.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
const MarkupToHtml = require('@joplin/renderer/MarkupToHtml').default;
|
||||
import MarkupToHtml, { MarkupLanguage, RenderResult } from '@joplin/renderer/MarkupToHtml';
|
||||
|
||||
describe('MarkupToHtml', () => {
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('MarkupToHtml', () => {
|
||||
const input = t[0];
|
||||
const expected = t[1];
|
||||
const actual = service.stripMarkup(Number(markup), input);
|
||||
expect(actual).toBe(expected, `Markup: ${markup}`);
|
||||
expect(actual).toBe(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,4 +40,18 @@ describe('MarkupToHtml', () => {
|
||||
expect(service.stripMarkup(1, 'one line\n two line', { collapseWhiteSpaces: true })).toBe('one line two line');
|
||||
}));
|
||||
|
||||
|
||||
test('should escape HTML in safe mode', async () => {
|
||||
const service = new MarkupToHtml({ isSafeMode: true });
|
||||
|
||||
const testString = '</pre>.<b>Test</b>';
|
||||
const expectedOutput: RenderResult = {
|
||||
html: '<pre></pre>.<b>Test</b></pre>',
|
||||
cssStrings: [],
|
||||
pluginAssets: [],
|
||||
};
|
||||
|
||||
expect(await service.render(MarkupLanguage.Html, testString, {}, {})).toMatchObject(expectedOutput);
|
||||
expect(await service.render(MarkupLanguage.Markdown, testString, {}, {})).toMatchObject(expectedOutput);
|
||||
});
|
||||
});
|
||||
@@ -248,7 +248,7 @@ describe('MdToHtml', () => {
|
||||
const result = await mdToHtml.render(input, null, { bodyOnly: true, mapsToLine: true });
|
||||
expect(result.html.trim()).toBe('<h1 id="head" class="maps-to-line" source-line="0" source-line-end="1">Head</h1>\n' +
|
||||
'<p class="maps-to-line" source-line="1" source-line-end="2">Fruits</p>\n' +
|
||||
'<ul>\n<li class="maps-to-line" source-line="2" source-line-end="3">Apple</li>\n</ul>'
|
||||
'<ul>\n<li class="maps-to-line" source-line="2" source-line-end="3">Apple</li>\n</ul>',
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export4.dtd">
|
||||
<en-export export-date="20230811T195236Z" application="Evernote" version="10.58.8">
|
||||
<note>
|
||||
<title>DSCN0716.JPG</title>
|
||||
<created>20130228T193612Z</created>
|
||||
<updated>20230616T123049Z</updated>
|
||||
<tag>Eye-Fi</tag>
|
||||
<tag>2013-02-27</tag>
|
||||
<tag>records</tag>
|
||||
<note-attributes>
|
||||
</note-attributes>
|
||||
<content>
|
||||
<![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><en-media type="image/jpeg" hash="33c6ed66e09569ed167dec1e1fd66b87"/><br/><![CDATA[DSCN0716.JPG]]></en-note> ]]>
|
||||
</content>
|
||||
</note>
|
||||
</en-export>
|
||||
@@ -0,0 +1 @@
|
||||
<p><b> A test... </b>Test</p>
|
||||
@@ -0,0 +1 @@
|
||||
**A test...** Test
|
||||
@@ -0,0 +1,10 @@
|
||||
<h1>Linebreaks</h1>
|
||||
<div><br>-style linebreaks should be replaced with two spaces followed by a
|
||||
newline.<br/>This allows the generated mark<br/>down to render to equivalent HTML
|
||||
even if the <code>markdown.plugin.softbreaks</code> setting is enabled.</div>
|
||||
<pre class="some-code" style="font-family:monospace;"><br>s shouldn't<br/>lead<br/>to trailing spaces in
|
||||
code
|
||||
however.
|
||||
</pre>
|
||||
<p>Because it isn't</p>
|
||||
<div>necessary.<br/><br/><br/><br/>...</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
# Linebreaks
|
||||
|
||||
<br>-style linebreaks should be replaced with two spaces followed by a newline.
|
||||
This allows the generated mark
|
||||
down to render to equivalent HTML even if the `markdown.plugin.softbreaks` setting is enabled.
|
||||
|
||||
```
|
||||
<br>s shouldn't
|
||||
lead
|
||||
to trailing spaces in
|
||||
code
|
||||
however.
|
||||
```
|
||||
|
||||
Because it isn't
|
||||
|
||||
necessary.
|
||||
|
||||
|
||||
|
||||
...
|
||||
@@ -0,0 +1,3 @@
|
||||
<p>Paragraphs with a single nonbreaking space should be preserved:</p>
|
||||
<p> </p>
|
||||
<p> </p>
|
||||
@@ -0,0 +1,5 @@
|
||||
Paragraphs with a single nonbreaking space should be preserved:
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
Some pictures:  
|
||||
Some pictures: 
|
||||
@@ -5,5 +5,6 @@
|
||||
| [Source](https://github.com/nim-lang/nim) | The github project |
|
||||
| [nimble](https://github.com/nim-lang/nimble) | The nim package manager |
|
||||
| [choosenim](https://github.com/dom96/choosenim) | Toolchain installer |
|
||||
| | |
|
||||
| **Community** | |
|
||||
| [Forums](https://forum.nim-lang.org) | An async discussion board |
|
||||
@@ -24,7 +24,7 @@ function newPluginService(appVersion = '1.4') {
|
||||
{
|
||||
dispatch: () => {},
|
||||
getState: () => {},
|
||||
}
|
||||
},
|
||||
);
|
||||
return service;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ function newPluginService(appVersion = '1.4') {
|
||||
{
|
||||
dispatch: () => {},
|
||||
getState: () => {},
|
||||
}
|
||||
},
|
||||
);
|
||||
return service;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export function newPluginService(appVersion = '1.4', options: PluginServiceOptio
|
||||
{
|
||||
dispatch: () => {},
|
||||
getState: options.getState ? options.getState : () => {},
|
||||
}
|
||||
},
|
||||
);
|
||||
return service;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ delete require.cache[require.resolve('./paths')];
|
||||
const NODE_ENV = process.env.NODE_ENV;
|
||||
if (!NODE_ENV) {
|
||||
throw new Error(
|
||||
'The NODE_ENV environment variable is required but was not specified.'
|
||||
'The NODE_ENV environment variable is required but was not specified.',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ dotenvFiles.forEach(dotenvFile => {
|
||||
require('dotenv-expand')(
|
||||
require('dotenv').config({
|
||||
path: dotenvFile,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -78,7 +78,7 @@ function getClientEnvironment(publicUrl) {
|
||||
// This should only be used as an escape hatch. Normally you would put
|
||||
// images into the `src` and `import` them in code to get their paths.
|
||||
PUBLIC_URL: publicUrl,
|
||||
}
|
||||
},
|
||||
);
|
||||
// Stringify all values so we can feed into Webpack DefinePlugin
|
||||
const stringified = {
|
||||
|
||||
@@ -55,8 +55,8 @@ function getAdditionalModulePaths(options = {}) {
|
||||
throw new Error(
|
||||
chalk.red.bold(
|
||||
'Your project\'s `baseUrl` can only be set to `src` or `node_modules`.' +
|
||||
' Create React App does not support other values at this time.'
|
||||
)
|
||||
' Create React App does not support other values at this time.',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ function getModules() {
|
||||
|
||||
if (hasTsConfig && hasJsConfig) {
|
||||
throw new Error(
|
||||
'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
|
||||
'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ const moduleFileExtensions = [
|
||||
// Resolve file paths in the same order as webpack
|
||||
const resolveModule = (resolveFn, filePath) => {
|
||||
const extension = moduleFileExtensions.find(extension =>
|
||||
fs.existsSync(resolveFn(`${filePath}.${extension}`))
|
||||
fs.existsSync(resolveFn(`${filePath}.${extension}`)),
|
||||
);
|
||||
|
||||
if (extension) {
|
||||
|
||||
@@ -7,14 +7,14 @@ exports.resolveModuleName = (
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost
|
||||
resolutionHost,
|
||||
) => {
|
||||
return resolveModuleName(
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost,
|
||||
typescript.resolveModuleName
|
||||
typescript.resolveModuleName,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -23,13 +23,13 @@ exports.resolveTypeReferenceDirective = (
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost
|
||||
resolutionHost,
|
||||
) => {
|
||||
return resolveModuleName(
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost,
|
||||
typescript.resolveTypeReferenceDirective
|
||||
typescript.resolveTypeReferenceDirective,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -46,15 +46,15 @@ if (process.env.HOST) {
|
||||
console.log(
|
||||
chalk.cyan(
|
||||
`Attempting to bind to HOST environment variable: ${chalk.yellow(
|
||||
chalk.bold(process.env.HOST)
|
||||
)}`
|
||||
)
|
||||
chalk.bold(process.env.HOST),
|
||||
)}`,
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
'If this was unintentional, check that you haven\'t mistakenly set it in your shell.'
|
||||
'If this was unintentional, check that you haven\'t mistakenly set it in your shell.',
|
||||
);
|
||||
console.log(
|
||||
`Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`
|
||||
`Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`,
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
@@ -102,7 +102,7 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
// Serve webpack assets generated by the compiler over a web server.
|
||||
const serverConfig = createDevServerConfig(
|
||||
proxyConfig,
|
||||
urls.lanUrlForConfig
|
||||
urls.lanUrlForConfig,
|
||||
);
|
||||
const devServer = new WebpackDevServer(compiler, serverConfig);
|
||||
// Launch WebpackDevServer.
|
||||
@@ -120,8 +120,8 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
if (process.env.NODE_PATH) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
|
||||
)
|
||||
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.',
|
||||
),
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
import { PluginMessage } from './services/plugins/PluginRunner';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import { isCallbackUrl } from '@joplin/lib/callbackUrlUtils';
|
||||
@@ -174,7 +174,7 @@ export default class ElectronAppWrapper {
|
||||
// so that it can tell us if we can really close the app or not.
|
||||
// Search for "appClose" event for closing logic on renderer side.
|
||||
event.preventDefault();
|
||||
this.win_.webContents.send('appClose');
|
||||
if (this.win_) this.win_.webContents.send('appClose');
|
||||
} else {
|
||||
// If the renderer process has responded, check if we can close or not
|
||||
if (this.rendererProcessQuitReply_.canClose) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { defaultState, State } from '@joplin/lib/reducer';
|
||||
import iterateItems from './gui/ResizableLayout/utils/iterateItems';
|
||||
import { LayoutItem } from './gui/ResizableLayout/utils/types';
|
||||
import validateLayout from './gui/ResizableLayout/utils/validateLayout';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
|
||||
const logger = Logger.create('app.reducer');
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import PlatformImplementation from './services/plugins/PlatformImplementation';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import AlarmService from '@joplin/lib/services/AlarmService';
|
||||
import AlarmServiceDriverNode from '@joplin/lib/services/AlarmServiceDriverNode';
|
||||
import Logger, { TargetType } from '@joplin/lib/Logger';
|
||||
import Logger, { TargetType } from '@joplin/utils/Logger';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import actionApi from '@joplin/lib/services/rest/actionApi.desktop';
|
||||
import BaseApplication from '@joplin/lib/BaseApplication';
|
||||
@@ -66,7 +66,7 @@ import syncDebugLog from '@joplin/lib/services/synchronizer/syncDebugLog';
|
||||
import eventManager from '@joplin/lib/eventManager';
|
||||
import path = require('path');
|
||||
import { checkPreInstalledDefaultPlugins, installDefaultPlugins, setSettingsForDefaultPlugins } from '@joplin/lib/services/plugins/defaultPlugins/defaultPluginsUtils';
|
||||
// import { runIntegrationTests } from '@joplin/lib/services/e2ee/ppkTestUtils';
|
||||
import userFetcher, { initializeUserFetcher } from '@joplin/lib/utils/userFetcher';
|
||||
|
||||
const pluginClasses = [
|
||||
require('./plugins/GotoAnything').default,
|
||||
@@ -74,7 +74,7 @@ const pluginClasses = [
|
||||
|
||||
const appDefaultState = createAppDefaultState(
|
||||
bridge().windowContentSize(),
|
||||
resourceEditWatcherDefaultState
|
||||
resourceEditWatcherDefaultState,
|
||||
);
|
||||
|
||||
class Application extends BaseApplication {
|
||||
@@ -487,6 +487,9 @@ class Application extends BaseApplication {
|
||||
shim.setInterval(() => { runAutoUpdateCheck(); }, 12 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
initializeUserFetcher();
|
||||
shim.setInterval(() => { void userFetcher(); }, 1000 * 60 * 60);
|
||||
|
||||
this.updateTray();
|
||||
|
||||
shim.setTimeout(() => {
|
||||
@@ -563,6 +566,8 @@ class Application extends BaseApplication {
|
||||
|
||||
await SpellCheckerService.instance().initialize(new SpellCheckerServiceDriverNative());
|
||||
|
||||
this.startRotatingLogMaintenance(Setting.value('profileDir'));
|
||||
|
||||
// await populateDatabase(reg.db(), {
|
||||
// clearDatabase: true,
|
||||
// folderCount: 1000,
|
||||
|
||||
@@ -96,11 +96,7 @@ export class Bridge {
|
||||
electronApp: this.electronApp(),
|
||||
|
||||
shouldShowMenu: (_event: any, params: any) => {
|
||||
// params.inputFieldType === 'none' when right-clicking the text
|
||||
// editor. This is a bit of a hack to detect it because in this
|
||||
// case we don't want to use the built-in context menu but a
|
||||
// custom one.
|
||||
return params.isEditable && params.inputFieldType !== 'none';
|
||||
return params.isEditable;
|
||||
},
|
||||
|
||||
// menu: (actions: any, props: any) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import shim from '@joplin/lib/shim';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import bridge from './services/bridge';
|
||||
import KvStore from '@joplin/lib/services/KvStore';
|
||||
@@ -23,8 +23,8 @@ function onCheckEnded() {
|
||||
isCheckingForUpdate_ = false;
|
||||
}
|
||||
|
||||
async function fetchLatestRelease() {
|
||||
const response = await shim.fetch('https://api.github.com/repos/laurent22/joplin/releases');
|
||||
async function fetchLatestReleases() {
|
||||
const response = await shim.fetch('https://objects.joplinusercontent.com/r/releases');
|
||||
|
||||
if (!response.ok) {
|
||||
const responseText = await response.text();
|
||||
@@ -76,8 +76,8 @@ export default async function checkForUpdates(inBackground: boolean, parentWindo
|
||||
logger.info(`Checking with options ${JSON.stringify(options)}`);
|
||||
|
||||
try {
|
||||
const releases = await fetchLatestRelease();
|
||||
const release = extractVersionInfo(releases, process.platform, options);
|
||||
const releases = await fetchLatestReleases();
|
||||
const release = extractVersionInfo(releases, process.platform, process.arch, shim.isPortable(), options);
|
||||
|
||||
logger.info(`Current version: ${packageInfo.version}`);
|
||||
logger.info(`Latest version: ${release.version}`);
|
||||
|
||||
@@ -2,7 +2,6 @@ import CommandService, { CommandRuntime, CommandDeclaration } from '@joplin/lib/
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { stateUtils } from '@joplin/lib/reducer';
|
||||
import { DesktopCommandContext } from '../services/commands/types';
|
||||
import { enabledCondition } from '../gui/NoteEditor/editorCommandDeclarations';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'toggleExternalEditing',
|
||||
@@ -23,7 +22,7 @@ export const runtime = (): CommandRuntime => {
|
||||
void CommandService.instance().execute('startExternalEditing', noteId);
|
||||
}
|
||||
},
|
||||
enabledCondition: enabledCondition(declaration.name),
|
||||
enabledCondition: 'oneNoteSelected && !noteIsReadOnly && (!modalDialogVisible || gotoAnythingVisible)',
|
||||
mapStateToTitle: (state: any) => {
|
||||
const noteId = stateUtils.selectedNoteId(state);
|
||||
return state.watchedNoteFiles.includes(noteId) ? _('Stop') : '';
|
||||
|
||||
@@ -71,36 +71,36 @@ class ClipperConfigScreenComponent extends React.Component {
|
||||
webClipperStatusComps.push(
|
||||
<p key="text_1" style={theme.textStyle}>
|
||||
<b>{_('The web clipper service is enabled and set to auto-start.')}</b>
|
||||
</p>
|
||||
</p>,
|
||||
);
|
||||
if (this.props.clipperServer.startState === 'started') {
|
||||
webClipperStatusComps.push(
|
||||
<p key="text_2" style={theme.textStyle}>
|
||||
{_('Status: Started on port %d', this.props.clipperServer.port)}
|
||||
</p>
|
||||
</p>,
|
||||
);
|
||||
} else {
|
||||
webClipperStatusComps.push(
|
||||
<p key="text_3" style={theme.textStyle}>
|
||||
{_('Status: %s', this.props.clipperServer.startState)}
|
||||
</p>
|
||||
</p>,
|
||||
);
|
||||
}
|
||||
webClipperStatusComps.push(
|
||||
<button key="disable_button" style={buttonStyle} onClick={this.disableClipperServer_click}>
|
||||
{_('Disable Web Clipper Service')}
|
||||
</button>
|
||||
</button>,
|
||||
);
|
||||
} else {
|
||||
webClipperStatusComps.push(
|
||||
<p key="text_4" style={theme.textStyle}>
|
||||
{_('The web clipper service is not enabled.')}
|
||||
</p>
|
||||
</p>,
|
||||
);
|
||||
webClipperStatusComps.push(
|
||||
<button key="enable_button" style={buttonStyle} onClick={this.enableClipperServer_click}>
|
||||
{_('Enable Web Clipper Service')}
|
||||
</button>
|
||||
</button>,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,17 @@ const { connect } = require('react-redux');
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
const pathUtils = require('@joplin/lib/path-utils');
|
||||
import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
|
||||
const shared = require('@joplin/lib/components/shared/config-shared.js');
|
||||
const shared = require('@joplin/lib/components/shared/config/config-shared.js');
|
||||
import ClipperConfigScreen from '../ClipperConfigScreen';
|
||||
import restart from '../../services/restart';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
import { getDefaultPluginsInstallState, updateDefaultPluginsInstallState } from '@joplin/lib/services/plugins/defaultPlugins/defaultPluginsUtils';
|
||||
import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo';
|
||||
import JoplinCloudConfigScreen from '../JoplinCloudConfigScreen';
|
||||
import ToggleAdvancedSettingsButton from './controls/ToggleAdvancedSettingsButton';
|
||||
import shouldShowMissingPasswordWarning from '@joplin/lib/components/shared/config/shouldShowMissingPasswordWarning';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import StyledLink from '../style/StyledLink';
|
||||
const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen');
|
||||
|
||||
const settingKeyToControl: any = {
|
||||
@@ -106,6 +111,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
if (screenName === 'encryption') return <EncryptionConfigScreen/>;
|
||||
if (screenName === 'server') return <ClipperConfigScreen themeId={this.props.themeId}/>;
|
||||
if (screenName === 'keymap') return <KeymapConfigScreen themeId={this.props.themeId}/>;
|
||||
if (screenName === 'joplinCloud') return <JoplinCloudConfigScreen />;
|
||||
|
||||
throw new Error(`Invalid screen name: ${screenName}`);
|
||||
}
|
||||
@@ -178,6 +184,34 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
if (section.name === 'sync') {
|
||||
const syncTargetMd = SyncTargetRegistry.idToMetadata(settings['sync.target']);
|
||||
const statusStyle = { ...theme.textStyle, marginTop: 10 };
|
||||
const warningStyle = { ...theme.textStyle, color: theme.colorWarn };
|
||||
|
||||
// Don't show the missing password warning if the user just changed the sync target (but hasn't
|
||||
// saved yet).
|
||||
const matchesSavedTarget = settings['sync.target'] === this.props.settings['sync.target'];
|
||||
if (matchesSavedTarget && shouldShowMissingPasswordWarning(settings['sync.target'], settings)) {
|
||||
const openMissingPasswordFAQ = () =>
|
||||
bridge().openExternal('https://joplinapp.org/faq#why-did-my-sync-and-encryption-passwords-disappear-after-updating-joplin');
|
||||
|
||||
const macInfoLink = (
|
||||
<StyledLink href="#"
|
||||
onClick={openMissingPasswordFAQ}
|
||||
style={theme.linkStyle}
|
||||
>
|
||||
{_('Help')}
|
||||
</StyledLink>
|
||||
);
|
||||
|
||||
// The FAQ section related to missing passwords is specific to MacOS/ARM -- only show it
|
||||
// in that case.
|
||||
const showMacInfoLink = shim.isMac() && process.arch === 'arm64';
|
||||
|
||||
settingComps.push(
|
||||
<p key='missing-password-warning' style={warningStyle}>
|
||||
{_('Warning: Missing password.')}{' '}{showMacInfoLink ? macInfoLink : null}
|
||||
</p>,
|
||||
);
|
||||
}
|
||||
|
||||
if (syncTargetMd.supportsConfigCheck) {
|
||||
const messages = shared.checkSyncConfigMessages(this);
|
||||
@@ -197,7 +231,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
onClick={this.checkSyncConfig_}
|
||||
/>
|
||||
{statusComp}
|
||||
</div>
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -206,17 +240,11 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
const advancedSettingsSectionStyle = { display: 'none' };
|
||||
|
||||
if (advancedSettingComps.length) {
|
||||
const iconName = this.state.showAdvancedSettings ? 'fa fa-angle-down' : 'fa fa-angle-right';
|
||||
// const advancedSettingsButtonStyle = { ...theme.buttonStyle, marginBottom: 10 };
|
||||
advancedSettingsButton = (
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<Button
|
||||
level={ButtonLevel.Secondary}
|
||||
onClick={() => shared.advancedSettingsButton_click(this)}
|
||||
iconName={iconName}
|
||||
title={_('Show Advanced Settings')}
|
||||
/>
|
||||
</div>
|
||||
<ToggleAdvancedSettingsButton
|
||||
onClick={() => shared.advancedSettingsButton_click(this)}
|
||||
advancedSettingsVisible={this.state.showAdvancedSettings}
|
||||
/>
|
||||
);
|
||||
advancedSettingsSectionStyle.display = this.state.showAdvancedSettings ? 'block' : 'none';
|
||||
}
|
||||
@@ -365,7 +393,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
items.push(
|
||||
<option value={e.key.toString()} key={e.key}>
|
||||
{settingOptions[e.key]}
|
||||
</option>
|
||||
</option>,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
import * as React from 'react';
|
||||
import Button, { ButtonLevel } from '../../Button/Button';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
interface Props {
|
||||
onClick: ()=> void;
|
||||
advancedSettingsVisible: boolean;
|
||||
}
|
||||
|
||||
const ToggleAdvancedSettingsButton: React.FunctionComponent<Props> = props => {
|
||||
const iconName = props.advancedSettingsVisible ? 'fa fa-angle-down' : 'fa fa-angle-right';
|
||||
return (
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<Button
|
||||
level={ButtonLevel.Secondary}
|
||||
onClick={props.onClick}
|
||||
iconName={iconName}
|
||||
title={_('Show Advanced Settings')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default ToggleAdvancedSettingsButton;
|
||||
@@ -13,7 +13,7 @@ import { PluginItem } from './PluginBox';
|
||||
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import useOnInstallHandler, { OnPluginSettingChangeEvent } from './useOnInstallHandler';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
import StyledMessage from '../../../style/StyledMessage';
|
||||
import StyledLink from '../../../style/StyledLink';
|
||||
const { space } = require('styled-system');
|
||||
|
||||
@@ -29,7 +29,7 @@ const callHook = (isUpdate: boolean, pluginEnabled = true, pluginInstalledViaGUI
|
||||
},
|
||||
repoApi,
|
||||
onPluginSettingsChange,
|
||||
isUpdate
|
||||
isUpdate,
|
||||
);
|
||||
|
||||
describe('useOnInstallHandler', () => {
|
||||
@@ -37,7 +37,7 @@ describe('useOnInstallHandler', () => {
|
||||
beforeAll(() => {
|
||||
(PluginService.instance as jest.Mock).mockReturnValue(pluginServiceInstance);
|
||||
(defaultPluginSetting as jest.Mock).mockImplementation(
|
||||
jest.requireActual('@joplin/lib/services/plugins/PluginService').defaultPluginSetting
|
||||
jest.requireActual('@joplin/lib/services/plugins/PluginService').defaultPluginSetting,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback } from 'react';
|
||||
import PluginService, { defaultPluginSetting, PluginSettings } from '@joplin/lib/services/plugins/PluginService';
|
||||
import produce from 'immer';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
import { ItemEvent } from './PluginBox';
|
||||
|
||||
const logger = Logger.create('useOnInstallHandler');
|
||||
|
||||
@@ -59,7 +59,7 @@ export default function DialogButtonRow(props: Props) {
|
||||
buttonComps.push(
|
||||
<button key={b.name} style={buttonStyle} onClick={() => onCustomButtonClick({ buttonName: b.name })} onKeyDown={onKeyDown}>
|
||||
{b.label}
|
||||
</button>
|
||||
</button>,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ export default function DialogButtonRow(props: Props) {
|
||||
buttonComps.push(
|
||||
<button disabled={props.okButtonDisabled} key="ok" style={buttonStyle} onClick={onOkButtonClick} ref={props.okButtonRef} onKeyDown={onKeyDown}>
|
||||
{props.okButtonLabel ? props.okButtonLabel : _('OK')}
|
||||
</button>
|
||||
</button>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ export default function DialogButtonRow(props: Props) {
|
||||
buttonComps.push(
|
||||
<button disabled={props.cancelButtonDisabled} key="cancel" style={{ ...buttonStyle }} onClick={onCancelButtonClick}>
|
||||
{props.cancelButtonLabel ? props.cancelButtonLabel : _('Cancel')}
|
||||
</button>
|
||||
</button>,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,13 @@ import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types';
|
||||
import { getEncryptionEnabled, masterKeyEnabled, SyncInfo } from '@joplin/lib/services/synchronizer/syncInfoUtils';
|
||||
import { getDefaultMasterKey, getMasterPasswordStatusMessage, masterPasswordIsValid, toggleAndSetupEncryption } from '@joplin/lib/services/e2ee/utils';
|
||||
import Button, { ButtonLevel } from '../Button/Button';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from '../../app.reducer';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
import { PublicPrivateKeyPair } from '@joplin/lib/services/e2ee/ppk';
|
||||
import ToggleAdvancedSettingsButton from '../ConfigScreen/controls/ToggleAdvancedSettingsButton';
|
||||
|
||||
interface Props {
|
||||
themeId: any;
|
||||
@@ -62,7 +63,7 @@ const EncryptionConfigScreen = (props: Props) => {
|
||||
<tr key={mk.id}>
|
||||
<td style={theme.textStyle}>{mk.id}</td>
|
||||
<td><button onClick={() => onUpgradeMasterKey(mk)} style={theme.buttonStyle}>Upgrade</button></td>
|
||||
</tr>
|
||||
</tr>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -83,34 +84,6 @@ const EncryptionConfigScreen = (props: Props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const renderReencryptData = () => {
|
||||
if (!shim.isElectron()) return null;
|
||||
if (!props.shouldReencrypt) return null;
|
||||
|
||||
const theme = themeStyle(props.themeId);
|
||||
const buttonLabel = _('Re-encrypt data');
|
||||
|
||||
const intro = props.shouldReencrypt ? _('The default encryption method has been changed to a more secure one and it is recommended that you apply it to your data.') : _('You may use the tool below to re-encrypt your data, for example if you know that some of your notes are encrypted with an obsolete encryption method.');
|
||||
|
||||
let t = `${intro}\n\n${_('In order to do so, your entire data set will have to be encrypted and synchronised, so it is best to run it overnight.\n\nTo start, please follow these instructions:\n\n1. Synchronise all your devices.\n2. Click "%s".\n3. Let it run to completion. While it runs, avoid changing any note on your other devices, to avoid conflicts.\n4. Once sync is done on this device, sync all your other devices and let it run to completion.\n\nImportant: you only need to run this ONCE on one device.', buttonLabel)}`;
|
||||
|
||||
t = t.replace(/\n\n/g, '</p><p>');
|
||||
t = t.replace(/\n/g, '<br>');
|
||||
t = `<p>${t}</p>`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{_('Re-encryption')}</h2>
|
||||
<p style={theme.textStyle} dangerouslySetInnerHTML={{ __html: t }}></p>
|
||||
<span style={{ marginRight: 10 }}>
|
||||
<button onClick={() => void reencryptData()} style={theme.buttonStyle}>{buttonLabel}</button>
|
||||
</span>
|
||||
|
||||
{ !props.shouldReencrypt ? null : <button onClick={() => dontReencryptData()} style={theme.buttonStyle}>{_('Ignore')}</button> }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderMasterKey = (mk: MasterKeyEntity) => {
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
@@ -121,6 +94,12 @@ const EncryptionConfigScreen = (props: Props) => {
|
||||
borderColor: theme.dividerColor,
|
||||
};
|
||||
|
||||
const missingPasswordCellStyle = {
|
||||
...theme.textStyle,
|
||||
border: '3px solid',
|
||||
borderColor: theme.colorError,
|
||||
};
|
||||
|
||||
const password = inputPasswords[mk.id] ? inputPasswords[mk.id] : '';
|
||||
const isActive = props.activeMasterKeyId === mk.id;
|
||||
const activeIcon = isActive ? '✔' : '';
|
||||
@@ -135,8 +114,15 @@ const EncryptionConfigScreen = (props: Props) => {
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<td style={theme.textStyle}>
|
||||
<input type="password" style={passwordStyle} value={password} onChange={event => onInputPasswordChange(mk, event.target.value)} />{' '}
|
||||
<td style={missingPasswordCellStyle}>
|
||||
<input
|
||||
type="password"
|
||||
placeholder={_('Enter password')}
|
||||
style={passwordStyle}
|
||||
value={password}
|
||||
onChange={event => onInputPasswordChange(mk, event.target.value)}
|
||||
/>
|
||||
{' '}
|
||||
<button style={theme.buttonStyle} onClick={() => onSavePasswordClick(mk, { ...props.passwords, ...inputPasswords })}>
|
||||
{_('Save')}
|
||||
</button>
|
||||
@@ -239,7 +225,6 @@ const EncryptionConfigScreen = (props: Props) => {
|
||||
/>
|
||||
);
|
||||
const needUpgradeSection = renderNeedUpgradeSection();
|
||||
const reencryptDataSection = renderReencryptData();
|
||||
|
||||
return (
|
||||
<div className="section">
|
||||
@@ -254,7 +239,6 @@ const EncryptionConfigScreen = (props: Props) => {
|
||||
{decryptedItemsInfo}
|
||||
{toggleButton}
|
||||
{needUpgradeSection}
|
||||
{props.shouldReencrypt ? reencryptDataSection : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -315,7 +299,7 @@ const EncryptionConfigScreen = (props: Props) => {
|
||||
rows.push(
|
||||
<tr key={id}>
|
||||
<td style={theme.textStyle}>{id}</td>
|
||||
</tr>
|
||||
</tr>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -338,6 +322,56 @@ const EncryptionConfigScreen = (props: Props) => {
|
||||
return nonExistingMasterKeySection;
|
||||
};
|
||||
|
||||
const renderReencryptData = () => {
|
||||
if (!shim.isElectron()) return null;
|
||||
if (!props.encryptionEnabled) return null;
|
||||
|
||||
const theme = themeStyle(props.themeId);
|
||||
const buttonLabel = _('Re-encrypt data');
|
||||
|
||||
const intro = props.shouldReencrypt ? _('The default encryption method has been changed to a more secure one and it is recommended that you apply it to your data.') : _('You may use the tool below to re-encrypt your data, for example if you know that some of your notes are encrypted with an obsolete encryption method.');
|
||||
|
||||
let t = `${intro}\n\n${_('In order to do so, your entire data set will have to be encrypted and synchronised, so it is best to run it overnight.\n\nTo start, please follow these instructions:\n\n1. Synchronise all your devices.\n2. Click "%s".\n3. Let it run to completion. While it runs, avoid changing any note on your other devices, to avoid conflicts.\n4. Once sync is done on this device, sync all your other devices and let it run to completion.\n\nImportant: you only need to run this ONCE on one device.', buttonLabel)}`;
|
||||
|
||||
t = t.replace(/\n\n/g, '</p><p>');
|
||||
t = t.replace(/\n/g, '<br>');
|
||||
t = `<p>${t}</p>`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{_('Re-encryption')}</h2>
|
||||
<p style={theme.textStyle} dangerouslySetInnerHTML={{ __html: t }}></p>
|
||||
<span style={{ marginRight: 10 }}>
|
||||
<button onClick={() => void reencryptData()} style={theme.buttonStyle}>{buttonLabel}</button>
|
||||
</span>
|
||||
|
||||
{ !props.shouldReencrypt ? null : <button onClick={() => dontReencryptData()} style={theme.buttonStyle}>{_('Ignore')}</button> }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// If the user should re-encrypt, ensure that the section is visible initially.
|
||||
const [showAdvanced, setShowAdvanced] = useState<boolean>(props.shouldReencrypt);
|
||||
const toggleAdvanced = useCallback(() => {
|
||||
setShowAdvanced(!showAdvanced);
|
||||
}, [showAdvanced]);
|
||||
|
||||
const renderAdvancedSection = () => {
|
||||
const reEncryptSection = renderReencryptData();
|
||||
|
||||
if (!reEncryptSection) return null;
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ToggleAdvancedSettingsButton
|
||||
onClick={toggleAdvanced}
|
||||
advancedSettingsVisible={showAdvanced}/>
|
||||
{ showAdvanced ? reEncryptSection : null }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="config-screen-content">
|
||||
{renderDebugSection()}
|
||||
@@ -346,6 +380,7 @@ const EncryptionConfigScreen = (props: Props) => {
|
||||
{renderMasterKeySection(props.masterKeys.filter(mk => masterKeyEnabled(mk)), true)}
|
||||
{renderMasterKeySection(props.masterKeys.filter(mk => !masterKeyEnabled(mk)), false)}
|
||||
{renderNonExistingMasterKeysSection()}
|
||||
{renderAdvancedSection()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@ interface State {
|
||||
|
||||
interface Props {
|
||||
message?: string;
|
||||
children: any;
|
||||
}
|
||||
|
||||
export default class ErrorBoundary extends React.Component<Props, State> {
|
||||
@@ -88,14 +89,14 @@ export default class ErrorBoundary extends React.Component<Props, State> {
|
||||
<section key="message">
|
||||
<h2>Message</h2>
|
||||
<p>{this.state.error.message}</p>
|
||||
</section>
|
||||
</section>,
|
||||
);
|
||||
|
||||
output.push(
|
||||
<section key="versionInfo">
|
||||
<h2>Version info</h2>
|
||||
<pre>{versionInfo(packageInfo, this.state.plugins).message}</pre>
|
||||
</section>
|
||||
</section>,
|
||||
);
|
||||
|
||||
if (this.state.pluginInfos.length) {
|
||||
@@ -103,7 +104,7 @@ export default class ErrorBoundary extends React.Component<Props, State> {
|
||||
<section key="pluginSettings">
|
||||
<h2>Plugins</h2>
|
||||
<pre>{JSON.stringify(this.state.pluginInfos, null, 4)}</pre>
|
||||
</section>
|
||||
</section>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -112,7 +113,7 @@ export default class ErrorBoundary extends React.Component<Props, State> {
|
||||
<section key="stacktrace">
|
||||
<h2>Stack trace</h2>
|
||||
<pre>{this.state.error.stack}</pre>
|
||||
</section>
|
||||
</section>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,7 +123,7 @@ export default class ErrorBoundary extends React.Component<Props, State> {
|
||||
<section key="componentStack">
|
||||
<h2>Component stack</h2>
|
||||
<pre>{this.state.errorInfo.componentStack}</pre>
|
||||
</section>
|
||||
</section>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ const styleSelector = createSelector(
|
||||
};
|
||||
|
||||
return output;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function platformAssets(type: string) {
|
||||
|
||||
@@ -42,7 +42,7 @@ class ImportScreenComponent extends React.Component<Props, State> {
|
||||
},
|
||||
() => {
|
||||
void this.doImport();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.inbox-email-value {
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
const { connect } = require('react-redux');
|
||||
import { AppState } from '../app.reducer';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { clipboard } from 'electron';
|
||||
import Button from './Button/Button';
|
||||
|
||||
type JoplinCloudConfigScreenProps = {
|
||||
inboxEmail: string;
|
||||
};
|
||||
|
||||
const JoplinCloudConfigScreen = (props: JoplinCloudConfigScreenProps) => {
|
||||
const copyToClipboard = () => {
|
||||
clipboard.writeText(props.inboxEmail);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{_('Email to note')}</h2>
|
||||
<p>{_('Any email sent to this address will be converted into a note and added to your collection. The note will be saved into the Inbox notebook')}</p>
|
||||
<p className='inbox-email-value'>{props.inboxEmail}</p>
|
||||
<Button onClick={copyToClipboard} title={_('Copy to clipboard')} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
return {
|
||||
inboxEmail: state.settings['sync.10.inboxEmail'],
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(JoplinCloudConfigScreen);
|
||||
@@ -88,7 +88,7 @@ export const KeymapConfigScreen = ({ themeId }: KeymapConfigScreenProps) => {
|
||||
<div>
|
||||
{accelerator.split('+').map(part => <kbd style={styles.kbd} key={part}>{part}</kbd>).reduce(
|
||||
(accumulator, part) => (accumulator.length ? [...accumulator, ' + ', part] : [part]),
|
||||
[]
|
||||
[],
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -12,7 +12,7 @@ const useCommandStatus = (): [CommandStatus, (commandName: string)=> void, (comm
|
||||
keymapService.getCommandNames().reduce((accumulator: CommandStatus, command: string) => {
|
||||
accumulator[command] = false;
|
||||
return accumulator;
|
||||
}, {})
|
||||
}, {}),
|
||||
);
|
||||
|
||||
const disableStatus = (commandName: string) => setStatus(prevStatus => ({ ...prevStatus, [commandName]: false }));
|
||||
|
||||
@@ -20,6 +20,7 @@ import NoteListWrapper from '../NoteListWrapper/NoteListWrapper';
|
||||
import { AppState } from '../../app.reducer';
|
||||
import { saveLayout, loadLayout } from '../ResizableLayout/utils/persist';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import shouldShowMissingPasswordWarning from '@joplin/lib/components/shared/config/shouldShowMissingPasswordWarning';
|
||||
import produce from 'immer';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import bridge from '../../services/bridge';
|
||||
@@ -67,6 +68,7 @@ interface Props {
|
||||
shouldUpgradeSyncTarget: boolean;
|
||||
hasDisabledSyncItems: boolean;
|
||||
hasDisabledEncryptionItems: boolean;
|
||||
hasMissingSyncCredentials: boolean;
|
||||
showMissingMasterKeyMessage: boolean;
|
||||
showNeedUpgradingMasterKeyMessage: boolean;
|
||||
showShouldReencryptMessage: boolean;
|
||||
@@ -561,6 +563,16 @@ class MainScreenComponent extends React.Component<Props, State> {
|
||||
});
|
||||
};
|
||||
|
||||
const onViewSyncSettingsScreen = () => {
|
||||
this.props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Config',
|
||||
props: {
|
||||
defaultSection: 'sync',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onViewPluginScreen = () => {
|
||||
this.props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
@@ -596,31 +608,37 @@ class MainScreenComponent extends React.Component<Props, State> {
|
||||
msg = this.renderNotificationMessage(
|
||||
_('Safe mode is currently active. Note rendering and all plugins are temporarily disabled.'),
|
||||
_('Disable safe mode and restart'),
|
||||
onDisableSafeModeAndRestart
|
||||
onDisableSafeModeAndRestart,
|
||||
);
|
||||
} else if (this.props.hasMissingSyncCredentials) {
|
||||
msg = this.renderNotificationMessage(
|
||||
_('The synchronisation password is missing.'),
|
||||
_('Set the password'),
|
||||
onViewSyncSettingsScreen,
|
||||
);
|
||||
} else if (this.props.shouldUpgradeSyncTarget) {
|
||||
msg = this.renderNotificationMessage(
|
||||
_('The sync target needs to be upgraded before Joplin can sync. The operation may take a few minutes to complete and the app needs to be restarted. To proceed please click on the link.'),
|
||||
_('Restart and upgrade'),
|
||||
onRestartAndUpgrade
|
||||
onRestartAndUpgrade,
|
||||
);
|
||||
} else if (this.props.hasDisabledEncryptionItems) {
|
||||
msg = this.renderNotificationMessage(
|
||||
_('Some items cannot be decrypted.'),
|
||||
_('View them now'),
|
||||
onViewStatusScreen
|
||||
onViewStatusScreen,
|
||||
);
|
||||
} else if (this.props.showNeedUpgradingMasterKeyMessage) {
|
||||
msg = this.renderNotificationMessage(
|
||||
_('One of your master keys use an obsolete encryption method.'),
|
||||
_('View them now'),
|
||||
onViewEncryptionConfigScreen
|
||||
onViewEncryptionConfigScreen,
|
||||
);
|
||||
} else if (this.props.showShouldReencryptMessage) {
|
||||
msg = this.renderNotificationMessage(
|
||||
_('The default encryption method has been changed, you should re-encrypt your data.'),
|
||||
_('More info'),
|
||||
onViewEncryptionConfigScreen
|
||||
onViewEncryptionConfigScreen,
|
||||
);
|
||||
} else if (this.showShareInvitationNotification(this.props)) {
|
||||
const invitation = this.props.shareInvitations.find(inv => inv.status === 0);
|
||||
@@ -631,25 +649,25 @@ class MainScreenComponent extends React.Component<Props, State> {
|
||||
_('Accept'),
|
||||
() => onInvitationRespond(invitation.id, invitation.share.folder_id, invitation.master_key, true),
|
||||
_('Reject'),
|
||||
() => onInvitationRespond(invitation.id, invitation.share.folder_id, invitation.master_key, false)
|
||||
() => onInvitationRespond(invitation.id, invitation.share.folder_id, invitation.master_key, false),
|
||||
);
|
||||
} else if (this.props.hasDisabledSyncItems) {
|
||||
msg = this.renderNotificationMessage(
|
||||
_('Some items cannot be synchronised.'),
|
||||
_('View them now'),
|
||||
onViewStatusScreen
|
||||
onViewStatusScreen,
|
||||
);
|
||||
} else if (this.props.showMissingMasterKeyMessage) {
|
||||
msg = this.renderNotificationMessage(
|
||||
_('One or more master keys need a password.'),
|
||||
_('Set the password'),
|
||||
onViewEncryptionConfigScreen
|
||||
onViewEncryptionConfigScreen,
|
||||
);
|
||||
} else if (this.props.showInstallTemplatesPlugin) {
|
||||
msg = this.renderNotificationMessage(
|
||||
'The template feature has been moved to a plugin called "Templates".',
|
||||
'Install plugin',
|
||||
onViewPluginScreen
|
||||
onViewPluginScreen,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -662,7 +680,7 @@ class MainScreenComponent extends React.Component<Props, State> {
|
||||
|
||||
public messageBoxVisible(props: Props = null) {
|
||||
if (!props) props = this.props;
|
||||
return props.hasDisabledSyncItems || props.showMissingMasterKeyMessage || props.showNeedUpgradingMasterKeyMessage || props.showShouldReencryptMessage || props.hasDisabledEncryptionItems || this.props.shouldUpgradeSyncTarget || props.isSafeMode || this.showShareInvitationNotification(props) || this.props.needApiAuth || this.props.showInstallTemplatesPlugin;
|
||||
return props.hasDisabledSyncItems || props.showMissingMasterKeyMessage || props.hasMissingSyncCredentials || props.showNeedUpgradingMasterKeyMessage || props.showShouldReencryptMessage || props.hasDisabledEncryptionItems || this.props.shouldUpgradeSyncTarget || props.isSafeMode || this.showShareInvitationNotification(props) || this.props.needApiAuth || this.props.showInstallTemplatesPlugin;
|
||||
}
|
||||
|
||||
public registerCommands() {
|
||||
@@ -875,6 +893,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
showNeedUpgradingMasterKeyMessage: showNeedUpgradingEnabledMasterKeyMessage,
|
||||
showShouldReencryptMessage: state.settings['encryption.shouldReencrypt'] >= Setting.SHOULD_REENCRYPT_YES,
|
||||
shouldUpgradeSyncTarget: state.settings['sync.upgradeState'] === Setting.SYNC_UPGRADE_STATE_SHOULD_DO,
|
||||
hasMissingSyncCredentials: shouldShowMissingPasswordWarning(state.settings['sync.target'], state.settings),
|
||||
pluginsLegacy: state.pluginsLegacy,
|
||||
plugins: state.pluginService.plugins,
|
||||
pluginHtmlContents: state.pluginService.pluginHtmlContents,
|
||||
|
||||
@@ -17,10 +17,12 @@ export const runtime = (): CommandRuntime => {
|
||||
const folder = await Folder.load(folderId);
|
||||
if (!folder) throw new Error(`No such folder: ${folderId}`);
|
||||
|
||||
const ok = bridge().showConfirmMessageBox(_('Delete notebook "%s"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.', substrWithEllipsis(folder.title, 0, 32)), {
|
||||
buttons: [_('Delete'), _('Cancel')],
|
||||
defaultId: 1,
|
||||
});
|
||||
let deleteMessage = _('Delete notebook "%s"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.', substrWithEllipsis(folder.title, 0, 32));
|
||||
if (folderId === context.state.settings['sync.10.inboxId']) {
|
||||
deleteMessage = _('Delete the Inbox notebook?\n\nIf you delete the inbox notebook, any email that\'s recently been sent to it may be lost.');
|
||||
}
|
||||
|
||||
const ok = bridge().showConfirmMessageBox(deleteMessage);
|
||||
if (!ok) return;
|
||||
|
||||
await Folder.delete(folderId);
|
||||
|
||||
@@ -38,6 +38,7 @@ export const runtime = (): CommandRuntime => {
|
||||
menuItem.click();
|
||||
});
|
||||
}
|
||||
return null;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import ShareService from '@joplin/lib/services/share/ShareService';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
|
||||
const logger = Logger.create('leaveSharedFolder');
|
||||
|
||||
|
||||
@@ -301,7 +301,7 @@ function useMenu(props: Props) {
|
||||
return menuUtils.commandsToMenuItems(
|
||||
commandNames.concat(pluginCommandNames),
|
||||
(commandName: string) => onMenuItemClickRef.current(commandName),
|
||||
props.locale
|
||||
props.locale,
|
||||
);
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [commandNames, pluginCommandNames, props.locale]);
|
||||
@@ -347,7 +347,7 @@ function useMenu(props: Props) {
|
||||
if (type === 'notes') {
|
||||
sortItems.push(
|
||||
{ ...menuItemDic.toggleNotesSortOrderReverse, type: 'checkbox' },
|
||||
{ ...menuItemDic.toggleNotesSortOrderField, visible: false }
|
||||
{ ...menuItemDic.toggleNotesSortOrderField, visible: false },
|
||||
);
|
||||
} else {
|
||||
sortItems.push({
|
||||
@@ -391,7 +391,7 @@ function useMenu(props: Props) {
|
||||
{
|
||||
plugins: pluginsRef.current,
|
||||
customCss: props.customCss,
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -414,7 +414,7 @@ function useMenu(props: Props) {
|
||||
});
|
||||
|
||||
exportItems.push(
|
||||
menuItemDic.exportPdf
|
||||
menuItemDic.exportPdf,
|
||||
);
|
||||
|
||||
// We need a dummy entry, otherwise the ternary operator to show a
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
import * as React from 'react';
|
||||
import NoteListUtils from './utils/NoteListUtils';
|
||||
import { Dispatch } from 'redux';
|
||||
|
||||
const { buildStyle } = require('@joplin/lib/theme');
|
||||
const bridge = require('@electron/remote').require('./bridge').default;
|
||||
@@ -9,8 +10,7 @@ interface MultiNoteActionsProps {
|
||||
themeId: number;
|
||||
selectedNoteIds: string[];
|
||||
notes: any[];
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
dispatch: Function;
|
||||
dispatch: Dispatch;
|
||||
watchedNoteFiles: string[];
|
||||
plugins: PluginStates;
|
||||
inConflictFolder: boolean;
|
||||
@@ -68,7 +68,7 @@ export default function MultiNoteActions(props: MultiNoteActionsProps) {
|
||||
itemComps.push(
|
||||
<button key={item.label} style={styles.button} onClick={() => multiNotesButton_click(item)}>
|
||||
{item.label}
|
||||
</button>
|
||||
</button>,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useState, useEffect, useRef, forwardRef, useCallback, useImperativeHand
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { EditorCommand, NoteBodyEditorProps } from '../../utils/types';
|
||||
import { commandAttachFileToBody, handlePasteEvent } from '../../utils/resourceHandling';
|
||||
import { commandAttachFileToBody, getResourcesFromPasteEvent } from '../../utils/resourceHandling';
|
||||
import { ScrollOptions, ScrollOptionTypes } from '../../utils/types';
|
||||
import { CommandValue } from '../../utils/types';
|
||||
import { usePrevious, cursorPositionToTextOffset } from './utils';
|
||||
@@ -40,6 +40,7 @@ import ErrorBoundary from '../../../ErrorBoundary';
|
||||
import { MarkupToHtmlOptions } from '../../utils/useMarkupToHtml';
|
||||
import eventManager from '@joplin/lib/eventManager';
|
||||
import { EditContextMenuFilterObject } from '@joplin/lib/services/plugins/api/JoplinWorkspace';
|
||||
import type { ContextMenuEvent, ContextMenuParams } from 'electron';
|
||||
|
||||
const menuUtils = new MenuUtils(CommandService.instance());
|
||||
|
||||
@@ -98,7 +99,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const addListItem = useCallback((string1, defaultText = '') => {
|
||||
const addListItem = useCallback((string1: string, defaultText = '') => {
|
||||
if (editorRef.current) {
|
||||
if (editorRef.current.somethingSelected()) {
|
||||
editorRef.current.wrapSelectionsByLine(string1);
|
||||
@@ -267,7 +268,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
}, [props.content, props.visiblePanes, addListItem, wrapSelectionWithStrings, setEditorPercentScroll, setViewerPercentScroll, resetScroll]);
|
||||
|
||||
const onEditorPaste = useCallback(async (event: any = null) => {
|
||||
const resourceMds = await handlePasteEvent(event);
|
||||
const resourceMds = await getResourcesFromPasteEvent(event);
|
||||
if (!resourceMds.length) return;
|
||||
if (editorRef.current) {
|
||||
editorRef.current.replaceSelection(resourceMds.join('\n'));
|
||||
@@ -782,20 +783,50 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
// It might be buggy, refer to the below issue
|
||||
// https://github.com/laurent22/joplin/pull/3974#issuecomment-718936703
|
||||
useEffect(() => {
|
||||
function pointerInsideEditor(params: any) {
|
||||
const x = params.x, y = params.y, isEditable = params.isEditable, inputFieldType = params.inputFieldType;
|
||||
const isAncestorOfCodeMirrorEditor = (elem: HTMLElement) => {
|
||||
for (; elem.parentElement; elem = elem.parentElement) {
|
||||
if (elem.classList.contains('codeMirrorEditor')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
let lastInCodeMirrorContextMenuTimestamp = 0;
|
||||
|
||||
// The browser's contextmenu event provides additional information about the
|
||||
// target of the event, not provided by the Electron context-menu event.
|
||||
const onBrowserContextMenu = (event: Event) => {
|
||||
if (isAncestorOfCodeMirrorEditor(event.target as HTMLElement)) {
|
||||
lastInCodeMirrorContextMenuTimestamp = Date.now();
|
||||
}
|
||||
};
|
||||
|
||||
function pointerInsideEditor(params: ContextMenuParams) {
|
||||
const x = params.x, y = params.y, isEditable = params.isEditable;
|
||||
const elements = document.getElementsByClassName('codeMirrorEditor');
|
||||
|
||||
// inputFieldType: The input field type of CodeMirror is "textarea" so the inputFieldType = "none",
|
||||
// and any single-line input above codeMirror has inputFieldType value according to the type of input e.g.(text = plainText, password = password, ...).
|
||||
if (!elements.length || !isEditable || inputFieldType !== 'none') return null;
|
||||
// Note: We can't check inputFieldType here. When spellcheck is enabled,
|
||||
// params.inputFieldType is "none". When spellcheck is disabled,
|
||||
// params.inputFieldType is "plainText". Thus, such a check would be inconsistent.
|
||||
if (!elements.length || !isEditable) return false;
|
||||
|
||||
const maximumMsSinceBrowserEvent = 100;
|
||||
if (Date.now() - lastInCodeMirrorContextMenuTimestamp > maximumMsSinceBrowserEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const rect = convertToScreenCoordinates(Setting.value('windowContentZoomFactor'), elements[0].getBoundingClientRect());
|
||||
return rect.x < x && rect.y < y && rect.right > x && rect.bottom > y;
|
||||
}
|
||||
|
||||
async function onContextMenu(_event: any, params: any) {
|
||||
async function onContextMenu(event: ContextMenuEvent, params: ContextMenuParams) {
|
||||
if (!pointerInsideEditor(params)) return;
|
||||
|
||||
// Don't show the default menu.
|
||||
event.preventDefault();
|
||||
|
||||
const menu = new Menu();
|
||||
|
||||
const hasSelectedText = editorRef.current && !!editorRef.current.getSelection() ;
|
||||
@@ -807,7 +838,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
click: async () => {
|
||||
editorCutText();
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
menu.append(
|
||||
@@ -817,7 +848,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
click: async () => {
|
||||
editorCopyText();
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
menu.append(
|
||||
@@ -827,7 +858,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
click: async () => {
|
||||
editorPaste();
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
const spellCheckerMenuItems = SpellCheckerService.instance().contextMenuItems(params.misspelledWord, params.dictionarySuggestions);
|
||||
@@ -872,10 +903,15 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
menu.popup();
|
||||
}
|
||||
|
||||
bridge().window().webContents.on('context-menu', onContextMenu);
|
||||
// Prepend the event listener so that it gets called before
|
||||
// the listener that shows the default menu.
|
||||
bridge().window().webContents.prependListener('context-menu', onContextMenu);
|
||||
|
||||
window.addEventListener('contextmenu', onBrowserContextMenu);
|
||||
|
||||
return () => {
|
||||
bridge().window().webContents.off('context-menu', onContextMenu);
|
||||
window.removeEventListener('contextmenu', onBrowserContextMenu);
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.plugins]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
|
||||
const logger = Logger.create('useEditorSearch');
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ export default function useScrollHandler(editorRef: any, webviewRef: any, onScro
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, []);
|
||||
|
||||
const editor_resize = useCallback((cm) => {
|
||||
const editor_resize = useCallback((cm: any) => {
|
||||
if (isCodeMirrorReady(cm)) {
|
||||
// This handler is called when resized and refreshed.
|
||||
// Only when resized, the scroll position is restored.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from 'react';
|
||||
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps, ResourceInfos } from '../../utils/types';
|
||||
import { resourcesStatus, commandAttachFileToBody, handlePasteEvent, processPastedHtml, attachedResources } from '../../utils/resourceHandling';
|
||||
import { resourcesStatus, commandAttachFileToBody, getResourcesFromPasteEvent, processPastedHtml, attachedResources } from '../../utils/resourceHandling';
|
||||
import useScroll from './utils/useScroll';
|
||||
import styles_ from './styles';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
@@ -91,7 +91,7 @@ let dispatchDidUpdateIID_: any = null;
|
||||
let changeId_ = 1;
|
||||
|
||||
const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
const [editor, setEditor] = useState(null);
|
||||
const [editor, setEditor] = useState<Editor|null>(null);
|
||||
const [scriptLoaded, setScriptLoaded] = useState(false);
|
||||
const [editorReady, setEditorReady] = useState(false);
|
||||
const [draggingStarted, setDraggingStarted] = useState(false);
|
||||
@@ -480,6 +480,23 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
border-top: none !important;
|
||||
}
|
||||
|
||||
/* Override the TinyMCE font styles with more specific CSS selectors.
|
||||
Without this, the built-in FontAwesome styles are not applied because
|
||||
they are overridden by TinyMCE. */
|
||||
.plugin-icon.fa, .plugin-icon.far, .plugin-icon.fas {
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-size: ${theme.toolbarHeight - theme.toolbarPadding}px;
|
||||
}
|
||||
|
||||
.plugin-icon.fa, .plugin-icon.fas {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.plugin-icon.fab, .plugin-icon.far {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
||||
.joplin-tinymce .tox-toolbar__group {
|
||||
background-color: ${theme.backgroundColor3};
|
||||
padding-top: ${theme.toolbarPadding}px;
|
||||
@@ -548,15 +565,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
'bold', 'italic', 'joplinHighlight', 'joplinStrikethrough', 'formattingExtras', '|',
|
||||
'link', 'joplinInlineCode', 'joplinCodeBlock', 'joplinAttach', '|',
|
||||
'bullist', 'numlist', 'joplinChecklist', '|',
|
||||
'h1', 'h2', 'h3', 'hr', 'blockquote', 'inserttable', `joplinInsertDateTime${toolbarPluginButtons}`,
|
||||
];
|
||||
|
||||
// Available table toolbar buttons:
|
||||
// https://www.tiny.cloud/docs/advanced/available-toolbar-buttons/#tableplugin
|
||||
const tableToolbar = [
|
||||
'tabledelete',
|
||||
'tableinsertrowafter tablecopyrow tablepasterowafter tabledeleterow',
|
||||
'tableinsertcolafter tablecopycol tablepastecolafter tabledeletecol',
|
||||
'h1', 'h2', 'h3', 'hr', 'blockquote', 'table', `joplinInsertDateTime${toolbarPluginButtons}`,
|
||||
];
|
||||
|
||||
const editors = await (window as any).tinymce.init({
|
||||
@@ -569,7 +578,12 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
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
|
||||
valid_elements: '*[*]', // We already filter in sanitize_html
|
||||
|
||||
// #p: Pad empty paragraphs with to prevent them from being removed.
|
||||
// *[*]: Allow all elements and attributes -- we already filter in sanitize_html
|
||||
// See https://www.tiny.cloud/docs/configure/content-filtering/#controlcharacters
|
||||
valid_elements: '#p,*[*]',
|
||||
|
||||
menubar: false,
|
||||
relative_urls: false,
|
||||
branding: false,
|
||||
@@ -578,7 +592,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
// Handle the first table row as table header.
|
||||
// https://www.tiny.cloud/docs/plugins/table/#table_header_type
|
||||
table_header_type: 'sectionCells',
|
||||
table_toolbar: tableToolbar.join(' | '),
|
||||
table_resize_bars: false,
|
||||
language_url: ['en_US', 'en_GB'].includes(language) ? undefined : `${bridge().vendorDir()}/lib/tinymce/langs/${language}`,
|
||||
toolbar: toolbar.join(' '),
|
||||
@@ -591,6 +604,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
joplinInsert: { inline: 'ins', remove: 'all' },
|
||||
joplinSub: { inline: 'sub', remove: 'all' },
|
||||
joplinSup: { inline: 'sup', remove: 'all' },
|
||||
code: { inline: 'code', remove: 'all', attributes: { spellcheck: false } },
|
||||
},
|
||||
setup: (editor: Editor) => {
|
||||
editor.addCommand('joplinAttach', () => {
|
||||
@@ -631,22 +645,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
},
|
||||
});
|
||||
|
||||
editor.ui.registry.addMenuButton('inserttable', {
|
||||
icon: 'table',
|
||||
tooltip: 'Table',
|
||||
fetch: (callback) => {
|
||||
callback([
|
||||
{
|
||||
type: 'fancymenuitem',
|
||||
fancytype: 'inserttable',
|
||||
onAction: (data) => {
|
||||
editor.execCommand('mceInsertTable', false, { rows: data.numRows, columns: data.numColumns, options: { headerRows: 1 } });
|
||||
},
|
||||
},
|
||||
]);
|
||||
},
|
||||
});
|
||||
|
||||
editor.ui.registry.addButton('joplinInsertDateTime', {
|
||||
tooltip: _('Insert time'),
|
||||
icon: 'insert-time',
|
||||
@@ -656,9 +654,16 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
});
|
||||
|
||||
for (const pluginCommandName of pluginCommandNames) {
|
||||
const iconClassName = CommandService.instance().iconName(pluginCommandName);
|
||||
|
||||
// Only allow characters that appear in Font Awesome class names: letters, spaces, and dashes.
|
||||
const safeIconClassName = iconClassName.replace(/[^a-z0-9 -]/g, '');
|
||||
|
||||
editor.ui.registry.addIcon(pluginCommandName, `<i class="plugin-icon ${safeIconClassName}"></i>`);
|
||||
|
||||
editor.ui.registry.addButton(pluginCommandName, {
|
||||
tooltip: CommandService.instance().label(pluginCommandName),
|
||||
icon: CommandService.instance().iconName(pluginCommandName, 'tinymce'),
|
||||
icon: pluginCommandName,
|
||||
onAction: function() {
|
||||
void CommandService.instance().execute(pluginCommandName);
|
||||
},
|
||||
@@ -698,7 +703,17 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
setEditorReady(true);
|
||||
});
|
||||
|
||||
const preprocessContent = () => {
|
||||
// Disable spellcheck for all inline code blocks.
|
||||
const codeElements = editor.dom.doc.querySelectorAll('code.inline-code');
|
||||
for (const code of codeElements) {
|
||||
code.setAttribute('spellcheck', 'false');
|
||||
}
|
||||
};
|
||||
|
||||
editor.on('SetContent', () => {
|
||||
preprocessContent();
|
||||
|
||||
props_onMessage.current({ channel: 'noteRenderComplete' });
|
||||
});
|
||||
},
|
||||
@@ -715,18 +730,10 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
// Set the initial content and load the plugin CSS and JS files
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
const loadDocumentAssets = (editor: any, pluginAssets: any[]) => {
|
||||
// Note: The way files are cached is not correct because it assumes there's only one version
|
||||
// of each file. However, when the theme change, a new CSS file, specific to the theme, is
|
||||
// created. That file should not be loaded on top of the previous one, but as a replacement.
|
||||
// Otherwise it would do this:
|
||||
// - Try to load CSS for theme 1 => OK
|
||||
// - Try to load CSS for theme 2 => OK
|
||||
// - Try to load CSS for theme 1 => Skip because the file is in cache. As a result, theme 2
|
||||
// incorrectly stay.
|
||||
// The fix would be to make allAssets() return a name and a version for each asset. Then the loading
|
||||
// code would check this and either append the CSS or replace.
|
||||
const documentCssElements: Record<string, HTMLLinkElement> = {};
|
||||
const documentScriptElements: Record<string, HTMLScriptElement> = {};
|
||||
|
||||
const loadDocumentAssets = (editor: any, pluginAssets: any[]) => {
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
let docHead_: any = null;
|
||||
@@ -737,49 +744,72 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
return docHead_;
|
||||
}
|
||||
|
||||
const cssFiles = [
|
||||
const allCssFiles = [
|
||||
`${bridge().vendorDir()}/lib/@fortawesome/fontawesome-free/css/all.min.css`,
|
||||
`gui/note-viewer/pluginAssets/highlight.js/${theme.codeThemeCss}`,
|
||||
].concat(
|
||||
pluginAssets
|
||||
.filter((a: any) => a.mime === 'text/css')
|
||||
.map((a: any) => a.path)
|
||||
).filter((path: string) => !loadedCssFiles_.includes(path));
|
||||
.map((a: any) => a.path),
|
||||
);
|
||||
|
||||
const jsFiles = [].concat(
|
||||
const allJsFiles = [].concat(
|
||||
pluginAssets
|
||||
.filter((a: any) => a.mime === 'application/javascript')
|
||||
.map((a: any) => a.path)
|
||||
).filter((path: string) => !loadedJsFiles_.includes(path));
|
||||
.map((a: any) => a.path),
|
||||
);
|
||||
|
||||
for (const cssFile of cssFiles) loadedCssFiles_.push(cssFile);
|
||||
for (const jsFile of jsFiles) loadedJsFiles_.push(jsFile);
|
||||
|
||||
// Remove all previously loaded files that aren't in the assets this time.
|
||||
// Note: This is important to ensure that we properly change themes.
|
||||
// See https://github.com/laurent22/joplin/issues/8520
|
||||
for (const cssFile of loadedCssFiles_) {
|
||||
if (!allCssFiles.includes(cssFile)) {
|
||||
documentCssElements[cssFile]?.remove();
|
||||
delete documentCssElements[cssFile];
|
||||
}
|
||||
}
|
||||
|
||||
for (const jsFile of loadedJsFiles_) {
|
||||
if (!allJsFiles.includes(jsFile)) {
|
||||
documentScriptElements[jsFile]?.remove();
|
||||
delete documentScriptElements[jsFile];
|
||||
}
|
||||
}
|
||||
|
||||
const newCssFiles = allCssFiles.filter((path: string) => !loadedCssFiles_.includes(path));
|
||||
const newJsFiles = allJsFiles.filter((path: string) => !loadedJsFiles_.includes(path));
|
||||
|
||||
loadedCssFiles_ = allCssFiles;
|
||||
loadedJsFiles_ = allJsFiles;
|
||||
|
||||
// console.info('loadDocumentAssets: files to load', cssFiles, jsFiles);
|
||||
|
||||
if (cssFiles.length) {
|
||||
for (const cssFile of cssFiles) {
|
||||
const script = editor.dom.create('link', {
|
||||
if (newCssFiles.length) {
|
||||
for (const cssFile of newCssFiles) {
|
||||
const style = editor.dom.create('link', {
|
||||
rel: 'stylesheet',
|
||||
type: 'text/css',
|
||||
href: cssFile,
|
||||
class: 'jop-tinymce-css',
|
||||
});
|
||||
|
||||
docHead().appendChild(script);
|
||||
documentCssElements[cssFile] = style;
|
||||
docHead().appendChild(style);
|
||||
}
|
||||
}
|
||||
|
||||
if (jsFiles.length) {
|
||||
if (newJsFiles.length) {
|
||||
const editorElementId = editor.dom.uniqueId();
|
||||
|
||||
for (const jsFile of jsFiles) {
|
||||
for (const jsFile of newJsFiles) {
|
||||
const script = editor.dom.create('script', {
|
||||
id: editorElementId,
|
||||
type: 'text/javascript',
|
||||
src: jsFile,
|
||||
});
|
||||
|
||||
documentScriptElements[jsFile] = script;
|
||||
docHead().appendChild(script);
|
||||
}
|
||||
}
|
||||
@@ -1034,38 +1064,43 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
// to be processed in various ways.
|
||||
event.preventDefault();
|
||||
|
||||
const resourceMds = await handlePasteEvent(event);
|
||||
if (resourceMds.length) {
|
||||
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, resourceMds.join('\n'), markupRenderOptions({ bodyOnly: true }));
|
||||
editor.insertContent(result.html);
|
||||
} else {
|
||||
const pastedText = event.clipboardData.getData('text/plain');
|
||||
const pastedText = event.clipboardData.getData('text/plain');
|
||||
|
||||
// event.clipboardData.getData('text/html') wraps the
|
||||
// content with <html><body></body></html>, which seems to
|
||||
// be not supported in editor.insertContent().
|
||||
//
|
||||
// when pasting text with Ctrl+Shift+V, the format should be
|
||||
// ignored. In this case,
|
||||
// event.clopboardData.getData('text/html') returns an empty
|
||||
// string, but the clipboard.readHTML() still returns the
|
||||
// formatted text.
|
||||
const pastedHtml = event.clipboardData.getData('text/html') ? clipboard.readHTML() : '';
|
||||
|
||||
// We should only process the images if there is no plain text or
|
||||
// HTML text in the clipboard. This is because certain applications,
|
||||
// such as Word, are going to add multiple versions of the copied
|
||||
// data to the clipboard - one with the text formatted as HTML, and
|
||||
// one with the text as an image. In that case, we need to ignore
|
||||
// the image and only process the HTML.
|
||||
|
||||
if (!pastedText && !pastedHtml) {
|
||||
const resourceMds = await getResourcesFromPasteEvent(event);
|
||||
if (resourceMds.length) {
|
||||
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, resourceMds.join('\n'), markupRenderOptions({ bodyOnly: true }));
|
||||
editor.insertContent(result.html);
|
||||
}
|
||||
} else {
|
||||
if (BaseItem.isMarkdownTag(pastedText)) { // Paste a link to a note
|
||||
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, pastedText, markupRenderOptions({ bodyOnly: true }));
|
||||
editor.insertContent(result.html);
|
||||
} else { // Paste regular text
|
||||
// event.clipboardData.getData('text/html') wraps the content with <html><body></body></html>,
|
||||
// which seems to be not supported in editor.insertContent().
|
||||
//
|
||||
// when pasting text with Ctrl+Shift+V, the format should be ignored.
|
||||
// In this case, event.clopboardData.getData('text/html') returns an empty string, but the clipboard.readHTML() still returns the formatted text.
|
||||
const pastedHtml = event.clipboardData.getData('text/html') ? clipboard.readHTML() : '';
|
||||
if (pastedHtml) { // Handles HTML
|
||||
const modifiedHtml = await processPastedHtml(pastedHtml);
|
||||
editor.insertContent(modifiedHtml);
|
||||
} else { // Handles plain text
|
||||
pasteAsPlainText(pastedText);
|
||||
}
|
||||
|
||||
// This code before was necessary to get undo working after
|
||||
// pasting but it seems it's no longer necessary, so
|
||||
// removing it for now. We also couldn't do it immediately
|
||||
// it seems, or else nothing is added to the stack, so do it
|
||||
// on the next frame.
|
||||
//
|
||||
// window.requestAnimationFrame(() =>
|
||||
// editor.undoManager.add()); onChangeHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
||||
import TinyMCE from './NoteBody/TinyMCE/TinyMCE';
|
||||
import CodeMirror from './NoteBody/CodeMirror/CodeMirror';
|
||||
import { connect } from 'react-redux';
|
||||
@@ -40,7 +40,7 @@ import Note from '@joplin/lib/models/Note';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
const bridge = require('@electron/remote').require('./bridge').default;
|
||||
import NoteRevisionViewer from '../NoteRevisionViewer';
|
||||
import { readFromSettings } from '@joplin/lib/services/share/reducer';
|
||||
import { parseShareCache } from '@joplin/lib/services/share/reducer';
|
||||
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
||||
import { ModelType } from '@joplin/lib/BaseModel';
|
||||
import BaseItem from '@joplin/lib/models/BaseItem';
|
||||
@@ -78,6 +78,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
|
||||
const { formNote, setFormNote, isNewNote, resourceInfos } = useFormNote({
|
||||
syncStarted: props.syncStarted,
|
||||
decryptionStarted: props.decryptionStarted,
|
||||
noteId: effectiveNoteId,
|
||||
isProvisional: props.isProvisional,
|
||||
titleInputRef: titleInputRef,
|
||||
@@ -286,11 +287,15 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
// }
|
||||
// }, [props.dispatch]);
|
||||
|
||||
const shareCache = useMemo(() => {
|
||||
return parseShareCache(props.shareCacheSetting);
|
||||
}, [props.shareCacheSetting]);
|
||||
|
||||
useAsyncEffect(async event => {
|
||||
if (!formNote.id) return;
|
||||
|
||||
try {
|
||||
const result = await itemIsReadOnly(BaseItem, ModelType.Note, ItemChange.SOURCE_UNSPECIFIED, formNote.id, props.syncUserId, props.shareCache);
|
||||
const result = await itemIsReadOnly(BaseItem, ModelType.Note, ItemChange.SOURCE_UNSPECIFIED, formNote.id, props.syncUserId, shareCache);
|
||||
if (event.cancelled) return;
|
||||
setIsReadOnly(result);
|
||||
} catch (error) {
|
||||
@@ -301,7 +306,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}, [formNote.id, props.syncUserId, props.shareCache]);
|
||||
}, [formNote.id, props.syncUserId, shareCache]);
|
||||
|
||||
const onBodyWillChange = useCallback((event: any) => {
|
||||
handleProvisionalFlag();
|
||||
@@ -324,7 +329,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
|
||||
const onMessage = useMessageHandler(scrollWhenReady, setScrollWhenReady, editorRef, setLocalSearchResultCount, props.dispatch, formNote);
|
||||
|
||||
const externalEditWatcher_noteChange = useCallback((event) => {
|
||||
const externalEditWatcher_noteChange = useCallback((event: any) => {
|
||||
if (event.id === formNote.id) {
|
||||
const newFormNote = {
|
||||
...formNote,
|
||||
@@ -337,7 +342,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [formNote]);
|
||||
|
||||
const onNotePropertyChange = useCallback((event) => {
|
||||
const onNotePropertyChange = useCallback((event: any) => {
|
||||
setFormNote(formNote => {
|
||||
if (formNote.id !== event.note.id) return formNote;
|
||||
|
||||
@@ -629,6 +634,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
isProvisional: state.provisionalNoteIds.includes(noteId),
|
||||
editorNoteStatuses: state.editorNoteStatuses,
|
||||
syncStarted: state.syncStarted,
|
||||
decryptionStarted: state.decryptionWorker?.state !== 'idle',
|
||||
themeId: state.settings.theme,
|
||||
richTextBannerDismissed: state.settings.richTextBannerDismissed,
|
||||
watchedNoteFiles: state.watchedNoteFiles,
|
||||
@@ -656,7 +662,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
isSafeMode: state.settings.isSafeMode,
|
||||
useCustomPdfViewer: false,
|
||||
syncUserId: state.settings['sync.userId'],
|
||||
shareCache: readFromSettings(state),
|
||||
shareCacheSetting: state.settings['sync.shareCache'],
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Resource from '@joplin/lib/models/Resource';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
const logger = Logger.create('contextMenuUtils');
|
||||
export enum ContextMenuItemType {
|
||||
None = '',
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { processPastedHtml } from './resourceHandling';
|
||||
|
||||
describe('resourceHandling', () => {
|
||||
it('should sanitize pasted HTML', async () => {
|
||||
const testCases = [
|
||||
['Test: <style onload="evil()"></style>', 'Test: <style></style>'],
|
||||
['<a href="javascript: alert()">test</a>', '<a href="#">test</a>'],
|
||||
['<script >evil()</script>', ''],
|
||||
['<script>evil()</script>', ''],
|
||||
[
|
||||
'<img onload="document.body.innerHTML = evil;" src="data:image/svg+xml;base64,=="/>',
|
||||
'<img src="data:image/svg+xml;base64,=="/>',
|
||||
],
|
||||
];
|
||||
|
||||
for (const [html, expected] of testCases) {
|
||||
expect(await processPastedHtml(html)).toBe(expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,8 @@ import Resource from '@joplin/lib/models/Resource';
|
||||
const bridge = require('@electron/remote').require('./bridge').default;
|
||||
import ResourceFetcher from '@joplin/lib/services/ResourceFetcher';
|
||||
import htmlUtils from '@joplin/lib/htmlUtils';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import rendererHtmlUtils, { extractHtmlBody } from '@joplin/renderer/htmlUtils';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
const { fileUriToPath } = require('@joplin/lib/urlUtils');
|
||||
const joplinRendererUtils = require('@joplin/renderer').utils;
|
||||
const { clipboard } = require('electron');
|
||||
@@ -77,7 +78,7 @@ export async function commandAttachFileToBody(body: string, filePaths: string[]
|
||||
logger.info(`Attaching ${filePath}`);
|
||||
const newBody = await shim.attachFileToNoteBody(body, filePath, options.position, {
|
||||
createFileURL: options.createFileURL,
|
||||
resizeLargeImages: 'ask',
|
||||
resizeLargeImages: Setting.value('imageResizing'),
|
||||
});
|
||||
|
||||
if (!newBody) {
|
||||
@@ -106,7 +107,7 @@ export function resourcesStatus(resourceInfos: any) {
|
||||
return joplinRendererUtils.resourceStatusName(lowestIndex);
|
||||
}
|
||||
|
||||
export async function handlePasteEvent(event: any) {
|
||||
export async function getResourcesFromPasteEvent(event: any) {
|
||||
const output = [];
|
||||
const formats = clipboard.availableFormats();
|
||||
for (let i = 0; i < formats.length; i++) {
|
||||
@@ -159,6 +160,8 @@ export async function processPastedHtml(html: string) {
|
||||
const createdResource = await shim.createResourceFromPath(imageFilePath);
|
||||
mappedResources[imageSrc] = `file://${encodeURI(Resource.fullPath(createdResource))}`;
|
||||
}
|
||||
} else if (imageSrc.startsWith('data:')) { // Data URIs
|
||||
mappedResources[imageSrc] = imageSrc;
|
||||
} else {
|
||||
const filePath = `${Setting.value('tempDir')}/${md5(Date.now() + Math.random())}`;
|
||||
await shim.fetchBlob(imageSrc, { path: filePath });
|
||||
@@ -173,7 +176,9 @@ export async function processPastedHtml(html: string) {
|
||||
}
|
||||
}
|
||||
|
||||
return htmlUtils.replaceImageUrls(html, (src: string) => {
|
||||
return mappedResources[src];
|
||||
});
|
||||
return extractHtmlBody(rendererHtmlUtils.sanitizeHtml(
|
||||
htmlUtils.replaceImageUrls(html, (src: string) => {
|
||||
return mappedResources[src];
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
|
||||
import { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
import { State as ShareState } from '@joplin/lib/services/share/reducer';
|
||||
import { MarkupLanguage } from '@joplin/renderer';
|
||||
import { RenderResult, RenderResultPluginAsset } from '@joplin/renderer/MarkupToHtml';
|
||||
import { MarkupToHtmlOptions } from './useMarkupToHtml';
|
||||
import { Dispatch } from 'redux';
|
||||
|
||||
export interface AllAssetsOptions {
|
||||
contentMaxWidthTarget?: string;
|
||||
@@ -16,11 +15,9 @@ export interface ToolbarButtonInfos {
|
||||
}
|
||||
|
||||
export interface NoteEditorProps {
|
||||
// style: any;
|
||||
noteId: string;
|
||||
themeId: number;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
dispatch: Function;
|
||||
dispatch: Dispatch;
|
||||
selectedNoteIds: string[];
|
||||
selectedFolderId: string;
|
||||
notes: any[];
|
||||
@@ -28,6 +25,7 @@ export interface NoteEditorProps {
|
||||
isProvisional: boolean;
|
||||
editorNoteStatuses: any;
|
||||
syncStarted: boolean;
|
||||
decryptionStarted: boolean;
|
||||
bodyEditor: string;
|
||||
notesParentType: string;
|
||||
selectedNoteTags: any[];
|
||||
@@ -46,7 +44,7 @@ export interface NoteEditorProps {
|
||||
contentMaxWidth: number;
|
||||
isSafeMode: boolean;
|
||||
useCustomPdfViewer: boolean;
|
||||
shareCache: ShareState;
|
||||
shareCacheSetting: string;
|
||||
syncUserId: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import useFormNote, { HookDependencies } from './useFormNote';
|
||||
|
||||
|
||||
describe('useFormNote', () => {
|
||||
beforeEach(async () => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
});
|
||||
|
||||
it('should update note when decryption completes', async () => {
|
||||
const testNote = await Note.save({ title: 'Test Note!' });
|
||||
|
||||
const makeFormNoteProps = (syncStarted: boolean, decryptionStarted: boolean): HookDependencies => {
|
||||
return {
|
||||
syncStarted,
|
||||
decryptionStarted,
|
||||
noteId: testNote.id,
|
||||
isProvisional: false,
|
||||
titleInputRef: null,
|
||||
editorRef: null,
|
||||
onBeforeLoad: ()=>{},
|
||||
onAfterLoad: ()=>{},
|
||||
};
|
||||
};
|
||||
|
||||
const formNote = renderHook(props => useFormNote(props), {
|
||||
initialProps: makeFormNoteProps(true, false),
|
||||
});
|
||||
await formNote.waitFor(() => {
|
||||
expect(formNote.result.current.formNote).toMatchObject({
|
||||
encryption_applied: 0,
|
||||
title: testNote.title,
|
||||
});
|
||||
});
|
||||
|
||||
await Note.save({
|
||||
id: testNote.id,
|
||||
encryption_cipher_text: 'cipher_text',
|
||||
encryption_applied: 1,
|
||||
});
|
||||
|
||||
// Sync starting should cause a re-render
|
||||
formNote.rerender(makeFormNoteProps(false, false));
|
||||
|
||||
await formNote.waitFor(() => {
|
||||
expect(formNote.result.current.formNote).toMatchObject({
|
||||
encryption_applied: 1,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
formNote.rerender(makeFormNoteProps(false, true));
|
||||
|
||||
await Note.save({
|
||||
id: testNote.id,
|
||||
encryption_applied: 0,
|
||||
title: 'Test Note!',
|
||||
});
|
||||
|
||||
// Ending decryption should also cause a re-render
|
||||
formNote.rerender(makeFormNoteProps(false, false));
|
||||
|
||||
await formNote.waitFor(() => {
|
||||
expect(formNote.result.current.formNote).toMatchObject({
|
||||
encryption_applied: 0,
|
||||
title: 'Test Note!',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,7 +3,7 @@ import { FormNote, defaultFormNote, ResourceInfos } from './types';
|
||||
import { clearResourceCache, attachedResources } from './resourceHandling';
|
||||
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
|
||||
import { handleResourceDownloadMode } from './resourceHandling';
|
||||
import HtmlToHtml from '@joplin/renderer/HtmlToHtml';
|
||||
import { splitHtml } from '@joplin/renderer/HtmlToHtml';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import usePrevious from '../../hooks/usePrevious';
|
||||
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher/index';
|
||||
@@ -18,8 +18,9 @@ export interface OnLoadEvent {
|
||||
formNote: FormNote;
|
||||
}
|
||||
|
||||
interface HookDependencies {
|
||||
export interface HookDependencies {
|
||||
syncStarted: boolean;
|
||||
decryptionStarted: boolean;
|
||||
noteId: string;
|
||||
isProvisional: boolean;
|
||||
titleInputRef: any;
|
||||
@@ -61,20 +62,26 @@ function resourceInfosChanged(a: ResourceInfos, b: ResourceInfos): boolean {
|
||||
}
|
||||
|
||||
export default function useFormNote(dependencies: HookDependencies) {
|
||||
const { syncStarted, noteId, isProvisional, titleInputRef, editorRef, onBeforeLoad, onAfterLoad } = dependencies;
|
||||
const {
|
||||
syncStarted, decryptionStarted, noteId, isProvisional, titleInputRef, editorRef, onBeforeLoad, onAfterLoad,
|
||||
} = dependencies;
|
||||
|
||||
const [formNote, setFormNote] = useState<FormNote>(defaultFormNote());
|
||||
const [isNewNote, setIsNewNote] = useState(false);
|
||||
const prevSyncStarted = usePrevious(syncStarted);
|
||||
const prevDecryptionStarted = usePrevious(decryptionStarted);
|
||||
const previousNoteId = usePrevious(formNote.id);
|
||||
const [resourceInfos, setResourceInfos] = useState<ResourceInfos>({});
|
||||
|
||||
// Increasing the value of this counter cancels any ongoing note refreshes and starts
|
||||
// a new refresh.
|
||||
const [formNoteRefeshScheduled, setFormNoteRefreshScheduled] = useState<number>(0);
|
||||
|
||||
async function initNoteState(n: any) {
|
||||
let originalCss = '';
|
||||
|
||||
if (n.markup_language === MarkupToHtml.MARKUP_LANGUAGE_HTML) {
|
||||
const htmlToHtml = new HtmlToHtml();
|
||||
const splitted = htmlToHtml.splitHtml(n.body);
|
||||
const splitted = splitHtml(n.body);
|
||||
originalCss = splitted.css;
|
||||
}
|
||||
|
||||
@@ -107,14 +114,7 @@ export default function useFormNote(dependencies: HookDependencies) {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Check that synchronisation has just finished - and
|
||||
// if the note has never been changed, we reload it.
|
||||
// If the note has already been changed, it's a conflict
|
||||
// that's already been handled by the synchronizer.
|
||||
|
||||
if (!prevSyncStarted) return () => {};
|
||||
if (syncStarted) return () => {};
|
||||
if (formNote.hasChanged) return () => {};
|
||||
if (formNoteRefeshScheduled <= 0) return () => {};
|
||||
|
||||
reg.logger().info('Sync has finished and note has never been changed - reloading it');
|
||||
|
||||
@@ -133,6 +133,7 @@ export default function useFormNote(dependencies: HookDependencies) {
|
||||
}
|
||||
|
||||
await initNoteState(n);
|
||||
setFormNoteRefreshScheduled(0);
|
||||
};
|
||||
|
||||
void loadNote();
|
||||
@@ -140,8 +141,34 @@ export default function useFormNote(dependencies: HookDependencies) {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [prevSyncStarted, syncStarted, formNote]);
|
||||
}, [formNoteRefeshScheduled, noteId]);
|
||||
|
||||
const refreshFormNote = useCallback(() => {
|
||||
// Increase the counter to cancel any ongoing refresh attempts
|
||||
// and start a new one.
|
||||
setFormNoteRefreshScheduled(formNoteRefeshScheduled + 1);
|
||||
}, [formNoteRefeshScheduled]);
|
||||
|
||||
useEffect(() => {
|
||||
// Check that synchronisation has just finished - and
|
||||
// if the note has never been changed, we reload it.
|
||||
// If the note has already been changed, it's a conflict
|
||||
// that's already been handled by the synchronizer.
|
||||
const decryptionJustEnded = prevDecryptionStarted && !decryptionStarted;
|
||||
const syncJustEnded = prevSyncStarted && !syncStarted;
|
||||
|
||||
if (!decryptionJustEnded && !syncJustEnded) return;
|
||||
if (formNote.hasChanged) return;
|
||||
|
||||
// Refresh the form note.
|
||||
// This is kept separate from the above logic so that when prevSyncStarted is changed
|
||||
// from true to false, it doesn't cancel the note from loading.
|
||||
refreshFormNote();
|
||||
}, [
|
||||
prevSyncStarted, syncStarted,
|
||||
prevDecryptionStarted, decryptionStarted,
|
||||
formNote.hasChanged, refreshFormNote,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!noteId) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useCallback, MutableRefObject, useEffect } from 'react';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
import { SearchMarkers } from './useSearchMarkers';
|
||||
const CommandService = require('@joplin/lib/services/CommandService').default;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import ItemList from '../ItemList';
|
||||
const { connect } = require('react-redux');
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
import { Props } from './types';
|
||||
import { Props } from './utils/types';
|
||||
import usePrevious from '../hooks/usePrevious';
|
||||
import { itemIsReadOnlySync, ItemSlice } from '@joplin/lib/models/utils/readOnly';
|
||||
import { FolderEntity } from '@joplin/lib/services/database/types';
|
||||
@@ -246,7 +246,10 @@ const NoteListComponent = (props: Props) => {
|
||||
event.dataTransfer.setDragImage(new Image(), 1, 1);
|
||||
event.dataTransfer.clearData();
|
||||
event.dataTransfer.setData('text/x-jop-note-ids', JSON.stringify(noteIds));
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
// While setting
|
||||
// event.dataTransfer.effectAllowed = 'move';
|
||||
// causes the drag cursor to have a "move", rather than an "add", icon,
|
||||
// this breaks note drag and drop into the markdown editor.
|
||||
return true;
|
||||
}, [props.parentFolderIsReadOnly, props.selectedNoteIds]);
|
||||
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
import * as React from 'react';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { useMemo, useRef, useEffect } from 'react';
|
||||
import { AppState } from '../../app.reducer';
|
||||
import BaseModel, { ModelType } from '@joplin/lib/BaseModel';
|
||||
import { ItemFlow, Props } from './utils/types';
|
||||
import { itemIsReadOnlySync, ItemSlice } from '@joplin/lib/models/utils/readOnly';
|
||||
import { FolderEntity } from '@joplin/lib/services/database/types';
|
||||
import ItemChange from '@joplin/lib/models/ItemChange';
|
||||
import { Size } from '@joplin/utils/types';
|
||||
import NoteListItem from '../NoteListItem/NoteListItem';
|
||||
import useRenderedNotes from './utils/useRenderedNotes';
|
||||
import useItemCss from './utils/useItemCss';
|
||||
import useOnContextMenu from '../NoteListItem/utils/useOnContextMenu';
|
||||
import useVisibleRange from './utils/useVisibleRange';
|
||||
import useScroll from './utils/useScroll';
|
||||
import useFocusNote from './utils/useFocusNote';
|
||||
import useOnNoteClick from './utils/useOnNoteClick';
|
||||
import useMoveNote from './utils/useMoveNote';
|
||||
import useOnKeyDown from './utils/useOnKeyDown';
|
||||
import * as focusElementNoteList from './commands/focusElementNoteList';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
import useDragAndDrop from './utils/useDragAndDrop';
|
||||
import usePrevious from '../hooks/usePrevious';
|
||||
// import defaultLeftToRightItemRenderer from './utils/defaultLeftToRightListRenderer';
|
||||
import defaultListRenderer from './utils/defaultListRenderer';
|
||||
const { connect } = require('react-redux');
|
||||
|
||||
const commands = {
|
||||
focusElementNoteList,
|
||||
};
|
||||
|
||||
const NoteList = (props: Props) => {
|
||||
const listRef = useRef(null);
|
||||
const itemRefs = useRef<Record<string, HTMLDivElement>>({});
|
||||
// const listRenderer = defaultLeftToRightItemRenderer;
|
||||
const listRenderer = defaultListRenderer;
|
||||
|
||||
const itemSize: Size = useMemo(() => {
|
||||
return {
|
||||
width: listRenderer.itemSize.width ? listRenderer.itemSize.width : props.size.width,
|
||||
height: listRenderer.itemSize.height,
|
||||
};
|
||||
}, [listRenderer.itemSize, props.size.width]);
|
||||
|
||||
const itemsPerLine = useMemo(() => {
|
||||
if (listRenderer.flow === ItemFlow.TopToBottom) {
|
||||
return 1;
|
||||
} else {
|
||||
return Math.max(1, Math.floor(props.size.width / itemSize.width));
|
||||
}
|
||||
}, [listRenderer.flow, props.size.width, itemSize.width]);
|
||||
|
||||
const { scrollTop, onScroll, makeItemIndexVisible } = useScroll(
|
||||
itemsPerLine,
|
||||
props.notes.length,
|
||||
itemSize,
|
||||
props.size,
|
||||
listRef,
|
||||
);
|
||||
|
||||
const [startNoteIndex, endNoteIndex, startLineIndex, endLineIndex, totalLineCount, visibleItemCount] = useVisibleRange(
|
||||
itemsPerLine,
|
||||
scrollTop,
|
||||
props.size,
|
||||
itemSize,
|
||||
props.notes.length,
|
||||
);
|
||||
|
||||
const focusNote = useFocusNote(itemRefs);
|
||||
|
||||
const moveNote = useMoveNote(
|
||||
props.notesParentType,
|
||||
props.noteSortOrder,
|
||||
props.selectedNoteIds,
|
||||
props.selectedFolderId,
|
||||
props.uncompletedTodosOnTop,
|
||||
props.showCompletedTodos,
|
||||
props.notes,
|
||||
);
|
||||
|
||||
const renderedNotes = useRenderedNotes(
|
||||
startNoteIndex,
|
||||
endNoteIndex,
|
||||
props.notes,
|
||||
props.selectedNoteIds,
|
||||
listRenderer,
|
||||
props.highlightedWords,
|
||||
props.watchedNoteFiles,
|
||||
);
|
||||
|
||||
const noteItemStyle = useMemo(() => {
|
||||
return {
|
||||
width: 'auto',
|
||||
height: itemSize.height,
|
||||
};
|
||||
}, [itemSize.height]);
|
||||
|
||||
const noteListStyle = useMemo(() => {
|
||||
return {
|
||||
width: props.size.width,
|
||||
height: props.size.height,
|
||||
};
|
||||
}, [props.size]);
|
||||
|
||||
const onNoteClick = useOnNoteClick(props.dispatch, focusNote);
|
||||
|
||||
const onKeyDown = useOnKeyDown(
|
||||
props.selectedNoteIds,
|
||||
moveNote,
|
||||
makeItemIndexVisible,
|
||||
focusNote,
|
||||
props.notes,
|
||||
props.dispatch,
|
||||
visibleItemCount,
|
||||
props.notes.length,
|
||||
listRenderer.flow,
|
||||
itemsPerLine,
|
||||
);
|
||||
|
||||
useItemCss(listRenderer.itemCss);
|
||||
|
||||
useEffect(() => {
|
||||
CommandService.instance().registerRuntime(commands.focusElementNoteList.declaration.name, commands.focusElementNoteList.runtime(focusNote));
|
||||
return () => {
|
||||
CommandService.instance().unregisterRuntime(commands.focusElementNoteList.declaration.name);
|
||||
};
|
||||
}, [focusNote]);
|
||||
|
||||
const onItemContextMenu = useOnContextMenu(
|
||||
props.selectedNoteIds,
|
||||
props.selectedFolderId,
|
||||
props.notes,
|
||||
props.dispatch,
|
||||
props.watchedNoteFiles,
|
||||
props.plugins,
|
||||
props.customCss,
|
||||
);
|
||||
|
||||
const { onDragStart, onDragOver, onDrop, dragOverTargetNoteIndex } = useDragAndDrop(props.parentFolderIsReadOnly,
|
||||
props.selectedNoteIds,
|
||||
props.selectedFolderId,
|
||||
listRef,
|
||||
scrollTop,
|
||||
itemSize,
|
||||
props.notesParentType,
|
||||
props.noteSortOrder,
|
||||
props.uncompletedTodosOnTop,
|
||||
props.showCompletedTodos,
|
||||
listRenderer.flow,
|
||||
itemsPerLine,
|
||||
);
|
||||
|
||||
const previousSelectedNoteIds = usePrevious(props.selectedNoteIds, []);
|
||||
const previousNoteCount = usePrevious(props.notes.length, 0);
|
||||
const previousVisible = usePrevious(props.visible, false);
|
||||
|
||||
useEffect(() => {
|
||||
if (previousSelectedNoteIds !== props.selectedNoteIds && props.selectedNoteIds.length === 1) {
|
||||
const id = props.selectedNoteIds[0];
|
||||
const doRefocus = props.notes.length < previousNoteCount && !props.focusedField;
|
||||
|
||||
for (let i = 0; i < props.notes.length; i++) {
|
||||
if (props.notes[i].id === id) {
|
||||
makeItemIndexVisible(i);
|
||||
if (doRefocus) {
|
||||
const ref = itemRefs.current[id];
|
||||
if (ref) ref.focus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [makeItemIndexVisible, previousSelectedNoteIds, previousNoteCount, previousVisible, props.selectedNoteIds, props.notes, props.focusedField, props.visible]);
|
||||
|
||||
const highlightedWords = useMemo(() => {
|
||||
if (props.notesParentType === 'Search') {
|
||||
const query = BaseModel.byId(props.searches, props.selectedSearchId);
|
||||
if (query) return props.highlightedWords;
|
||||
}
|
||||
return [];
|
||||
}, [props.notesParentType, props.searches, props.selectedSearchId, props.highlightedWords]);
|
||||
|
||||
const renderEmptyList = () => {
|
||||
if (props.notes.length) return null;
|
||||
return <div className="emptylist">{props.folders.length ? _('No notes in here. Create one by clicking on "New note".') : _('There is currently no notebook. Create one by clicking on "New notebook".')}</div>;
|
||||
};
|
||||
|
||||
const renderFiller = (key: string, style: React.CSSProperties) => {
|
||||
if (!props.notes.length) return null;
|
||||
if (style.height as number <= 0) return null;
|
||||
return <div key={key} style={style}></div>;
|
||||
};
|
||||
|
||||
const renderNotes = () => {
|
||||
if (!props.notes.length) return null;
|
||||
|
||||
const output: JSX.Element[] = [];
|
||||
|
||||
for (let i = startNoteIndex; i <= endNoteIndex; i++) {
|
||||
const note = props.notes[i];
|
||||
const renderedNote = renderedNotes[note.id];
|
||||
|
||||
output.push(
|
||||
<NoteListItem
|
||||
key={note.id}
|
||||
ref={el => itemRefs.current[note.id] = el}
|
||||
index={i}
|
||||
dragIndex={dragOverTargetNoteIndex}
|
||||
noteCount={props.notes.length}
|
||||
itemSize={itemSize}
|
||||
noteHtml={renderedNote ? renderedNote.html : ''}
|
||||
noteId={note.id}
|
||||
onChange={listRenderer.onChange}
|
||||
onClick={onNoteClick}
|
||||
onContextMenu={onItemContextMenu}
|
||||
onDragStart={onDragStart}
|
||||
onDragOver={onDragOver}
|
||||
style={noteItemStyle}
|
||||
highlightedWords={highlightedWords}
|
||||
isProvisional={props.provisionalNoteIds.includes(note.id)}
|
||||
flow={listRenderer.flow}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
const topFillerHeight = startLineIndex * itemSize.height;
|
||||
const bottomFillerHeight = (totalLineCount - endLineIndex - 1) * itemSize.height;
|
||||
|
||||
const fillerBaseStyle = useMemo(() => {
|
||||
// return { width: 'auto', border: '1px solid red', backgroundColor: 'green' };
|
||||
return { width: 'auto' };
|
||||
}, []);
|
||||
|
||||
const topFillerStyle = useMemo(() => {
|
||||
return { ...fillerBaseStyle, height: topFillerHeight };
|
||||
}, [fillerBaseStyle, topFillerHeight]);
|
||||
|
||||
const bottomFillerStyle = useMemo(() => {
|
||||
return { ...fillerBaseStyle, height: bottomFillerHeight };
|
||||
}, [fillerBaseStyle, bottomFillerHeight]);
|
||||
|
||||
const notesStyle = useMemo(() => {
|
||||
const output: React.CSSProperties = {};
|
||||
|
||||
if (listRenderer.flow === ItemFlow.LeftToRight) {
|
||||
output.flexFlow = 'row wrap';
|
||||
} else {
|
||||
output.flexDirection = 'column';
|
||||
}
|
||||
|
||||
return output;
|
||||
}, [listRenderer.flow]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="note-list"
|
||||
style={noteListStyle}
|
||||
ref={listRef}
|
||||
onScroll={onScroll}
|
||||
onKeyDown={onKeyDown}
|
||||
onDrop={onDrop}
|
||||
>
|
||||
{renderEmptyList()}
|
||||
{renderFiller('top', topFillerStyle)}
|
||||
<div className="notes" style={notesStyle}>
|
||||
{renderNotes()}
|
||||
</div>
|
||||
{renderFiller('bottom', bottomFillerStyle)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
const selectedFolder: FolderEntity = state.notesParentType === 'Folder' ? BaseModel.byId(state.folders, state.selectedFolderId) : null;
|
||||
const userId = state.settings['sync.userId'];
|
||||
|
||||
return {
|
||||
notes: state.notes,
|
||||
folders: state.folders,
|
||||
selectedNoteIds: state.selectedNoteIds,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
themeId: state.settings.theme,
|
||||
notesParentType: state.notesParentType,
|
||||
searches: state.searches,
|
||||
selectedSearchId: state.selectedSearchId,
|
||||
watchedNoteFiles: state.watchedNoteFiles,
|
||||
provisionalNoteIds: state.provisionalNoteIds,
|
||||
isInsertingNotes: state.isInsertingNotes,
|
||||
noteSortOrder: state.settings['notes.sortOrder.field'],
|
||||
uncompletedTodosOnTop: state.settings.uncompletedTodosOnTop,
|
||||
showCompletedTodos: state.settings.showCompletedTodos,
|
||||
highlightedWords: state.highlightedWords,
|
||||
plugins: state.pluginService.plugins,
|
||||
customCss: state.customCss,
|
||||
focusedField: state.focusedField,
|
||||
parentFolderIsReadOnly: state.notesParentType === 'Folder' && selectedFolder ? itemIsReadOnlySync(ModelType.Folder, ItemChange.SOURCE_UNSPECIFIED, selectedFolder as ItemSlice, userId, state.shareService) : false,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(NoteList);
|
||||
@@ -0,0 +1,113 @@
|
||||
import * as React from 'react';
|
||||
import { useMemo, useState, useRef, useCallback } from 'react';
|
||||
import { AppState } from '../../app.reducer';
|
||||
import BaseModel, { ModelType } from '@joplin/lib/BaseModel';
|
||||
import NoteListItem from '../NoteListItem';
|
||||
import styled from 'styled-components';
|
||||
import ItemList from '../ItemList';
|
||||
const { connect } = require('react-redux');
|
||||
import { Props } from './utils/types';
|
||||
import { itemIsReadOnlySync, ItemSlice } from '@joplin/lib/models/utils/readOnly';
|
||||
import { FolderEntity } from '@joplin/lib/services/database/types';
|
||||
import ItemChange from '@joplin/lib/models/ItemChange';
|
||||
|
||||
const StyledRoot = styled.div``;
|
||||
|
||||
const NoteListComponent = (props: Props) => {
|
||||
const [width] = useState(0);
|
||||
|
||||
const itemHeight = 34;
|
||||
|
||||
const noteListRef = useRef(null);
|
||||
const itemListRef = useRef(null);
|
||||
|
||||
const style = useMemo(() => {
|
||||
return {};
|
||||
}, []);
|
||||
|
||||
const renderItem = useCallback((item: any, index: number) => {
|
||||
return <NoteListItem
|
||||
key={item.id}
|
||||
style={style}
|
||||
item={item}
|
||||
index={index}
|
||||
themeId={props.themeId}
|
||||
width={width}
|
||||
height={itemHeight}
|
||||
dragItemIndex={0}
|
||||
highlightedWords={[]}
|
||||
isProvisional={props.provisionalNoteIds.includes(item.id)}
|
||||
isSelected={props.selectedNoteIds.indexOf(item.id) >= 0}
|
||||
isWatched={props.watchedNoteFiles.indexOf(item.id) < 0}
|
||||
itemCount={props.notes.length}
|
||||
onCheckboxClick={() => {}}
|
||||
onDragStart={()=>{}}
|
||||
onNoteDragOver={()=>{}}
|
||||
onTitleClick={() => {}}
|
||||
onContextMenu={() => {}}
|
||||
draggable={!props.parentFolderIsReadOnly}
|
||||
/>;
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [style, props.themeId, width, itemHeight, props.provisionalNoteIds, props.selectedNoteIds, props.watchedNoteFiles,
|
||||
props.notes,
|
||||
props.notesParentType,
|
||||
props.searches,
|
||||
props.selectedSearchId,
|
||||
props.highlightedWords,
|
||||
props.parentFolderIsReadOnly,
|
||||
]);
|
||||
const renderItemList = () => {
|
||||
if (!props.notes.length) return null;
|
||||
|
||||
return (
|
||||
<ItemList
|
||||
ref={itemListRef}
|
||||
disabled={props.isInsertingNotes}
|
||||
itemHeight={32}
|
||||
className={'note-list'}
|
||||
items={props.notes}
|
||||
style={props.size}
|
||||
itemRenderer={renderItem}
|
||||
onKeyDown={() => {}}
|
||||
onNoteDrop={()=>{}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
if (!props.size) throw new Error('props.size is required');
|
||||
|
||||
return (
|
||||
<StyledRoot ref={noteListRef}>
|
||||
{renderItemList()}
|
||||
</StyledRoot>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
const selectedFolder: FolderEntity = state.notesParentType === 'Folder' ? BaseModel.byId(state.folders, state.selectedFolderId) : null;
|
||||
const userId = state.settings['sync.userId'];
|
||||
|
||||
return {
|
||||
notes: state.notes,
|
||||
folders: state.folders,
|
||||
selectedNoteIds: state.selectedNoteIds,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
themeId: state.settings.theme,
|
||||
notesParentType: state.notesParentType,
|
||||
searches: state.searches,
|
||||
selectedSearchId: state.selectedSearchId,
|
||||
watchedNoteFiles: state.watchedNoteFiles,
|
||||
provisionalNoteIds: state.provisionalNoteIds,
|
||||
isInsertingNotes: state.isInsertingNotes,
|
||||
noteSortOrder: state.settings['notes.sortOrder.field'],
|
||||
uncompletedTodosOnTop: state.settings.uncompletedTodosOnTop,
|
||||
showCompletedTodos: state.settings.showCompletedTodos,
|
||||
highlightedWords: state.highlightedWords,
|
||||
plugins: state.pluginService.plugins,
|
||||
customCss: state.customCss,
|
||||
focusedField: state.focusedField,
|
||||
parentFolderIsReadOnly: state.notesParentType === 'Folder' && selectedFolder ? itemIsReadOnlySync(ModelType.Folder, ItemChange.SOURCE_UNSPECIFIED, selectedFolder as ItemSlice, userId, state.shareService) : false,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(NoteListComponent);
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { stateUtils } from '@joplin/lib/reducer';
|
||||
import { itemAnchorRef } from '../NoteList';
|
||||
import { FocusNote } from '../utils/useFocusNote';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'focusElementNoteList',
|
||||
@@ -9,15 +9,11 @@ export const declaration: CommandDeclaration = {
|
||||
parentLabel: () => _('Focus'),
|
||||
};
|
||||
|
||||
export const runtime = (): CommandRuntime => {
|
||||
export const runtime = (focusNote: FocusNote): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context: CommandContext, noteId: string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
if (noteId) {
|
||||
const ref = itemAnchorRef(noteId);
|
||||
if (ref) ref.focus();
|
||||
}
|
||||
focusNote(noteId);
|
||||
},
|
||||
enabledCondition: 'noteListHasNotes',
|
||||
};
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
.note-list {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--joplin-background-color3);
|
||||
border-right: 1px solid var(--joplin-divider-color);
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
> .notes {
|
||||
display: flex;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
> .emptylist {
|
||||
padding: 10px;
|
||||
font-size: var(--joplin-font-size);
|
||||
color: var(--joplin-color);
|
||||
background-color: var(--joplin-background-color);
|
||||
font-family: var(--joplin-font-family);
|
||||
}
|
||||
}
|
||||
|
||||
.note-list-item {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.note-list-item-wrapper {
|
||||
border-color: var(--joplin-color);
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
|
||||
> .dragcursor {
|
||||
background-color: var(--joplin-color);
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.note-list-item-wrapper.-provisional {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { FolderEntity, NoteEntity } from '@joplin/lib/services/database/types';
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
|
||||
export interface Props {
|
||||
themeId: any;
|
||||
selectedNoteIds: string[];
|
||||
notes: NoteEntity[];
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
dispatch: Function;
|
||||
watchedNoteFiles: any[];
|
||||
plugins: PluginStates;
|
||||
selectedFolderId: string;
|
||||
customCss: string;
|
||||
notesParentType: string;
|
||||
noteSortOrder: string;
|
||||
uncompletedTodosOnTop: boolean;
|
||||
showCompletedTodos: boolean;
|
||||
resizableLayoutEventEmitter: any;
|
||||
isInsertingNotes: boolean;
|
||||
folders: FolderEntity[];
|
||||
size: any;
|
||||
searches: any[];
|
||||
selectedSearchId: string;
|
||||
highlightedWords: string[];
|
||||
provisionalNoteIds: string[];
|
||||
visible: boolean;
|
||||
focusedField: string;
|
||||
parentFolderIsReadOnly: boolean;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import bridge from '../../../services/bridge';
|
||||
|
||||
const canManuallySortNotes = (notesParentType: string, noteSortOrder: string) => {
|
||||
if (notesParentType !== 'Folder') return false;
|
||||
|
||||
if (noteSortOrder !== 'order') {
|
||||
const doIt = bridge().showConfirmMessageBox(_('To manually sort the notes, the sort order must be changed to "%s" in the menu "%s" > "%s"', _('Custom order'), _('View'), _('Sort notes by')), {
|
||||
buttons: [_('Do it now'), _('Cancel')],
|
||||
});
|
||||
if (!doIt) return false;
|
||||
|
||||
Setting.setValue('notes.sortOrder.field', 'order');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export default canManuallySortNotes;
|
||||
@@ -0,0 +1,165 @@
|
||||
import { MarkupLanguage, MarkupToHtml } from '@joplin/renderer';
|
||||
import { ItemFlow, ListRenderer } from './types';
|
||||
|
||||
interface Props {
|
||||
note: {
|
||||
id: string;
|
||||
title: string;
|
||||
is_todo: number;
|
||||
todo_completed: number;
|
||||
body: string;
|
||||
};
|
||||
item: {
|
||||
size: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
selected: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const defaultLeftToRightItemRenderer: ListRenderer = {
|
||||
flow: ItemFlow.LeftToRight,
|
||||
|
||||
itemSize: {
|
||||
width: 150,
|
||||
height: 150,
|
||||
},
|
||||
|
||||
dependencies: [
|
||||
'item.selected',
|
||||
'item.size.width',
|
||||
'item.size.height',
|
||||
'note.body',
|
||||
'note.id',
|
||||
'note.is_shared',
|
||||
'note.is_todo',
|
||||
'note.isWatched',
|
||||
'note.titleHtml',
|
||||
'note.todo_completed',
|
||||
],
|
||||
|
||||
itemCss: // css
|
||||
`
|
||||
&:before {
|
||||
content: '';
|
||||
border-bottom: 1px solid var(--joplin-divider-color);
|
||||
width: 90%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 5%;
|
||||
}
|
||||
|
||||
> .content.-selected {
|
||||
background-color: var(--joplin-selected-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--joplin-background-color-hover3);
|
||||
}
|
||||
|
||||
> .content {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
align-items: flex-start;
|
||||
overflow-y: hidden;
|
||||
flex-direction: column;
|
||||
user-select: none;
|
||||
|
||||
> .checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> input {
|
||||
margin: 0px 10px 1px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
> .title {
|
||||
font-family: var(--joplin-font-family);
|
||||
font-size: var(--joplin-font-size);
|
||||
color: var(--joplin-color);
|
||||
cursor: default;
|
||||
flex: 0;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 8px;
|
||||
|
||||
> .checkbox {
|
||||
margin: 0 6px 0 0;
|
||||
}
|
||||
|
||||
> .watchedicon {
|
||||
display: none;
|
||||
padding-right: 4px;
|
||||
color: var(--joplin-color);
|
||||
}
|
||||
|
||||
> .titlecontent {
|
||||
word-break: break-all;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
> .preview {
|
||||
overflow-y: hidden;
|
||||
font-family: var(--joplin-font-family);
|
||||
font-size: var(--joplin-font-size);
|
||||
color: var(--joplin-color);
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
> .content.-shared {
|
||||
> .title {
|
||||
color: var(--joplin-color-warn3);
|
||||
}
|
||||
}
|
||||
|
||||
> .content.-completed {
|
||||
> .title {
|
||||
opacity: 0.5;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
> .content.-watched {
|
||||
> .title {
|
||||
> .watchedicon {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
||||
itemTemplate: // html
|
||||
`
|
||||
<div class="content {{#item.selected}}-selected{{/item.selected}} {{#note.is_shared}}-shared{{/note.is_shared}} {{#note.todo_completed}}-completed{{/note.todo_completed}} {{#note.isWatched}}-watched{{/note.isWatched}}">
|
||||
<div style="width: {{titleWidth}}px;" class="title" data-id="{{note.id}}">
|
||||
{{#note.is_todo}}
|
||||
<input class="checkbox" data-id="todo-checkbox" type="checkbox" {{#note.todo_completed}}checked="checked"{{/note.todo_completed}}>
|
||||
{{/note.is_todo}}
|
||||
<i class="watchedicon fa fa-share-square"></i>
|
||||
<div class="titlecontent">{{{note.titleHtml}}}</div>
|
||||
</div>
|
||||
<div class="preview">{{notePreview}}</div>
|
||||
</div>
|
||||
`,
|
||||
|
||||
onRenderNote: async (props: Props) => {
|
||||
const markupToHtml_ = new MarkupToHtml();
|
||||
|
||||
return {
|
||||
...props,
|
||||
notePreview: markupToHtml_.stripMarkup(MarkupLanguage.Markdown, props.note.body).substring(0, 200),
|
||||
titleWidth: props.item.size.width - 32,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default defaultLeftToRightItemRenderer;
|
||||
@@ -0,0 +1,134 @@
|
||||
import { ItemFlow, ListRenderer } from './types';
|
||||
|
||||
interface Props {
|
||||
note: {
|
||||
id: string;
|
||||
title: string;
|
||||
is_todo: number;
|
||||
todo_completed: number;
|
||||
};
|
||||
item: {
|
||||
size: {
|
||||
height: number;
|
||||
};
|
||||
selected: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const defaultItemRenderer: ListRenderer = {
|
||||
flow: ItemFlow.TopToBottom,
|
||||
|
||||
itemSize: {
|
||||
width: 0,
|
||||
height: 34,
|
||||
},
|
||||
|
||||
dependencies: [
|
||||
'item.selected',
|
||||
'item.size.height',
|
||||
'note.id',
|
||||
'note.is_shared',
|
||||
'note.is_todo',
|
||||
'note.isWatched',
|
||||
'note.titleHtml',
|
||||
'note.todo_completed',
|
||||
],
|
||||
|
||||
itemCss: // css
|
||||
`
|
||||
&:before {
|
||||
content: '';
|
||||
border-bottom: 1px solid var(--joplin-divider-color);
|
||||
width: 90%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 5%;
|
||||
}
|
||||
|
||||
> .content.-selected {
|
||||
background-color: var(--joplin-selected-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--joplin-background-color-hover3);
|
||||
}
|
||||
|
||||
> .content {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-left: 16px;
|
||||
|
||||
> .checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> input {
|
||||
margin: 0px 10px 1px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
> .title {
|
||||
font-family: var(--joplin-font-family);
|
||||
font-size: var(--joplin-font-size);
|
||||
text-decoration: none;
|
||||
color: var(--joplin-color);
|
||||
cursor: default;
|
||||
white-space: nowrap;
|
||||
flex: 1 1 0%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
> .watchedicon {
|
||||
display: none;
|
||||
padding-right: 4px;
|
||||
color: var(--joplin-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .content.-shared {
|
||||
> .title {
|
||||
color: var(--joplin-color-warn3);
|
||||
}
|
||||
}
|
||||
|
||||
> .content.-completed {
|
||||
> .title {
|
||||
opacity: 0.5;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
> .content.-watched {
|
||||
> .title {
|
||||
> .watchedicon {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
||||
itemTemplate: // html
|
||||
`
|
||||
<div class="content {{#item.selected}}-selected{{/item.selected}} {{#note.is_shared}}-shared{{/note.is_shared}} {{#note.todo_completed}}-completed{{/note.todo_completed}} {{#note.isWatched}}-watched{{/note.isWatched}}">
|
||||
{{#note.is_todo}}
|
||||
<div class="checkbox">
|
||||
<input data-id="todo-checkbox" type="checkbox" {{#note.todo_completed}}checked="checked"{{/note.todo_completed}}>
|
||||
</div>
|
||||
{{/note.is_todo}}
|
||||
<div class="title" data-id="{{note.id}}">
|
||||
<i class="watchedicon fa fa-share-square"></i>
|
||||
<span>{{{note.titleHtml}}}</span>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
|
||||
onRenderNote: async (props: Props) => {
|
||||
return props;
|
||||
},
|
||||
};
|
||||
|
||||
export default defaultItemRenderer;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user