1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-03-03 09:27:01 +02:00

Compare commits

..

31 Commits

Author SHA1 Message Date
Joplin Bot
36b25a9517 Doc: Auto-update documentation
Auto-updated using release-website.sh
2026-03-03 02:18:36 +00:00
Laurent Cozic
b3e0575361 iOS 13.6.2 2026-03-02 22:16:27 +00:00
Joplin Bot
f9f40b3c9b Doc: Auto-update documentation
Auto-updated using release-website.sh
2026-03-02 18:59:44 +00:00
Laurent Cozic
b59721f4b3 Android 3.6.13 2026-03-02 17:47:33 +00:00
Laurent Cozic
891ab3e317 Desktop release v3.6.3 2026-03-02 17:26:21 +00:00
Laurent Cozic
0e156796bc Desktop: Fix editor plugins receiving stale note body during navigation (#14513) 2026-03-01 18:54:42 +00:00
renovate[bot]
f2b558cb75 Update dependency react-native-localize to v3.6.0 (#14511)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-01 15:47:39 +00:00
renovate[bot]
322657ef72 Update dependency gettext-extractor to v4.0.3 (#14508)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-01 13:17:45 +00:00
renovate[bot]
c1e99afd2e Update dependency samlify to v2.10.2 (#14509)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-01 13:12:44 +00:00
Sriram Varun Kumar
b3822e2700 CLI: Fixes #13158: Fix null crash in e2ee decrypt command (#14461)
Co-authored-by: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com>
2026-03-01 12:21:21 +00:00
Nagmani Upadhyay
a43f46fc01 Mobile: Fixes #11793: uses consistent padding in plugin info dialog (#14466) 2026-03-01 12:20:22 +00:00
Kanishka..
50a26b63c8 Desktop: Fixes #13679: Fix sidebar scroll jump when expanding/collapsing folders (#14467) 2026-03-01 12:20:02 +00:00
Harsh Gupta
02c1c75587 Desktop: Fixes #12401: copying from markdown preview including theme background colour (#14474) 2026-03-01 12:11:16 +00:00
Sriram Varun Kumar
345632324d Desktop: Fix UI freeze when closing plugin dialog with Escape key (#14477)
Co-authored-by: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com>
2026-03-01 12:08:58 +00:00
mrjo118
8073e03daf Mobile: Prevent race condition when refreshing note contents on mobile (#14486) 2026-03-01 11:58:15 +00:00
Laurent Cozic
7feb953c70 Chore: Trying to use CodeRabbit to validate pull request descriptions 2026-03-01 11:57:51 +00:00
yentropysack
8e895fb2c0 Desktop: Fixes #12385: Copy and paste from markdown preview includes search highlight effect (#14493) 2026-03-01 11:24:41 +00:00
Harsh Gupta
ee97c41309 Desktop, Mobile: Resolves #12220: Add new option to disable the Joplin icon for internal note links (#14503) 2026-03-01 11:16:14 +00:00
Joplin Bot
c9a55563b5 Doc: Auto-update documentation
Auto-updated using release-website.sh
2026-03-01 02:33:23 +00:00
GeorgiPopovIT
39f5dc8c95 All: Translation: Update bg_BG.po (#14505) 2026-02-28 19:48:02 -05:00
Joplin Bot
9256ab197a Doc: Auto-update documentation
Auto-updated using release-website.sh
2026-02-28 18:44:15 +00:00
Laurent Cozic
ee2869da86 Doc: Fix sponsor link 2026-02-28 17:51:48 +00:00
Henry Heino
e11441cfbc Web: Move web app out of beta (#14497) 2026-02-28 12:01:50 +00:00
Yugal Kaushik
2b5be639ce All: Resolves #14336: Store note history settings in sync info (#14449)
Signed-off-by: yugalkaushik <yugalkaushik14@gmail.com>
2026-02-28 12:01:23 +00:00
GeorgiPopovIT
cf3d7f5b88 All: Translation: Update bg_BG.po (#14482) 2026-02-27 12:32:19 -05:00
Mihai Vasiliu
595452f30e All: Translation: Update ro_RO.po and ro_MD.po (#14470) 2026-02-27 12:30:11 -05:00
Henry Heino
33c4029547 Server: Performance: Improve performance of share maintenance task (#14484) 2026-02-27 14:31:48 +00:00
Laurent Cozic
eb238efc7b Transcribe v3.6.6 2026-02-26 21:08:39 +00:00
Laurent Cozic
513341f103 Transcribe: Upgraded image to node:24-bookworm to fix build issue 2026-02-26 21:08:18 +00:00
Henry Heino
65b7c4be26 Doc: Link to GSoC pull request guidelines in the pull request template (#14478) 2026-02-26 17:45:58 +00:00
Henry Heino
344a3c2605 Server: Fixes #14107: Update item ownership information when the original owner no longer has access (#14469) 2026-02-26 15:25:22 +00:00
70 changed files with 3444 additions and 3594 deletions

View File

@@ -85,6 +85,22 @@ reviews:
instructions: "Apply when the PR modifies files under packages/app-web/. Or when the PR modifies files under packages/app-mobile and the change is specific to the web app only"
- label: "windows"
instructions: "Apply when the PR is mainly about changes specific to Windows"
pre_merge_checks:
description:
mode: "warning"
custom_checks:
- name: "PR Description Must Follow Guidelines"
mode: "error"
instructions: |
Fail if the pull request description does not include clear sections for:
- Problem or user-impact description
- A high-level Solution explanation
- Any Test Plan or verification steps
The description should align with our PR guidelines
at https://github.com/joplin/gsoc/blob/master/pull_request_guidelines.md
and should not just restate the diff or implementation details.
knowledge_base:
code_guidelines:
enabled: true

View File

@@ -695,8 +695,6 @@ packages/app-mobile/components/ExtendedWebView/index.js
packages/app-mobile/components/ExtendedWebView/index.web.js
packages/app-mobile/components/ExtendedWebView/types.js
packages/app-mobile/components/ExtendedWebView/utils/useCss.js
packages/app-mobile/components/FeedbackBanner.test.js
packages/app-mobile/components/FeedbackBanner.js
packages/app-mobile/components/FolderPicker.js
packages/app-mobile/components/Icon.js
packages/app-mobile/components/IconButton.js
@@ -741,7 +739,6 @@ packages/app-mobile/components/ScreenHeader/Menu.js
packages/app-mobile/components/ScreenHeader/WarningBanner.test.js
packages/app-mobile/components/ScreenHeader/WarningBanner.js
packages/app-mobile/components/ScreenHeader/WarningBox.js
packages/app-mobile/components/ScreenHeader/WebBetaButton.js
packages/app-mobile/components/ScreenHeader/index.js
packages/app-mobile/components/SearchInput.js
packages/app-mobile/components/SelectDateTimeDialog.js
@@ -1434,6 +1431,7 @@ packages/lib/services/AlarmServiceDriverNode.js
packages/lib/services/BaseService.js
packages/lib/services/CommandService.test.js
packages/lib/services/CommandService.js
packages/lib/services/DecryptionWorker.test.js
packages/lib/services/DecryptionWorker.js
packages/lib/services/ExternalEditWatcher.js
packages/lib/services/ExternalEditWatcher/utils.js

View File

@@ -1,6 +1,12 @@
<!--
Please prefix the title with the platform you are targetting:
Before contributing, please read the contribution guidelines: https://github.com/laurent22/joplin/blob/dev/readme/dev/index.md
If this is a Google Summer of Code pull request, please read the [GSoC pull request guidelines](https://github.com/joplin/gsoc/blob/master/pull_request_guidelines.md).
---
**Pull request title**: Please prefix the title with the platform you are targetting.
Here are some examples of good titles:
@@ -20,6 +26,4 @@ If it's not related to any platform (such as a translation, change to the docume
Then please append the issue that you've addressed or fixed. Use "Resolves #123" for new features or improvements and "Fixes #123" for bug fixes.
AND PLEASE READ THE GUIDE: https://github.com/laurent22/joplin/blob/dev/readme/dev/index.md
-->

4
.gitignore vendored
View File

@@ -668,8 +668,6 @@ packages/app-mobile/components/ExtendedWebView/index.js
packages/app-mobile/components/ExtendedWebView/index.web.js
packages/app-mobile/components/ExtendedWebView/types.js
packages/app-mobile/components/ExtendedWebView/utils/useCss.js
packages/app-mobile/components/FeedbackBanner.test.js
packages/app-mobile/components/FeedbackBanner.js
packages/app-mobile/components/FolderPicker.js
packages/app-mobile/components/Icon.js
packages/app-mobile/components/IconButton.js
@@ -714,7 +712,6 @@ packages/app-mobile/components/ScreenHeader/Menu.js
packages/app-mobile/components/ScreenHeader/WarningBanner.test.js
packages/app-mobile/components/ScreenHeader/WarningBanner.js
packages/app-mobile/components/ScreenHeader/WarningBox.js
packages/app-mobile/components/ScreenHeader/WebBetaButton.js
packages/app-mobile/components/ScreenHeader/index.js
packages/app-mobile/components/SearchInput.js
packages/app-mobile/components/SelectDateTimeDialog.js
@@ -1407,6 +1404,7 @@ packages/lib/services/AlarmServiceDriverNode.js
packages/lib/services/BaseService.js
packages/lib/services/CommandService.test.js
packages/lib/services/CommandService.js
packages/lib/services/DecryptionWorker.test.js
packages/lib/services/DecryptionWorker.js
packages/lib/services/ExternalEditWatcher.js
packages/lib/services/ExternalEditWatcher/utils.js

View File

@@ -1,4 +1,4 @@
FROM node:24-bullseye
FROM node:24-bookworm
RUN apt-get update \
&& apt-get install -y \

View File

@@ -31,7 +31,7 @@ Please see the [donation page](https://github.com/laurent22/joplin/blob/dev/read
# Sponsors
<!-- SPONSORS-ORG -->
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://citricsheep.com"><img title="Citric Sheep" width="256" src="https://joplinapp.org/images/sponsors/CitricSheep.png"/></a> <a href="https://sorted.travel/?utm_source=joplinapp"><img title="Sorted Travel" width="256" src="https://joplinapp.org/images/sponsors/SortedTravel.png"/></a> <a href="https://celebian.com"><img title="Celebian" width="256" src="https://joplinapp.org/images/sponsors/Celebian.png"/></a> <a href="https://bestkru.com"><img title="BestKru" width="256" src="https://joplinapp.org/images/sponsors/BestKru.png"/></a> <a href="https://www.socialfollowers.uk/buy-tiktok-followers/"><img title="Social Followers" width="256" src="https://joplinapp.org/images/sponsors/SocialFollowers.png"/></a> <a href="https://stormlikes.com/"><img title="Stormlikes" width="256" src="https://joplinapp.org/images/sponsors/Stormlikes.png"/></a> <a href="https://route4me.com"><img title="Route4Me" width="256" src="https://joplinapp.org/images/sponsors/Route4Me.png"/></a> <a href="https://topagency.webflow.io"><img title="WebDesignAgency" width="256" src="https://joplinapp.org/images/sponsors/WebDesignAgency.png" alt="topagency"/></a> <a href="https://essayshark.com"><img title="EssayShark - essay writers for hire" width="256" src="https://joplinapp.org/images/sponsors/EssayShark.png" alt="EssayShark - essay writers for hire"/></a> <a href="https://pokieslab1.com/real-money-pokies/"><img title="Australian Real Money Pokies" width="256" src="https://joplinapp.org/images/sponsors/PokiesLab.png" alt="Australian Real Money Pokies"/></a> <a href="https://pokiesman1.net/real-money-pokies/"><img title="Australian Real Money Pokies" width="256" src="https://joplinapp.org/images/sponsors/Pokiesman.png" alt="Australian Real Money Pokies"/></a> <a href="https://socialkings.online"><img title="Boost your reach and buy real followers" width="256" src="https://joplinapp.org/images/sponsors/SocialKings.png" alt="Boost your reach and buy real followers"/></a> <a href="https://essayservice.com/"><img title="For those in need of immediate academic assistance, EssayService offers a fast and reliable service to write my essay for me now, ensuring high-quality results within tight deadlines" width="256" src="https://joplinapp.org/images/sponsors/EssayService.png" alt="For those in need of immediate academic assistance, EssayService offers a fast and reliable service to write my essay for me now, ensuring high-quality results within tight deadlines"/></a> <a href="&nbsp;https://thenationonlineng.net/casino-en-ligne/casino-en-ligne-payant-au-canada/"><img title="casino en ligne le plus payant" width="256" src="https://joplinapp.org/images/sponsors/TheNationOnline.jpg" alt="casino en ligne le plus payant"/></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://citricsheep.com"><img title="Citric Sheep" width="256" src="https://joplinapp.org/images/sponsors/CitricSheep.png"/></a> <a href="https://sorted.travel/?utm_source=joplinapp"><img title="Sorted Travel" width="256" src="https://joplinapp.org/images/sponsors/SortedTravel.png"/></a> <a href="https://celebian.com"><img title="Celebian" width="256" src="https://joplinapp.org/images/sponsors/Celebian.png"/></a> <a href="https://bestkru.com"><img title="BestKru" width="256" src="https://joplinapp.org/images/sponsors/BestKru.png"/></a> <a href="https://www.socialfollowers.uk/buy-tiktok-followers/"><img title="Social Followers" width="256" src="https://joplinapp.org/images/sponsors/SocialFollowers.png"/></a> <a href="https://stormlikes.com/"><img title="Stormlikes" width="256" src="https://joplinapp.org/images/sponsors/Stormlikes.png"/></a> <a href="https://route4me.com"><img title="Route4Me" width="256" src="https://joplinapp.org/images/sponsors/Route4Me.png"/></a> <a href="https://topagency.webflow.io"><img title="WebDesignAgency" width="256" src="https://joplinapp.org/images/sponsors/WebDesignAgency.png" alt="topagency"/></a> <a href="https://essayshark.com"><img title="EssayShark - essay writers for hire" width="256" src="https://joplinapp.org/images/sponsors/EssayShark.png" alt="EssayShark - essay writers for hire"/></a> <a href="https://pokieslab1.com/real-money-pokies/"><img title="Australian Real Money Pokies" width="256" src="https://joplinapp.org/images/sponsors/PokiesLab.png" alt="Australian Real Money Pokies"/></a> <a href="https://pokiesman1.net/real-money-pokies/"><img title="Australian Real Money Pokies" width="256" src="https://joplinapp.org/images/sponsors/Pokiesman.png" alt="Australian Real Money Pokies"/></a> <a href="https://socialkings.online"><img title="Boost your reach and buy real followers" width="256" src="https://joplinapp.org/images/sponsors/SocialKings.png" alt="Boost your reach and buy real followers"/></a> <a href="https://essayservice.com/"><img title="For those in need of immediate academic assistance, EssayService offers a fast and reliable service to write my essay for me now, ensuring high-quality results within tight deadlines" width="256" src="https://joplinapp.org/images/sponsors/EssayService.png" alt="For those in need of immediate academic assistance, EssayService offers a fast and reliable service to write my essay for me now, ensuring high-quality results within tight deadlines"/></a> <a href="https://thenationonlineng.net/casino-en-ligne/casino-en-ligne-payant-au-canada/"><img title="casino en ligne le plus payant" width="256" src="https://joplinapp.org/images/sponsors/TheNationOnline.jpg" alt="casino en ligne le plus payant"/></a>
<!-- SPONSORS-ORG -->
* * *

View File

@@ -646,6 +646,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
useCustomPdfViewer: props.useCustomPdfViewer,
noteId: props.noteId,
vendorDir: bridge().vendorDir(),
showNoteLinkIcon: props.showNoteLinkIcon,
}));
if (cancelled) return;
@@ -666,7 +667,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
shim.clearTimeout(timeoutId);
};
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [props.content, props.contentKey, renderedBodyContentKey, props.contentMarkupLanguage, props.visiblePanes, props.resourceInfos, props.markupToHtml]);
}, [props.content, props.contentKey, renderedBodyContentKey, props.contentMarkupLanguage, props.visiblePanes, props.resourceInfos, props.markupToHtml, props.showNoteLinkIcon]);
useEffect(() => {
if (!webviewReady) return;

View File

@@ -222,6 +222,7 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
noteId: props.noteId,
vendorDir: bridge().vendorDir(),
globalSettings: getGlobalSettings(Setting),
showNoteLinkIcon: props.showNoteLinkIcon,
}));
if (cancelled) return;
@@ -244,7 +245,7 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
}, [
props.content, props.contentKey, renderedBodyContentKey, props.contentMarkupLanguage,
props.visiblePanes, props.resourceInfos, props.markupToHtml, props.contentMaxWidth,
props.noteId, props.useCustomPdfViewer,
props.noteId, props.useCustomPdfViewer, props.showNoteLinkIcon,
]);
useEffect(() => {

View File

@@ -474,6 +474,7 @@ function NoteEditorContent(props: NoteEditorProps) {
noteId: props.noteId,
watchedNoteFiles: props.watchedNoteFiles,
enableHtmlToMarkdownBanner: props.enableHtmlToMarkdownBanner,
showNoteLinkIcon: props.showNoteLinkIcon,
};
let editor = null;
@@ -767,6 +768,7 @@ const mapStateToProps = (state: AppState, ownProps: ConnectProps) => {
searchResults: state.searchResults,
enableHtmlToMarkdownBanner: state.settings['editor.enableHtmlToMarkdownBanner'],
enableInEditorRendering: state.settings['editor.inlineRendering'],
showNoteLinkIcon: state.settings['notes.showNoteLinkIcon'],
};
};

View File

@@ -75,6 +75,7 @@ export interface NoteEditorProps {
bodyEditor: NoteBodyEditorType;
startupPluginsLoaded: boolean;
enableHtmlToMarkdownBanner: boolean;
showNoteLinkIcon: boolean;
}
export interface NoteBodyEditorRef {
@@ -156,6 +157,7 @@ export interface NoteBodyEditorProps {
useCustomPdfViewer: boolean;
watchedNoteFiles: string[];
enableHtmlToMarkdownBanner: boolean;
showNoteLinkIcon: boolean;
}
export interface NoteBodyEditorPropsAndRef extends NoteBodyEditorProps {

View File

@@ -95,12 +95,18 @@ const useConnectToEditorPlugin = ({
}, [activeEditorView, editorPluginHandler]);
const formNoteBody = formNote.body;
const formNoteId = formNote.id;
useEffect(() => {
// Don't emit updates when formNote hasn't loaded the current note yet.
// This can happen during note navigation when effectiveNoteId updates
// immediately but formNote still contains the previous note's data.
if (formNoteId !== effectiveNoteId) return;
editorPluginHandler.emitUpdate({
noteId: effectiveNoteId,
newBody: formNoteBody,
}, shownEditorViewIds);
}, [effectiveNoteId, formNoteBody, editorPluginHandler, shownEditorViewIds]);
}, [effectiveNoteId, formNoteId, formNoteBody, editorPluginHandler, shownEditorViewIds]);
};
export default useConnectToEditorPlugin;

View File

@@ -35,6 +35,7 @@ interface Props {
customCss: string;
scrollbarSize: ScrollbarSize;
fontFamily: string;
showNoteLinkIcon: boolean;
}
const useNoteContent = (
@@ -45,6 +46,7 @@ const useNoteContent = (
customCss: string,
scrollbarSize: ScrollbarSize,
fontFamily: string,
showNoteLinkIcon: boolean,
) => {
const [note, setNote] = useState<NoteEntity>(null);
@@ -75,17 +77,18 @@ const useNoteContent = (
resources: await shared.attachedResources(noteBody),
whiteBackgroundNoteRendering: markupLanguage === MarkupLanguage.Html,
globalSettings: getGlobalSettings(Setting),
showNoteLinkIcon,
});
viewerRef.current.setHtml(result.html, {
pluginAssets: result.pluginAssets,
});
}, [note, viewerRef]);
}, [note, viewerRef, markupToHtml, showNoteLinkIcon]);
return note;
};
const NoteRevisionViewerComponent: React.FC<Props> = ({ themeId, noteId, onBack, customCss, scrollbarSize, fontFamily }) => {
const NoteRevisionViewerComponent: React.FC<Props> = ({ themeId, noteId, onBack, customCss, scrollbarSize, fontFamily, showNoteLinkIcon }) => {
const helpButton_onClick = useCallback(() => {}, []);
const viewerRef = useRef<NoteViewerControl|null>(null);
const revisionListRef = useRef<HTMLSelectElement|null>(null);
@@ -96,7 +99,7 @@ const NoteRevisionViewerComponent: React.FC<Props> = ({ themeId, noteId, onBack,
const [deleting, setDeleting] = useState(false);
const note = useNoteContent(
viewerRef, currentRevId, revisions, themeId, customCss, scrollbarSize, fontFamily,
viewerRef, currentRevId, revisions, themeId, customCss, scrollbarSize, fontFamily, showNoteLinkIcon,
);
const viewer_domReady = useCallback(async () => {
@@ -229,6 +232,7 @@ const mapStateToProps = (state: AppState) => {
themeId: state.settings.theme,
scrollbarSize: state.settings['style.scrollbarSize'],
fontFamily: state.settings['style.viewer.fontFamily'],
showNoteLinkIcon: state.settings['notes.showNoteLinkIcon'],
};
};

View File

@@ -28,12 +28,19 @@ const useScrollToSelectionHandler = (
return lastSelectedItemKey.current;
}
}, [listItems, selectedIndex]);
lastSelectedItemKey.current = selectedItemKey;
const selectedIndexRef = useRef(selectedIndex);
selectedIndexRef.current = selectedIndex;
useEffect(() => {
// Skip scrolling if the selected item hasn't actually changed. When a folder is
// expanded or collapsed the selected item's index may shift, but its key stays
// the same — in that case we don't want to scroll the view.
if (selectedItemKey === lastSelectedItemKey.current) {
return;
}
lastSelectedItemKey.current = selectedItemKey;
if (!itemListRef.current || !selectedItemKey) return;
const hasFocus = !!itemListRef.current.container.contains(document.activeElement);

View File

@@ -54,12 +54,42 @@ type ItemContextMenuListener = MouseEventHandler<HTMLElement>;
const menuUtils = new MenuUtils(CommandService.instance());
// Checks whether an element is at least partially visible within a scrollable
// container by comparing their bounding rectangles.
const isElementVisibleInContainer = (element: HTMLElement, container: HTMLElement) => {
const elementRect = element.getBoundingClientRect();
const containerRect = container.getBoundingClientRect();
return elementRect.bottom > containerRect.top && elementRect.top < containerRect.bottom;
};
const focusListItem = (item: HTMLElement|null) => {
if (item) {
// Avoid scrolling to the selected item when refocusing the note list. Such a refocus
// can happen if the note list rerenders and the selection is scrolled out of view and
// can cause scroll to change unexpectedly.
focus('useOnRenderItem', item, { preventScroll: true });
const activeElement = document.activeElement instanceof HTMLElement ? document.activeElement : null;
const itemList = item.closest('.item-list');
const activeTreeItem = activeElement?.closest('[role="treeitem"]');
const focusWasLost = activeElement === document.body;
// If the currently focused element is a tree item inside the same list,
// the user is navigating with the keyboard — always allow focus to move
// to the newly selected item so arrow-key scrolling is not interrupted.
const isKeyboardNavigating = !!activeTreeItem && itemList?.contains(activeTreeItem);
// Avoid disturbing scroll while user is manually scrolling through the list.
// However, if focus was lost (activeElement -> <body>), or the user is
// navigating with the keyboard, allow re-focusing even when the selected
// item is currently out of view.
if (itemList instanceof HTMLElement && !isElementVisibleInContainer(item, itemList) && !focusWasLost && !isKeyboardNavigating) {
return;
}
// Move focus only if needed: either focus was lost, or selection changed
// to a different tree item.
if (focusWasLost || activeTreeItem !== item) {
// preventScroll: true avoids a secondary scroll caused by the focus() call
// itself when the item is near the edge of the visible area.
focus('useOnRenderItem', item, { preventScroll: true });
}
}
};
@@ -363,7 +393,12 @@ const useOnRenderItem = (props: Props) => {
multipleItemsSelected: props.selectedIndexes.length > 1,
};
const focusInList = document.hasFocus() && props.containerRef.current?.contains(document.activeElement);
const sidebarContainsFocus = props.containerRef.current?.contains(document.activeElement);
// Focus moves to <body> when the previously-focused element is removed
// from the DOM (e.g. scrolled out of the virtualized list). We still
// want to restore focus to the newly-selected item in that case.
const focusLostFromDom = document.activeElement === document.body;
const focusInList = document.hasFocus() && (sidebarContainsFocus || focusLostFromDom);
const anchorRef = (focusInList && primarySelected) ? focusListItem : noFocusListItem;
if (item.kind === ListItemType.Tag) {

View File

@@ -35,6 +35,10 @@
ul ul, ul ol, ol ul, ol ol {
margin-bottom: 0px;
}
::highlight(search-results) {
background-color: #f7d26e;
color: black;
}
</style>
</head>
@@ -503,7 +507,7 @@
if (!options) options = {};
// TODO: Add support for scriptType on mobile and CLI
CSS.highlights.clear();
if (!mark_) {
mark_ = new Mark(document.getElementById('joplin-container-content'), {
exclude: ['img'],
@@ -519,7 +523,8 @@
let selectedElement = null;
let elementIndex = 0;
const allRanges = [];
const markKeywordOptions = {};
if ('separateWordSearch' in options) markKeywordOptions.separateWordSearch = options.separateWordSearch;
@@ -527,9 +532,17 @@
try {
for (const keyword of keywords) {
markJsUtils.markKeyword(mark_, keyword, {
pregQuote: pregQuote,
replaceRegexDiacritics: replaceRegexDiacritics,
}, markKeywordOptions);
pregQuote: pregQuote,
replaceRegexDiacritics: replaceRegexDiacritics,
}, {
...markKeywordOptions,
element: 'mark-ghost',
each: (node) => {
const range = new Range();
range.selectNodeContents(node);
allRanges.push(range);
}
});
}
} catch (error) {
if (error.name !== 'SyntaxError') {
@@ -540,6 +553,10 @@
// https://github.com/laurent22/joplin/issues/7634
console.error('Error while trying to highlight words from search: ', error);
}
if (allRanges.length > 0) {
const searchResultsHighlight = new Highlight(...allRanges);
CSS.highlights.set("search-results", searchResultsHighlight);
}
}
let markLoader_ = { state: 'idle', whenDone: null };
@@ -809,6 +826,37 @@
);
}));
// By default, Chromium inlines body styles (e.g. theme background color) into the clipboard HTML.
// Intercept the copy event and write only the selected content to bypass this behaviour.
document.addEventListener('copy', webviewLib.logEnabledEventHandler(e => {
const selection = window.getSelection();
if (!selection || selection.isCollapsed) return;
if (selection.rangeCount === 0 || !e.clipboardData) return;
const range = selection.getRangeAt(0);
const wrapper = document.createElement('div');
wrapper.appendChild(range.cloneContents());
wrapper.querySelectorAll('style').forEach(s => s.remove());
const inlineTags = new Set(['STRONG', 'EM', 'CODE', 'S', 'DEL', 'INS', 'MARK', 'SUP', 'SUB', 'U', 'SPAN', 'A']);
let node = range.commonAncestorContainer;
if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
while (node && node !== document.body && node.id !== 'rendered-md' && node.id !== 'joplin-container-content') {
if (inlineTags.has(node.tagName)) {
const el = node.cloneNode(false);
while (wrapper.firstChild) el.appendChild(wrapper.firstChild);
wrapper.appendChild(el);
}
node = node.parentElement;
}
e.clipboardData.setData('text/html', wrapper.innerHTML);
e.clipboardData.setData('text/plain', selection.toString());
e.preventDefault();
}));
let lastClientWidth_ = NaN, lastClientHeight_ = NaN, lastScrollTop_ = NaN;
window.addEventListener('resize', webviewLib.logEnabledEventHandler(() => {

View File

@@ -161,7 +161,7 @@ test.describe('markdownEditor', () => {
const viewer = noteEditor.getNoteViewerFrameLocator();
await expect(viewer.locator('h1')).toHaveText('Testing');
const matches = viewer.locator('mark');
const matches = viewer.locator('mark-ghost');
await expect(matches).toHaveCount(0);
await mainWindow.keyboard.press(process.platform === 'darwin' ? 'Meta+f' : 'Control+f');
@@ -404,5 +404,35 @@ test.describe('markdownEditor', () => {
await activateMainMenuItem(electronApp, 'Redo');
await noteEditor.expectToHaveText('A');
});
});
test('copying from the preview pane should not include theme background color and should preserve bold formatting', async ({ mainWindow, electronApp }) => {
// Set dark theme so background-color would be present in clipboard without the fix
await setSettingValue(electronApp, mainWindow, 'theme', 2);
const mainScreen = await new MainScreen(mainWindow).setup();
await mainScreen.waitFor();
await mainScreen.createNewNote('Test copy formatting');
const noteEditor = mainScreen.noteEditor;
await noteEditor.focusCodeMirrorEditor();
await mainWindow.keyboard.type('**hello**');
const viewerFrame = noteEditor.getNoteViewerFrameLocator();
await expect(viewerFrame.locator('strong')).toHaveText('hello');
// Double-click selects the text node inside <strong>, not <strong> itself.
// Without the ancestor re-wrapping fix, <strong> would be dropped.
await viewerFrame.locator('strong').dblclick();
const modifier = process.platform === 'darwin' ? 'Meta' : 'Control';
await mainWindow.keyboard.press(`${modifier}+c`);
const clipboardHtml = await mainWindow.evaluate(() => {
const { clipboard } = require('electron');
return clipboard.readHTML();
});
expect(clipboardHtml).toContain('hello');
expect(clipboardHtml).not.toMatch(/background-color\s*:/i);
expect(clipboardHtml).toContain('<strong>');
});
});

View File

@@ -2,6 +2,7 @@
import { test, expect } from './util/test';
import MainScreen from './models/MainScreen';
import { msleep, Second } from '@joplin/utils/time';
import setSettingValue from './util/setSettingValue';
test.describe('pluginApi', () => {
test('the editor.setText command should update the current note (use RTE: false)', async ({ startAppWithPlugins }) => {
@@ -80,6 +81,30 @@ test.describe('pluginApi', () => {
await expectVisible(false);
});
// Regression tests for #13718
for (const method of ['Cancel button', 'Escape key'] as const) {
test(`should dismiss a plugin dialog via ${method} with isolated iframes`, async ({ startAppWithPlugins }) => {
const { app, mainWindow } = await startAppWithPlugins(['resources/test-plugins/dialogs.js']);
const mainScreen = await new MainScreen(mainWindow).setup();
await mainScreen.createNewNote('Test note');
// WebView isolation is currently behind a feature flag:
await setSettingValue(app, mainWindow, 'featureFlag.plugins.isolatePluginWebViews', true);
await mainScreen.goToAnything.runCommand(app, 'showTestDialogWithDismiss');
const dialogContent = mainScreen.dialog.locator('iframe').contentFrame();
await dialogContent.locator('p').waitFor();
if (method === 'Cancel button') {
await mainScreen.dialog.getByRole('button', { name: 'Cancel' }).click();
} else {
await mainWindow.keyboard.press('Escape');
}
await expect(mainScreen.dialog).toBeHidden();
});
}
test('should be possible to create multiple toasts with the same text from a plugin', async ({ startAppWithPlugins }) => {
const { app, mainWindow } = await startAppWithPlugins(['resources/test-plugins/showToast.js']);
const mainScreen = await new MainScreen(mainWindow).setup();

View File

@@ -48,6 +48,21 @@ joplin.plugins.register({
},
});
const dismissDialogHandle = await dialogs.create('test-dialog-with-dismiss');
await dialogs.setHtml(dismissDialogHandle, '<p>Press Escape to dismiss</p>');
await dialogs.setButtons(dismissDialogHandle, [
{ id: 'ok', title: 'Okay' },
{ id: 'cancel', title: 'Cancel' },
]);
await joplin.commands.register({
name: 'showTestDialogWithDismiss',
label: 'showTestDialogWithDismiss',
execute: async () => {
const result = await joplin.views.dialogs.open(dismissDialogHandle);
await joplin.commands.execute('editor.setText', result.id);
},
});
await joplin.commands.register({
name: 'getTestDialogVisibility',
label: 'Returns the dialog visibility state',

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "3.6.2",
"version": "3.6.3",
"description": "Joplin for Desktop",
"main": "main.bundle.js",
"private": true,

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { useRef, useCallback } from 'react';
import { useRef, useCallback, useMemo } from 'react';
import { ButtonSpec, DialogResult } from '@joplin/lib/services/plugins/api/types';
import PluginService from '@joplin/lib/services/plugins/PluginService';
import WebviewController from '@joplin/lib/services/plugins/WebviewController';
@@ -76,8 +76,16 @@ export default function UserWebviewDialog(props: Props) {
if (webviewRef.current) focus('UserWebviewDialog', webviewRef.current);
}, []);
// When the iframe is isolated (security setting enabled), keyboard events
// like Escape don't reach the iframe content. We let the native <dialog>
// handle Escape by passing onCancel, but only when there's a dismiss button.
// https://github.com/laurent22/joplin/issues/13718
const onCancel = useMemo(() => {
return findDismissButton(buttons) ? onDismiss : undefined;
}, [buttons, onDismiss]);
return (
<Dialog className={`user-webview-dialog ${props.fitToContent ? '-fit' : ''}`}>
<Dialog className={`user-webview-dialog ${props.fitToContent ? '-fit' : ''}`} onCancel={onCancel}>
<div className='user-dialog-wrapper'>
<UserWebview
ref={webviewRef}

View File

@@ -83,8 +83,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097800
versionName "3.6.12"
versionCode 2097801
versionName "3.6.13"
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""

View File

@@ -57,6 +57,7 @@ const useStyles = (themeId: number, containerStyle: ViewStyle, size: DialogVaria
},
closeButton: {
margin: 0,
marginRight: -8,
},
// Ensure that the close button is aligned with the center of the header:
// Make its container smaller and center it.
@@ -91,8 +92,8 @@ const useStyles = (themeId: number, containerStyle: ViewStyle, size: DialogVaria
dialogSurface: {
borderRadius: 24,
backgroundColor: theme.backgroundColor,
paddingHorizontal: 16,
paddingVertical: 24,
paddingHorizontal: theme.margin,
paddingVertical: theme.margin,
...dialogSizing,
},
});

View File

@@ -1,134 +0,0 @@
import * as React from 'react';
import { Store } from 'redux';
import { AppState } from '../utils/types';
import TestProviderStack from './testing/TestProviderStack';
import { switchClient, setupDatabase, mockMobilePlatform, mockFetch } from '@joplin/lib/testing/test-utils';
import waitFor from '@joplin/lib/testing/waitFor';
import createMockReduxStore from '../utils/testing/createMockReduxStore';
import setupGlobalStore from '../utils/testing/setupGlobalStore';
import { act, fireEvent, render, screen } from '@testing-library/react-native';
import FeedbackBanner from './FeedbackBanner';
import { MobilePlatform } from '@joplin/lib/shim';
interface WrapperProps { }
let store: Store<AppState>;
const WrappedFeedbackBanner: React.FC<WrapperProps> = () => {
return <TestProviderStack store={store}>
<FeedbackBanner/>
</TestProviderStack>;
};
const getFeedbackButton = (positive: boolean) => {
return screen.getByRole('button', { name: positive ? 'Useful' : 'Not useful' });
};
const getSurveyLink = () => {
return screen.getByRole('button', { name: 'Take survey' });
};
const mockFeedbackServer = (surveyName = 'web-app-test') => {
let helpfulCount = 0;
let unhelpfulCount = 0;
const { reset } = mockFetch((request) => {
const surveyBaseUrls = [
'https://objects.joplinusercontent.com/',
'http://localhost:3430/',
];
const isSurveyRequest = surveyBaseUrls.some(url => request.url.startsWith(url));
if (!isSurveyRequest) {
return null;
}
const url = new URL(request.url);
if (url.pathname === `/r/survey--${surveyName}--helpful`) {
helpfulCount ++;
} else if (url.pathname === `/r/survey--${surveyName}--unhelpful`) {
unhelpfulCount ++;
} else {
return new Response('Not found', { status: 404 });
}
// The feedback server always redirects to another URL after a
// successful request. Mock this by always redirecting to the
// same URL.
return new Response('', {
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/302
status: 302,
statusText: 'Found',
headers: [
['location', 'https://joplinapp.org'],
],
});
});
return {
reset,
get helpfulCount() {
return helpfulCount;
},
get unhelpfulCount() {
return unhelpfulCount;
},
};
};
describe('FeedbackBanner', () => {
const resetMobilePlatform = ()=>{};
beforeEach(async () => {
await setupDatabase(0);
await switchClient(0);
store = createMockReduxStore();
setupGlobalStore(store);
jest.useFakeTimers({ advanceTimers: true });
mockMobilePlatform(MobilePlatform.Web);
});
afterEach(() => {
screen.unmount();
resetMobilePlatform();
});
test.each([
{ platform: MobilePlatform.Android, shouldShow: false },
{ platform: MobilePlatform.Web, shouldShow: true },
{ platform: MobilePlatform.Ios, shouldShow: false },
])('should correctly show/hide the feedback banner on %s', ({ platform, shouldShow }) => {
mockMobilePlatform(platform);
render(<WrappedFeedbackBanner />);
const header = screen.queryByRole('header', { name: 'Feedback' });
if (shouldShow) {
expect(header).toBeVisible();
} else {
expect(header).toBeNull();
}
});
test('clicking the "Useful" button should submit the response and show the "take survey" link', async () => {
const feedbackServerMock = mockFeedbackServer();
render(<WrappedFeedbackBanner />);
try {
const usefulButton = getFeedbackButton(true);
fireEvent.press(usefulButton);
await act(() => waitFor(async () => {
expect(getSurveyLink()).toBeVisible();
}));
expect(feedbackServerMock).toMatchObject({
helpfulCount: 1,
unhelpfulCount: 0,
});
} finally {
feedbackServerMock.reset();
}
});
});

View File

@@ -1,216 +0,0 @@
import { _ } from '@joplin/lib/locale';
import * as React from 'react';
import { View, StyleSheet, useWindowDimensions, TextStyle, Linking } from 'react-native';
import { Portal, Text } from 'react-native-paper';
import IconButton from './IconButton';
import { useCallback, useMemo } from 'react';
import shim from '@joplin/lib/shim';
import { Dispatch } from 'redux';
import { themeStyle } from './global-style';
import { AppState } from '../utils/types';
import { connect } from 'react-redux';
import Setting from '@joplin/lib/models/Setting';
import { LinkButton } from './buttons';
import Logger from '@joplin/utils/Logger';
import { SurveyProgress } from '@joplin/lib/models/settings/builtInMetadata';
const logger = Logger.create('FeedbackBanner');
interface Props {
dispatch: Dispatch;
progress: SurveyProgress;
surveyKey: string;
themeId: number;
}
const useStyles = (themeId: number, sentFeedback: boolean) => {
const { width: windowWidth } = useWindowDimensions();
return useMemo(() => {
const theme = themeStyle(themeId);
const iconBaseStyle: TextStyle = {
fontSize: 24,
color: theme.color3,
};
return StyleSheet.create({
container: {
backgroundColor: theme.backgroundColor3,
borderTopRightRadius: 16,
display: 'flex',
flexGrow: 1,
flexWrap: 'wrap',
flexDirection: 'row',
position: 'absolute',
bottom: 0,
left: 0,
maxWidth: windowWidth - 50,
gap: 18,
padding: 12,
},
contentRight: {
display: sentFeedback ? 'none' : 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: 16,
},
header: {
fontWeight: 'bold',
},
iconUseful: {
...iconBaseStyle,
color: theme.colorCorrect,
},
iconNotUseful: {
...iconBaseStyle,
color: theme.colorWarn,
},
dismissButtonIcon: {
fontSize: 16,
color: theme.color2,
marginLeft: 'auto',
marginRight: 'auto',
},
dismissButton: {
backgroundColor: theme.backgroundColor2,
borderColor: theme.backgroundColor,
borderWidth: 2,
width: 29,
height: 29,
borderRadius: 14,
position: 'absolute',
top: -16,
right: -16,
justifyContent: 'center',
},
dismissButtonContent: {
flexShrink: 1,
},
});
}, [themeId, windowWidth, sentFeedback]);
};
const useSurveyUrl = (surveyKey: string) => {
return useMemo(() => {
let baseUrl = 'https://objects.joplinusercontent.com/';
// For testing with a locally-hosted server:
const useLocalServer = false;
if (Setting.value('env') === 'dev' && useLocalServer) {
baseUrl = 'http://localhost:3430/';
}
return `${baseUrl}r/survey--${encodeURIComponent(surveyKey)}`;
}, [surveyKey]);
};
const setProgress = (progress: SurveyProgress) => {
Setting.setValue('survey.webClientEval2025.progress', progress);
};
const onDismiss = () => {
setProgress(SurveyProgress.Dismissed);
};
const FeedbackBanner: React.FC<Props> = props => {
const surveyUrl = useSurveyUrl(props.surveyKey);
const sentFeedback = props.progress === SurveyProgress.Started;
const sendSurveyResponse = useCallback(async (surveyResponse: string) => {
const fetchUrl = `${surveyUrl}--${encodeURIComponent(surveyResponse)}`;
logger.debug('sending response to', fetchUrl);
const showError = (message: string) => {
logger.error('Error', message);
void shim.showErrorDialog(
_('An error occurred while sending the response. This can happen if the app is offline or cannot connect to the server.\nError: %s', message),
);
};
try {
const response = await shim.fetch(fetchUrl);
// The server currently redirects (status 302) in response
// to many survey-related requests. This may be returned by
// the web app service worker as a 200 OK response, however. Support both:
if (response.ok || response.status === 302) {
setProgress(SurveyProgress.Started);
} else {
const body = await response.text();
showError(`Server error: ${response.status} ${body}`);
}
} catch (error) {
showError(error);
}
}, [surveyUrl]);
const onSurveyLinkClick = useCallback(() => {
void Linking.openURL(surveyUrl);
onDismiss();
}, [surveyUrl]);
const onNotUsefulClick = useCallback(() => {
void sendSurveyResponse('unhelpful');
}, [sendSurveyResponse]);
const onUsefulClick = useCallback(() => {
void sendSurveyResponse('helpful');
}, [sendSurveyResponse]);
const styles = useStyles(props.themeId, sentFeedback);
const renderStatusMessage = () => {
if (sentFeedback) {
return <View>
<Text>{_('Thank you for the feedback!\nDo you have time to complete a short survey?')}</Text>
<LinkButton onPress={onSurveyLinkClick}>{_('Take survey')}</LinkButton>
</View>;
} else {
return <Text>{_('Do you find the Joplin web app useful?')}</Text>;
}
};
if (shim.mobilePlatform() !== 'web' || props.progress === SurveyProgress.Dismissed) return null;
return <Portal>
<View style={styles.container} role='complementary'>
<View>
<Text
accessibilityRole='header'
variant='titleMedium'
style={styles.header}
>{_('Feedback')}</Text>
<Text>{renderStatusMessage()}</Text>
</View>
<View style={styles.contentRight}>
<IconButton
iconName='fas times'
themeId={props.themeId}
onPress={onNotUsefulClick}
description={_('Not useful')}
iconStyle={styles.iconNotUseful}
/>
<IconButton
iconName='fas check'
themeId={props.themeId}
onPress={onUsefulClick}
description={_('Useful')}
iconStyle={styles.iconUseful}
/>
</View>
<IconButton
iconName='fas times'
themeId={props.themeId}
onPress={onDismiss}
description={_('Dismiss')}
iconStyle={styles.dismissButtonIcon}
contentWrapperStyle={styles.dismissButtonContent}
containerStyle={styles.dismissButton}
/>
</View>
</Portal>;
};
export default connect((state: AppState) => ({
themeId: state.settings.theme,
surveyKey: 'web-app-test',
progress: state.settings['survey.webClientEval2025.progress'],
}))(FeedbackBanner);

View File

@@ -34,6 +34,7 @@ interface Props {
onScroll: OnScrollCallback;
onLoadEnd?: ()=> void;
pluginStates: PluginStates;
showNoteLinkIcon: boolean;
}
const onJoplinLinkClick = async (message: string) => {
@@ -84,6 +85,7 @@ function NoteBodyViewer(props: Props) {
initialScrollPercent: props.initialScrollPercent,
paddingBottom: props.paddingBottom,
showNoteLinkIcon: props.showNoteLinkIcon,
});
const onLoadEnd = useCallback(() => {
@@ -115,4 +117,5 @@ export default connect((state: AppState) => ({
themeId: state.settings.theme,
fontSize: state.settings['style.viewer.fontSize'],
pluginStates: state.pluginService.plugins,
showNoteLinkIcon: state.settings['notes.showNoteLinkIcon'],
}))(NoteBodyViewer);

View File

@@ -27,6 +27,7 @@ interface Props {
initialScrollPercent: number|undefined;
paddingBottom: number;
showNoteLinkIcon: boolean;
}
const onlyCheckboxHasChangedHack = (previousBody: string, newBody: string) => {
@@ -100,7 +101,7 @@ const useRerenderHandler = (props: Props) => {
const effectDependencies = [
props.noteBody, props.noteMarkupLanguage, props.renderer, props.highlightedKeywords,
props.noteHash, props.noteResources, props.themeId, props.paddingBottom, resourceDownloadRerenderCounter,
props.fontSize,
props.fontSize, props.showNoteLinkIcon,
];
const previousDeps = usePrevious(effectDependencies, []);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -138,6 +139,7 @@ const useRerenderHandler = (props: Props) => {
// instead.
initialScrollPercent: (previousHash && hashChanged) ? undefined : props.initialScrollPercent,
noteHash: props.noteHash,
showNoteLinkIcon: props.showNoteLinkIcon,
};
try {

View File

@@ -1,89 +0,0 @@
import * as React from 'react';
import { Linking, StyleSheet, TextStyle, View, ViewStyle } from 'react-native';
import { Text } from 'react-native-paper';
import IconButton from '../IconButton';
import { _ } from '@joplin/lib/locale';
import { useCallback, useState } from 'react';
import DismissibleDialog, { DialogVariant } from '../DismissibleDialog';
import { LinkButton, PrimaryButton } from '../buttons';
import makeDiscourseDebugUrl from '@joplin/lib/makeDiscourseDebugUrl';
import getPackageInfo from '../../utils/getPackageInfo';
import PluginService from '@joplin/lib/services/plugins/PluginService';
import Setting from '@joplin/lib/models/Setting';
interface Props {
wrapperStyle: ViewStyle;
iconStyle: TextStyle;
themeId: number;
}
const onLeaveFeedback = () => {
void Linking.openURL('https://forms.gle/B5YGDNzsUYBnoPx19');
};
const onReportBug = () => {
void Linking.openURL(
makeDiscourseDebugUrl('', '', [], getPackageInfo(), PluginService.instance(), Setting.value('plugins.states')),
);
};
const styles = StyleSheet.create({
feedbackContainer: {
flexGrow: 1,
flexDirection: 'row',
gap: 16,
justifyContent: 'flex-end',
flexWrap: 'wrap',
},
paragraph: {
paddingBottom: 7,
},
});
const WebBetaButton: React.FC<Props> = props => {
const [dialogVisible, setDialogVisible] = useState(false);
const onShowDialog = useCallback(() => {
setDialogVisible(true);
}, []);
const onHideDialog = useCallback(() => {
setDialogVisible(false);
}, []);
const renderParagraph = (content: string) => {
return <Text variant='bodyLarge' style={styles.paragraph}>{content}</Text>;
};
return (
<>
<IconButton
onPress={onShowDialog}
description={_('Beta')}
themeId={props.themeId}
contentWrapperStyle={props.wrapperStyle}
iconName="material beta"
iconStyle={props.iconStyle}
/>
<DismissibleDialog
heading={_('Beta')}
size={DialogVariant.SmallResize}
themeId={props.themeId}
visible={dialogVisible}
onDismiss={onHideDialog}
>
{renderParagraph('Welcome to the beta version of the Joplin Web App!')}
{renderParagraph('Thank you for participating in the beta version of the Joplin Web App.')}
{renderParagraph('The Joplin Web App is available for a limited time in open beta and may later join the Joplin Cloud plans.')}
{renderParagraph('Feel free to use it and let us know if have any questions or notice any issues!')}
<View style={styles.feedbackContainer}>
<LinkButton onPress={onReportBug}>{'Report bug'}</LinkButton>
<PrimaryButton onPress={onLeaveFeedback}>{'Give feedback'}</PrimaryButton>
</View>
</DismissibleDialog>
</>
);
};
export default WebBetaButton;

View File

@@ -1,7 +1,7 @@
import * as React from 'react';
import { PureComponent, ReactElement } from 'react';
import { connect } from 'react-redux';
import { View, Text, StyleSheet, TouchableOpacity, ViewStyle, Platform } from 'react-native';
import { View, Text, StyleSheet, TouchableOpacity, ViewStyle } from 'react-native';
import BackButtonService from '../../services/BackButtonService';
import NavService from '@joplin/lib/services/NavService';
import { _, _n } from '@joplin/lib/locale';
@@ -20,7 +20,6 @@ import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import { ContainerType } from '@joplin/lib/services/plugins/WebviewController';
import { Dispatch } from 'redux';
import WarningBanner from './WarningBanner';
import WebBetaButton from './WebBetaButton';
import Menu, { MenuOptionType } from './Menu';
import shim from '@joplin/lib/shim';
@@ -456,18 +455,6 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade
);
};
const betaIconButton = () => {
if (Platform.OS !== 'web') return null;
return (
<WebBetaButton
themeId={themeId}
wrapperStyle={this.styles().iconButton}
iconStyle={this.styles().topIcon}
/>
);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const renderTogglePluginEditorButton = (styles: any, onPress: OnPressCallback, disabled: boolean) => {
if (!this.props.showPluginEditorButton) return null;
@@ -666,7 +653,6 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade
const sideMenuComp = !showSideMenuButton ? null : sideMenuButton(this.styles(), () => this.sideMenuButton_press());
const backButtonComp = !showBackButton ? null : backButton(this.styles(), () => this.backButton_press(), backButtonDisabled);
const pluginPanelsComp = pluginPanelToggleButton(this.styles(), () => this.pluginPanelToggleButton_press());
const betaIconComp = !showSideMenuButton ? null : betaIconButton();
const selectAllButtonComp = !showSelectAllButton ? null : selectAllButton(this.styles(), () => this.selectAllButton_press());
const searchButtonComp = !showSearchButton ? null : searchButton(this.styles(), () => this.searchButton_press());
const customDeleteButtonComp = this.props.onDeleteButtonPress ? customDeleteButton(this.styles(), this.props.onDeleteButtonPress) : null;
@@ -680,7 +666,6 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade
// space while in use, we allow certain buttons to be hidden.
const hideableRightComponents = <>
{pluginPanelsComp}
{betaIconComp}
{togglePluginEditorButton}
{selectAllButtonComp}
{searchButtonComp}

View File

@@ -8,7 +8,7 @@ import { Icon, Text } from 'react-native-paper';
import { _ } from '@joplin/lib/locale';
import JoplinCloudIcon from './JoplinCloudIcon';
import NavService from '@joplin/lib/services/NavService';
import { StyleSheet, View } from 'react-native';
import { Platform, StyleSheet, View } from 'react-native';
import CardButton from '../buttons/CardButton';
import Setting from '@joplin/lib/models/Setting';
@@ -47,6 +47,14 @@ const styles = StyleSheet.create({
},
});
const useShouldShowOtherButton = () => {
// Always show "other" on non-web platforms
if (Platform.OS !== 'web') return true;
// Don't show "other" when hosted on Joplin Cloud (other sync
// targets can still be selected from settings).
return location.origin !== 'https://app.joplincloud.com';
};
interface SyncProviderProps {
title: string;
icon: ()=> React.ReactNode;
@@ -102,6 +110,8 @@ const SyncWizard: React.FC<Props> = ({ themeId, visible, dispatch }) => {
await NavService.go('Config', { sectionName: 'sync' });
}, [onDismiss]);
const showOther = useShouldShowOtherButton();
return <DismissibleDialog
themeId={themeId}
visible={visible}
@@ -126,14 +136,14 @@ const SyncWizard: React.FC<Props> = ({ themeId, visible, dispatch }) => {
onPress={onSelectJoplinCloud}
disabled={false}
/>
<SyncProvider
{showOther && <SyncProvider
title={_('Other')}
description={_('Select one of the other supported sync targets.')}
icon={() => <Icon size={iconSize} source='dots-horizontal-circle'/>}
featuresList={[]}
onPress={onSelectOtherTarget}
disabled={false}
/>
/>}
</View>
</DismissibleDialog>;
};

View File

@@ -8,7 +8,6 @@ import { themeStyle } from './global-style';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import useKeyboardState from '../utils/hooks/useKeyboardState';
import usePrevious from '@joplin/lib/hooks/usePrevious';
import FeedbackBanner from './FeedbackBanner';
import { Theme } from '@joplin/lib/themes/type';
import { useMemo } from 'react';
import KeyboardAvoidingView from './KeyboardAvoidingView';
@@ -78,7 +77,6 @@ const AppNavComponent: React.FC<Props> = (props) => {
<NotesScreen visible={notesScreenVisible} />
{searchScreenLoaded && <SearchScreen visible={searchScreenVisible} />}
{!notesScreenVisible && !searchScreenVisible && <Screen navigation={{ state: route }} themeId={props.themeId} dispatch={props.dispatch} />}
{notesScreenVisible ? <FeedbackBanner/> : null}
<View style={{ height: autocompletionBarPadding }} />
</KeyboardAvoidingView>
);

View File

@@ -39,8 +39,6 @@ const styles = (() => {
display: 'flex',
flexDirection: 'column',
gap: 20,
marginLeft: 10,
marginRight: 10,
};
return StyleSheet.create({
descriptionText: {
@@ -64,7 +62,7 @@ const styles = (() => {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 10,
paddingVertical: 10,
marginTop: 12,
marginBottom: 14,
},
@@ -123,7 +121,7 @@ const PluginInfoModalContent: React.FC<Props> = props => {
});
const aboutPlugin = (
<Card mode='outlined' style={{ margin: 8 }} testID='plugin-card'>
<Card mode='outlined' style={{ marginVertical: 8 }} testID='plugin-card'>
<Card.Content>
<PluginTitle manifest={manifest}/>
<Text variant='bodyMedium'>{_('by %s', manifest.author)}</Text>

View File

@@ -718,8 +718,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
const explicitReloadRequired = !editorPlugin && this.props.editorNoteReloadTimeRequest > this.state.noteLastLoadTime;
if (explicitReloadRequired) {
void shared.reloadNote(this);
this.refreshKey = this.props.editorNoteReloadTimeRequest;
void this.reloadNoteAndUpdateRefreshKey();
}
if (explicitReloadRequired || (editorPlugin && editorPluginIdsChanged)) {
@@ -774,6 +773,11 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
});
}
private async reloadNoteAndUpdateRefreshKey() {
await shared.reloadNote(this);
this.refreshKey = this.props.editorNoteReloadTimeRequest;
}
private title_changeText(text: string) {
let newText = text;
newText = text.replace(/(\r\n|\n|\r)/gm, ' ');

View File

@@ -21,6 +21,7 @@ const defaultRendererSettings: RenderSettings = {
pluginSettings: {},
requestPluginSetting: () => { },
showNoteLinkIcon: true,
};
const makeRenderer = (options: Partial<RendererSetupOptions>) => {

View File

@@ -27,6 +27,7 @@ export interface RenderSettings {
splitted?: boolean; // Move CSS into a separate output
mapsToLine?: boolean; // Sourcemaps
showNoteLinkIcon?: boolean;
createEditPopupSyntax: string;
destroyEditPopupSyntax: string;
@@ -136,6 +137,7 @@ export default class Renderer {
splitted: settings.splitted,
mapsToLine: settings.mapsToLine,
whiteBackgroundNoteRendering: markup.language === MarkupLanguage.Html,
showNoteLinkIcon: settings.showNoteLinkIcon,
globalSettings: settings.globalSettings,
};

View File

@@ -71,6 +71,7 @@ export interface RenderOptions {
// Forwarded renderer settings
splitted?: boolean;
mapsToLine?: boolean;
showNoteLinkIcon?: boolean;
}
type CancelEvent = { cancelled: boolean };

View File

@@ -240,6 +240,7 @@ const useWebViewSetup = (props: Props): Result => {
}
},
removeUnusedPluginAssets: options.removeUnusedPluginAssets,
showNoteLinkIcon: options.showNoteLinkIcon,
globalSettings: {
'markdown.plugin.abc.options': Setting.value('markdown.plugin.abc.options'),
},

View File

@@ -520,7 +520,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
CURRENT_PROJECT_VERSION = 150;
CURRENT_PROJECT_VERSION = 151;
DEVELOPMENT_TEAM = A9BXAFS6CT;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Joplin/Info.plist;
@@ -529,7 +529,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 13.6.1;
MARKETING_VERSION = 13.6.2;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -555,7 +555,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
CURRENT_PROJECT_VERSION = 150;
CURRENT_PROJECT_VERSION = 151;
DEVELOPMENT_TEAM = A9BXAFS6CT;
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
@@ -563,7 +563,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 13.6.1;
MARKETING_VERSION = 13.6.2;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -758,7 +758,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 150;
CURRENT_PROJECT_VERSION = 151;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = A9BXAFS6CT;
GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -769,7 +769,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 13.6.1;
MARKETING_VERSION = 13.6.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
@@ -801,7 +801,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 150;
CURRENT_PROJECT_VERSION = 151;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = A9BXAFS6CT;
GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -812,7 +812,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 13.6.1;
MARKETING_VERSION = 13.6.2;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
"$(inherited)",

View File

@@ -2015,7 +2015,7 @@ PODS:
- Yoga
- RNCPushNotificationIOS (1.11.0):
- React-Core
- RNDateTimePicker (8.4.7):
- RNDateTimePicker (8.5.1):
- hermes-engine
- RCTRequired
- RCTTypeSafety
@@ -2041,7 +2041,7 @@ PODS:
- React-Core
- RNFileViewer (2.1.5):
- React-Core
- RNLocalize (3.5.4):
- RNLocalize (3.6.0):
- hermes-engine
- RCTRequired
- RCTTypeSafety
@@ -2627,10 +2627,10 @@ SPEC CHECKSUMS:
rn-fetch-blob: 25612b6d6f6e980c6f17ed98ba2f58f5696a51ca
RNCClipboard: 88d7eeb555d1183915f0885bdbc5c97eb6f7f3ba
RNCPushNotificationIOS: 6c4ca3388c7434e4a662b92e4dfeeee858e6f440
RNDateTimePicker: f11373a05d806e849ab984e2806c531278b47cdd
RNDateTimePicker: 19ffa303c4524ec0a2dfdee2658198451c16b7f1
RNDeviceInfo: bcce8752b5043a623fe3c26789679b473f705d3c
RNFileViewer: 4b5d83358214347e4ab2d4ca8d5c1c90d869e251
RNLocalize: da3c00bf1044a67e72cf8b450289e263fd5baab3
RNLocalize: 83a242b38886bf7e84073410c101e9ea39a1c1a5
RNQuickAction: c2c8f379e614428be0babe4d53a575739667744d
RNSecureRandom: b64d263529492a6897e236a22a2c4249aa1b53dc
RNShare: 0e600372fb35783fe30d413efd28d11de2bf6cf0

View File

@@ -66,7 +66,7 @@
"react-native-file-viewer": "2.1.5",
"react-native-get-random-values": "1.11.0",
"react-native-image-picker": "8.2.1",
"react-native-localize": "3.5.4",
"react-native-localize": "3.6.0",
"react-native-modal-datetime-picker": "18.0.0",
"react-native-nitro-modules": "0.33.2",
"react-native-paper": "5.14.5",

View File

@@ -531,6 +531,13 @@ export default class Synchronizer {
// console.info('NEW', newInfo);
if (newInfo.revisionServiceEnabled !== localInfo.revisionServiceEnabled) {
Setting.setValue('revisionService.enabled', newInfo.revisionServiceEnabled);
}
if (newInfo.revisionServiceTtlDays !== localInfo.revisionServiceTtlDays) {
Setting.setValue('revisionService.ttlDays', newInfo.revisionServiceTtlDays);
}
if (newInfo.e2ee !== previousE2EE) {
if (newInfo.e2ee) {
const mk = getActiveMasterKey(newInfo);

View File

@@ -10,6 +10,7 @@ import BaseItem from '../../models/BaseItem';
import shim from '../../shim';
import { Dispatch } from 'redux';
import { State } from '../../reducer';
import { onRevisionServiceSettingsChanged } from '../../services/synchronizer/syncInfoUtils';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
let sortNoteListTimeout: any = null;
@@ -31,6 +32,10 @@ export default async (store: any, _next: any, action: any, dispatch: Dispatch) =
reg.resetSyncTarget();
}
if (action.type === 'SETTING_UPDATE_ONE') {
onRevisionServiceSettingsChanged(action.key, action.value);
}
let mustAutoAddResources = false;
if (action.type === 'SETTING_UPDATE_ONE' && action.key === 'sync.resourceDownloadMode') {

View File

@@ -1967,7 +1967,8 @@ const builtInMetadata = (Setting: typeof SettingType) => {
},
'survey.webClientEval2025.progress': {
value: SurveyProgress.NotStarted,
// Ended in February 2026. See https://github.com/laurent22/joplin/pull/14497.
value: SurveyProgress.Dismissed,
type: SettingItemType.Int,
public: false,
isEnum: true,
@@ -2077,6 +2078,17 @@ const builtInMetadata = (Setting: typeof SettingType) => {
storage: SettingStorage.File,
isGlobal: false,
},
'notes.showNoteLinkIcon': {
value: true,
type: SettingItemType.Bool,
storage: SettingStorage.File,
section: 'note',
public: true,
isGlobal: true,
label: () => _('Show Joplin icon for note links'),
appTypes: [AppType.Desktop, AppType.Mobile],
},
} satisfies Record<string, SettingItem>;
for (const [key, md] of Object.entries(output)) {

View File

@@ -0,0 +1,26 @@
import { setupDatabaseAndSynchronizer, switchClient, decryptionWorker } from '../testing/test-utils';
describe('services/DecryptionWorker', () => {
beforeEach(async () => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
});
it('should not return null when a call to .start is cancelled', async () => {
const worker = decryptionWorker();
// Both calls should return a valid DecryptionResult, even if the
// queue skips one task due to concurrency.
const results = await Promise.all([
worker.start(),
worker.start(),
]);
for (const result of results) {
expect(result === null).toBe(false);
expect(result === undefined).toBe(false);
expect(result).toHaveProperty('error');
}
});
});

View File

@@ -349,6 +349,15 @@ export default class DecryptionWorker {
if (lastError) {
throw lastError;
}
// If this task was skipped due to a concurrent start() call, return an empty
// DecryptionResult instead of null. AsyncActionQueue drops earlier tasks when
// multiple are queued, but start() guarantees Promise<DecryptionResult> and
// must not resolve null.
if (!output) {
return { error: null, decryptedItemCount: 0, skippedItemCount: 0 };
}
return output;
}

View File

@@ -4,6 +4,7 @@ import { synchronizerStart, revisionService, setupDatabaseAndSynchronizer, synch
import Note from '../../models/Note';
import Revision from '../../models/Revision';
import { loadMasterKeysFromSettings, setupAndEnableEncryption } from '../e2ee/utils';
import { onRevisionServiceSettingsChanged } from './syncInfoUtils';
describe('Synchronizer.revisions', () => {
@@ -301,4 +302,27 @@ describe('Synchronizer.revisions', () => {
jest.useRealTimers();
});
it('should sync revision service settings across clients', (async () => {
const changeSetting = (key: string, value: unknown) => {
Setting.setValue(key, value);
onRevisionServiceSettingsChanged(key, value);
};
changeSetting('revisionService.enabled', false);
await synchronizerStart();
await switchClient(2);
expect(Setting.value('revisionService.enabled')).toBe(true);
await synchronizerStart();
expect(Setting.value('revisionService.enabled')).toBe(false);
changeSetting('revisionService.enabled', true);
await synchronizerStart();
await switchClient(1);
expect(Setting.value('revisionService.enabled')).toBe(false);
await synchronizerStart();
expect(Setting.value('revisionService.enabled')).toBe(true);
}));
});

View File

@@ -1,6 +1,6 @@
import { afterAllCleanUp, setupDatabaseAndSynchronizer, logger, switchClient, encryptionService, msleep, fileApi } from '../../testing/test-utils';
import MasterKey from '../../models/MasterKey';
import { checkIfCanSync, localSyncInfo, masterKeyEnabled, mergeSyncInfos, saveLocalSyncInfo, setMasterKeyEnabled, SyncInfo, syncInfoEquals, checkSyncTargetIsValid, fetchSyncInfo } from './syncInfoUtils';
import { checkIfCanSync, localSyncInfo, masterKeyEnabled, mergeSyncInfos, saveLocalSyncInfo, setMasterKeyEnabled, SyncInfo, syncInfoEquals, checkSyncTargetIsValid, fetchSyncInfo, onRevisionServiceSettingsChanged } from './syncInfoUtils';
import Setting from '../../models/Setting';
import BaseItem from '../../models/BaseItem';
import BaseModel from '../../models/BaseItem';
@@ -283,6 +283,14 @@ describe('syncInfoUtils', () => {
'publicKey': '-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCA...',
},
},
'revisionServiceEnabled': {
'updatedTime': 0,
'value': true,
},
'revisionServiceTtlDays': {
'updatedTime': 0,
'value': 90,
},
'version': 3,
});
});
@@ -392,4 +400,66 @@ describe('syncInfoUtils', () => {
Setting.setValue('sync.wipeOutFailSafe', true);
expect(fetchSyncInfo(fileApi())).resolves.not.toThrow();
}));
it('should merge revision service settings based on timestamps', () => {
const s1 = new SyncInfo();
s1.revisionServiceEnabled = false;
s1.revisionServiceTtlDays = 30;
const s2 = new SyncInfo();
s2.revisionServiceEnabled = true;
s2.revisionServiceTtlDays = 90;
s1.setKeyTimestamp('revisionServiceEnabled', 200);
s1.setKeyTimestamp('revisionServiceTtlDays', 100);
s2.setKeyTimestamp('revisionServiceEnabled', 100);
s2.setKeyTimestamp('revisionServiceTtlDays', 200);
const merged = mergeSyncInfos(s1, s2);
expect(merged.revisionServiceEnabled).toBe(false);
expect(merged.revisionServiceTtlDays).toBe(90);
});
it('should use default revision service settings when not present in sync info', () => {
const s = new SyncInfo(JSON.stringify({ version: 3 }));
expect(s.revisionServiceEnabled).toBe(true);
expect(s.revisionServiceTtlDays).toBe(90);
});
it('should update syncInfo when revision service setting changes', async () => {
const s = new SyncInfo();
s.revisionServiceTtlDays = 90;
s.setKeyTimestamp('revisionServiceTtlDays', 0);
saveLocalSyncInfo(s);
onRevisionServiceSettingsChanged('revisionService.ttlDays', 30);
const updated = localSyncInfo();
expect(updated.revisionServiceTtlDays).toBe(30);
expect(updated.keyTimestamp('revisionServiceTtlDays')).toBeGreaterThan(0);
});
it('should not update syncInfo when revision service setting value is unchanged', async () => {
const s = new SyncInfo();
s.revisionServiceTtlDays = 90;
s.setKeyTimestamp('revisionServiceTtlDays', 0);
saveLocalSyncInfo(s);
onRevisionServiceSettingsChanged('revisionService.ttlDays', 90);
const updated = localSyncInfo();
expect(updated.keyTimestamp('revisionServiceTtlDays')).toBe(0);
});
it('should ignore unrelated keys in onRevisionServiceSettingsChanged', async () => {
const s = new SyncInfo();
s.revisionServiceTtlDays = 90;
s.setKeyTimestamp('revisionServiceTtlDays', 0);
saveLocalSyncInfo(s);
onRevisionServiceSettingsChanged('sync.target', 1);
const updated = localSyncInfo();
expect(updated.keyTimestamp('revisionServiceTtlDays')).toBe(0);
});
});

View File

@@ -24,6 +24,11 @@ export interface SyncInfoValueString {
updatedTime: number;
}
export interface SyncInfoValueInt {
value: number;
updatedTime: number;
}
export interface SyncInfoValuePublicPrivateKeyPair {
value: PublicPrivateKeyPair;
updatedTime: number;
@@ -44,6 +49,23 @@ export const setAppMinVersion = (v: string) => {
appMinVersion_ = v;
};
export function onRevisionServiceSettingsChanged(key: string, value: unknown) {
if (key !== 'revisionService.enabled' && key !== 'revisionService.ttlDays') return;
const s = localSyncInfo();
let changed = false;
if (key === 'revisionService.enabled' && s.revisionServiceEnabled !== value) {
if (typeof value !== 'boolean') return;
s.revisionServiceEnabled = value;
changed = true;
}
if (key === 'revisionService.ttlDays' && s.revisionServiceTtlDays !== value) {
if (typeof value !== 'number') return;
s.revisionServiceTtlDays = value;
changed = true;
}
if (changed) saveLocalSyncInfo(s);
}
export async function migrateLocalSyncInfo(db: JoplinDatabase) {
if (Setting.value('syncInfoCache')) return; // Already initialized
@@ -75,6 +97,10 @@ export async function migrateLocalSyncInfo(db: JoplinDatabase) {
// most likely not what the user wants.
syncInfo.setKeyTimestamp('e2ee', 0);
syncInfo.setKeyTimestamp('activeMasterKeyId', 0);
syncInfo.revisionServiceEnabled = Setting.value('revisionService.enabled');
syncInfo.revisionServiceTtlDays = Setting.value('revisionService.ttlDays');
syncInfo.setKeyTimestamp('revisionServiceEnabled', 0);
syncInfo.setKeyTimestamp('revisionServiceTtlDays', 0);
await saveLocalSyncInfo(syncInfo);
}
@@ -219,6 +245,8 @@ export function mergeSyncInfos(s1: SyncInfo, s2: SyncInfo): SyncInfo {
output.setWithTimestamp(s1.keyTimestamp('e2ee') > s2.keyTimestamp('e2ee') ? s1 : s2, 'e2ee');
output.setWithTimestamp(s1.keyTimestamp('ppk') > s2.keyTimestamp('ppk') ? s1 : s2, 'ppk');
output.setWithTimestamp(s1.keyTimestamp('revisionServiceEnabled') > s2.keyTimestamp('revisionServiceEnabled') ? s1 : s2, 'revisionServiceEnabled');
output.setWithTimestamp(s1.keyTimestamp('revisionServiceTtlDays') > s2.keyTimestamp('revisionServiceTtlDays') ? s1 : s2, 'revisionServiceTtlDays');
output.version = s1.version > s2.version ? s1.version : s2.version;
mergeActiveMasterKeys(s1, s2, output);
@@ -255,11 +283,15 @@ export class SyncInfo {
private masterKeys_: MasterKeyEntity[] = [];
private ppk_: SyncInfoValuePublicPrivateKeyPair;
private appMinVersion_: string = appMinVersion_;
private revisionServiceEnabled_: SyncInfoValueBoolean;
private revisionServiceTtlDays_: SyncInfoValueInt;
public constructor(serialized: string = null) {
this.e2ee_ = { value: false, updatedTime: 0 };
this.activeMasterKeyId_ = { value: '', updatedTime: 0 };
this.ppk_ = { value: null, updatedTime: 0 };
this.revisionServiceEnabled_ = { value: true, updatedTime: 0 };
this.revisionServiceTtlDays_ = { value: 90, updatedTime: 0 };
if (serialized) this.load(serialized);
}
@@ -273,6 +305,8 @@ export class SyncInfo {
masterKeys: this.masterKeys,
ppk: this.ppk_,
appMinVersion: this.appMinVersion,
revisionServiceEnabled: this.revisionServiceEnabled_,
revisionServiceTtlDays: this.revisionServiceTtlDays_,
};
}
@@ -315,6 +349,8 @@ export class SyncInfo {
this.masterKeys_ = 'masterKeys' in s ? s.masterKeys : [];
this.ppk_ = 'ppk' in s ? s.ppk : { value: null, updatedTime: 0 };
this.appMinVersion_ = s.appMinVersion ? s.appMinVersion : '0.0.0';
this.revisionServiceEnabled_ = 'revisionServiceEnabled' in s ? s.revisionServiceEnabled : { value: true, updatedTime: 0 };
this.revisionServiceTtlDays_ = 'revisionServiceTtlDays' in s ? s.revisionServiceTtlDays : { value: 90, updatedTime: 0 };
// Migration for master keys that didn't have "hasBeenUsed" property -
// in that case we assume they've been used at least once.
@@ -382,6 +418,26 @@ export class SyncInfo {
this.activeMasterKeyId_ = { value: v, updatedTime: Date.now() };
}
public get revisionServiceEnabled(): boolean {
return this.revisionServiceEnabled_.value;
}
public set revisionServiceEnabled(v: boolean) {
if (v === this.revisionServiceEnabled) return;
this.revisionServiceEnabled_ = { value: v, updatedTime: Date.now() };
}
public get revisionServiceTtlDays(): number {
return this.revisionServiceTtlDays_.value;
}
public set revisionServiceTtlDays(v: number) {
if (v === this.revisionServiceTtlDays) return;
this.revisionServiceTtlDays_ = { value: v, updatedTime: Date.now() };
}
public get masterKeys(): MasterKeyEntity[] {
return this.masterKeys_;
}

View File

@@ -210,6 +210,8 @@ export interface RuleOptions {
allowedFilePrefixes?: string[];
platformName?: string;
showNoteLinkIcon?: boolean;
}
export default class MdToHtml implements MarkupRenderer {

View File

@@ -79,4 +79,24 @@ describe('linkReplacement', () => {
expect(linkHtml).toContain('ontouchend');
expect(linkHtml).toContain('ontouchcancel');
});
test('should show Joplin Icon for internal link by default', () => {
const resourceId = 'e6afba55bdf74568ac94f8d1e3578d2c';
const linkHtml = linkReplacement(`:/${resourceId}`, {
ResourceModel: defaultResourceModel,
resources: {},
showNoteLinkIcon: true,
}).html;
expect(linkHtml).toContain('<span class="resource-icon fa-joplin"></span>');
});
test('should not include Joplin Icon for internal link if configuration is not enabled', () => {
const resourceId = 'e6afba55bdf74568ac94f8d1e3578d2c';
const linkHtml = linkReplacement(`:/${resourceId}`, {
ResourceModel: defaultResourceModel,
resources: {},
showNoteLinkIcon: false,
}).html;
expect(linkHtml).not.toContain('<span class="resource-icon fa-joplin"></span>');
});
});

View File

@@ -16,6 +16,7 @@ export interface Options {
postMessageSyntax?: string;
enableLongPress?: boolean;
itemIdToUrl?: ItemIdToUrlHandler;
showNoteLinkIcon?: boolean;
}
export interface LinkReplacementResult {
@@ -35,6 +36,7 @@ export default function(href: string, options: Options = null): LinkReplacementR
options.plainResourceRendering ??= false;
options.postMessageSyntax ??= 'postMessage';
options.enableLongPress ??= false;
options.showNoteLinkIcon ??= true;
const resourceHrefInfo = urlUtils.parseResourceUrl(href);
const isResourceUrl = options.resources && !!resourceHrefInfo;
@@ -74,12 +76,16 @@ export default function(href: string, options: Options = null): LinkReplacementR
if (resourceHrefInfo.hash) href += `#${resourceHrefInfo.hash}`;
resourceIdAttr = `data-resource-id='${resourceId}'`;
const iconType = mime ? getClassNameForMimeType(mime) : 'fa-joplin';
let iconType;
if (mime) {
iconType = getClassNameForMimeType(mime);
} else {
iconType = options.showNoteLinkIcon ? 'fa-joplin' : '';
}
// Icons are defined in lib/renderers/noteStyle using inline svg
// The icons are taken from fork-awesome but use the font-awesome naming scheme in order
// to be more compatible with the getClass library
icon = `<span class="resource-icon ${iconType}"></span>`;
icon = iconType ? `<span class="resource-icon ${iconType}"></span>` : '';
}
} else {
// If the link is a plain URL (as opposed to a resource link), set the href to the actual

View File

@@ -23,6 +23,7 @@ function plugin(markdownIt: any, ruleOptions: RuleOptions) {
postMessageSyntax: ruleOptions.postMessageSyntax,
enableLongPress: ruleOptions.enableLongPress,
itemIdToUrl: ruleOptions.itemIdToUrl,
showNoteLinkIcon: ruleOptions.showNoteLinkIcon,
});
ruleOptions.context.currentLinks.push({

View File

@@ -92,6 +92,8 @@ export interface RenderOptions {
platformName?: string;
showNoteLinkIcon?: boolean;
// HtmlToHtml only
whiteBackgroundNoteRendering?: boolean;

View File

@@ -56,7 +56,7 @@
"query-string": "7.1.3",
"rate-limiter-flexible": "7.4.0",
"raw-body": "3.0.2",
"samlify": "2.10.1",
"samlify": "2.10.2",
"sqlite3": "5.1.6",
"stripe": "13.9.0",
"uuid": "11.1.0",

View File

@@ -3,7 +3,7 @@ import { ItemType, databaseSchema, Uuid, Item, ShareType, Share, ChangeType, Use
import { defaultPagination, paginateDbQuery, PaginatedResults, Pagination } from './utils/pagination';
import { isJoplinItemName, isJoplinResourceBlobPath, linkedResourceIds, serializeJoplinItem, unserializeJoplinItem } from '../utils/joplinUtils';
import { ModelType } from '@joplin/lib/BaseModel';
import { ApiError, CustomErrorCode, ErrorConflict, ErrorForbidden, ErrorPayloadTooLarge, ErrorUnprocessableEntity, ErrorCode } from '../utils/errors';
import { ApiError, CustomErrorCode, ErrorConflict, ErrorForbidden, ErrorPayloadTooLarge, ErrorUnprocessableEntity, ErrorCode, ErrorBadRequest } from '../utils/errors';
import { Knex } from 'knex';
import { ChangePreviousItem } from './ChangeModel';
import { unique } from '../utils/array';
@@ -1065,6 +1065,8 @@ export default class ItemModel extends BaseModel<Item> {
if (!item.owner_id) item.owner_id = userId;
} else {
const beforeSaveItem = await this.load(item.id, { fields: ['name', 'jop_share_id'] });
if (!beforeSaveItem) throw new ErrorBadRequest('Item does not exist');
previousName = beforeSaveItem.name;
previousItem = {
jop_share_id: beforeSaveItem.jop_share_id,

View File

@@ -4,6 +4,51 @@ import { ShareType } from '../services/database/types';
import { inviteUserToShare, shareFolderWithUser, shareWithUserAndAccept, updateItemShareId } from '../utils/testing/shareApiUtils';
import { withWarningSilenced } from '@joplin/lib/testing/test-utils';
// Goes through the process of:
// 1. Creating two users/sessions
// 2. Creating a share and accepting it
// 3. Moving a note created by the share recipient in to the share
//
// This creates a note owned by the share recipient, but within the
// share.
const createShareWithNoteOwnedByRecipient = async () => {
const { session: session1 } = await createUserAndSession(1);
const { session: session2, user: user2 } = await createUserAndSession(2);
await createItemTree(session1.user_id, '', {
'000000000000000000000000000000F1': {
},
});
await createItemTree(session2.user_id, '', {
'000000000000000000000000000000F2': {
'00000000000000000000000000000001': null,
},
});
const shareRoot = await models().item().loadByJopId(session1.user_id, '000000000000000000000000000000F1');
// Note should initially be owned by user 2
let note = await models().item().loadByJopId(session2.user_id, '00000000000000000000000000000001');
expect(note.owner_id).toBe(session2.user_id);
const { share, shareUser } = await shareWithUserAndAccept(session1.id, session2.id, user2, ShareType.Folder, shareRoot);
await updateItemShareId(session1, shareRoot.id, share.id);
await models().share().updateSharedItems3();
// Changing the note's share ID and parent should not change the owner ID
note = await updateItemShareId(session2, note.id, share.id);
note = await models().item().saveForUser(session1.user_id, {
...note,
jop_parent_id: '000000000000000000000000000000F1',
});
await models().share().updateSharedItems3();
expect(note.owner_id).toBe(session2.user_id);
return { share, shareUser, note, session1, session2 };
};
describe('ShareModel', () => {
beforeAll(async () => {
@@ -258,4 +303,42 @@ describe('ShareModel', () => {
});
expect(await getUser2UserItems()).toHaveLength(0);
});
test.each([
{ deleteShare: false, label: '' },
// Deleting the share means that the maintenance task can't use share.owner_id to
// determine the new owner for the item.
{ deleteShare: true, label: 'and the share is deleted' },
])('should update owner_id when the original owner no longer has access $label', async ({ deleteShare }) => {
const { share, shareUser, note, session1, session2 } = await createShareWithNoteOwnedByRecipient();
expect(note.owner_id).toBe(session2.user_id);
// Remove session2.user_id from the share either by deleting the entire share or
// by removing the shareUser.
if (deleteShare) {
await models().share().delete(share.id);
} else {
await models().shareUser().delete(shareUser.id);
}
await models().share().updateSharedItems3();
const updatedNote = await models().item().load(note.id);
// The owner_id should be updated
expect(updatedNote.owner_id).toBe(session1.user_id);
// ...but it should still be part of the share.
expect(updatedNote.jop_share_id).toBe(share.id);
});
test('should not update owner_id after unsharing if an item has been moved out of a share by the item\'s owner', async () => {
const { shareUser, note, session2 } = await createShareWithNoteOwnedByRecipient();
await updateItemShareId(session2, note.id, '');
// Removing session2 from the share should keep the item's owner the same
await models().shareUser().delete(shareUser.id);
await models().share().updateSharedItems3();
const updatedNote = await models().item().load(note.id);
expect(updatedNote.owner_id).toBe(session2.user_id);
});
});

View File

@@ -208,7 +208,11 @@ export default class ShareModel extends BaseModel<Share> {
}
};
const handleCreated = async (change: Change, item: Item, share: Share) => {
// For performance, handleCreated acts on all changes for a particular item at once.
//
// This function must behave correctly regardless of whether it is called before or after
// other events are processed.
const handleCreated = async (item: Item, changes: Change[], share: Share) => {
if (!item.jop_share_id) return;
// When a folder is unshared, the share object is deleted, then all
@@ -229,7 +233,9 @@ export default class ShareModel extends BaseModel<Share> {
const shareUserIds = await this.allShareUserIds(share);
for (const shareUserId of shareUserIds) {
if (shareUserId === change.user_id) continue;
const hasCreationEvent = changes.some(change => change.user_id === shareUserId);
if (hasCreationEvent) continue;
await addUserItem(shareUserId, item.id);
}
@@ -275,18 +281,22 @@ export default class ShareModel extends BaseModel<Share> {
};
const handleDeleted = async (change: Change, item: Item|null, share: Share|null) => {
// On deletion, we check for extra user_items entries for items that still exist.
// These user_items can be created by race conditions between updateSharedItems3
// and logic for removing users from a share.
// On deletion, we check for extra user_items entries and incorrect ownership for
// items that still exist:
// - Unexpected user_items can be created by race conditions between updateSharedItems3
// and logic for removing users from a share.
// - Outdated owner_id information can be caused by moving an item into a share,
// then removing the item's original owner from the share.
//
// For now, only check the case where the item exists, and thus the user_items
// entry could allow access to the item.
// For now, only check the case where the item exists, and thus the user_items entry
// could allow access to the item.
if (!item) return;
perfTimer.push('handleDeleted');
// If the userItem exists, the user still has access to the item, despite the deletion change:
const userItem = await this.models().userItem().byUserAndItemId(change.user_id, change.item_id);
let userItem = await this.models().userItem().byUserAndItemId(change.user_id, change.item_id);
if (userItem) {
perfTimer.push('handleDeleted');
const isShareMember = async () => {
if (!share) return false;
@@ -302,10 +312,56 @@ export default class ShareModel extends BaseModel<Share> {
// Delete by the UserItem's ID to avoid race conditions. If a new user item is created for the same
// (user, item) pair (perhaps after removing the original), it should not be deleted by this task:
await this.models().userItem().deleteByUserItemIds([userItem.id]);
userItem = null;
}
perfTimer.pop();
}
// If an item was deleted for the owner, and the owner no longer has access, the item should now be owned by
// a different user:
const deletedForOwner = item.owner_id === change.user_id;
if (deletedForOwner && !userItem) {
const userItems = await this.models().userItem().byItemIds([item.id]);
const usersWithAccess = userItems.map(item => item.user_id);
let newOwnerId;
// Check that the share owner still has access: Handle the case where the item's parent share
// is changed after the item and share have been loaded.
if (share && usersWithAccess.includes(share.owner_id)) {
// Case where the item was moved to a different share or the original owner was removed from the
// share:
newOwnerId = share.owner_id;
} else if (usersWithAccess.length === 1) {
// Case where the item was moved out of a share by a user that didn't previously own the item,
// or the item's share was deleted:
newOwnerId = usersWithAccess[0];
} else {
// May happen due to a race condition related to moving an item between shares
// while processing the item's shares/deletions.
logger.warn('handleDeleted: Unable to accurately fix owner_id for item', item.id, 'in share', share?.id, 'and users with access', usersWithAccess);
}
if (!newOwnerId) {
logger.warn('handleDeleted: Item', item.id, 'deleted for owner', item.owner_id, 'and still exists, but no new owner ID was assigned.');
} else {
try {
await this.models().item().saveForUser(newOwnerId, {
id: item.id,
owner_id: newOwnerId,
}, { isNew: false });
} catch (error) {
// Guard against a potential race condition: Handle the case where the item was deleted for all users
// during the share update process:
if (error instanceof ErrorBadRequest) {
logger.warn('handleDeleted: Unable to update owner_id on item', item.id, error);
} else {
throw error;
}
}
}
}
perfTimer.pop();
};
// This function add any missing item to a user's collection. Normally
@@ -354,6 +410,21 @@ export default class ShareModel extends BaseModel<Share> {
perfTimer.pop();
};
const buildItemToChangeTypeMap = (changeType: ChangeType, changes: Change[]) => {
const itemToChanges = new Map<Uuid, Change[]>();
for (const change of changes) {
if (change.type !== changeType) continue;
const itemChanges = itemToChanges.get(change.item_id);
if (itemChanges) {
itemChanges.push(change);
} else {
itemToChanges.set(change.item_id, [change]);
}
}
return itemToChanges;
};
// This loop essentially applies the change made by one user to all the
// other users in the share.
//
@@ -399,18 +470,17 @@ export default class ShareModel extends BaseModel<Share> {
await this.withTransaction(async () => {
perfTimer.push(`Processing ${changes.length} changes`);
const itemToUpdates = new Map<Uuid, Change[]>();
for (const change of changes) {
if (change.type === ChangeType.Update) {
const updates = itemToUpdates.get(change.item_id);
if (updates) {
updates.push(change);
} else {
itemToUpdates.set(change.item_id, [change]);
}
}
// Performance: Group creation events per-item
const itemToCreations = buildItemToChangeTypeMap(ChangeType.Create, changes);
for (const [itemId, itemChanges] of itemToCreations.entries()) {
const item = items.find(i => i.id === itemId);
if (!item) continue;
const itemShare = shares.find(s => s.id === item.jop_share_id);
await handleCreated(item, itemChanges, itemShare);
}
const itemToUpdates = buildItemToChangeTypeMap(ChangeType.Update, changes);
for (const change of changes) {
const item = items.find(i => i.id === change.item_id);
@@ -419,10 +489,6 @@ export default class ShareModel extends BaseModel<Share> {
if (item) {
const itemShare = shares.find(s => s.id === item.jop_share_id);
if (change.type === ChangeType.Create) {
await handleCreated(change, item, itemShare);
}
if (change.type === ChangeType.Update) {
const allUpdates = itemToUpdates.get(item.id);
const changeIndex = allUpdates.indexOf(change);

View File

@@ -83,7 +83,7 @@ export const updateItemShareId = async (session: Session, itemId: Uuid, shareId:
const item = await models().item().load(itemId);
const joplinItem = await models().item().loadAsJoplinItem<FolderEntity|NoteEntity>(itemId);
await updateItem(session.id, `root:/${item.name}:`, await serializeJoplinItem({
return await updateItem(session.id, `root:/${item.name}:`, await serializeJoplinItem({
...joplinItem,
share_id: shareId,
}));

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -55,7 +55,7 @@
"@types/node": "18.19.130",
"@types/node-fetch": "2.6.13",
"@types/yargs": "17.0.35",
"gettext-extractor": "4.0.1",
"gettext-extractor": "4.0.3",
"gulp": "4.0.2",
"html-entities": "1.4.0",
"jest": "29.7.0",

View File

@@ -204,6 +204,9 @@
"android-v3.5.9": true,
"android-v3.6.12": true,
"ios-v13.6.1": true,
"v3.5.13": true
"v3.5.13": true,
"v3.6.3": true,
"android-v3.6.13": true,
"ios-v13.6.2": true
}
}

View File

@@ -114,7 +114,7 @@
"alt": "For those in need of immediate academic assistance, EssayService offers a fast and reliable service to write my essay for me now, ensuring high-quality results within tight deadlines"
},
{
"url": " https://thenationonlineng.net/casino-en-ligne/casino-en-ligne-payant-au-canada/",
"url": "https://thenationonlineng.net/casino-en-ligne/casino-en-ligne-payant-au-canada/",
"title": "casino en ligne le plus payant",
"imageName": "TheNationOnline.jpg",
"alt": "casino en ligne le plus payant"

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/transcribe",
"version": "3.6.5",
"version": "3.6.6",
"private": true,
"scripts": {
"rebuild": "yarn clean && yarn build && yarn tsc",

View File

@@ -1,5 +1,43 @@
# Joplin Android Changelog
## [android-v3.6.13](https://github.com/laurent22/joplin/releases/tag/android-v3.6.13) - 2026-03-02T17:36:26Z
- New: Add an import txt function (#13742 by [@mrjo118](https://github.com/mrjo118))
- Improved: Add new option to disable the Joplin icon for internal note links (#14503) (#12220 by [@Harsh16gupta](https://github.com/Harsh16gupta))
- Improved: Add waving hand emoji to welcome notebook (#14398) (#13755 by Ashutosh Singh)
- Improved: Markdown editor: Enable in-editor rendering by default (#13878) (#13215 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Move editor settings to dedicated editor section (#14403) (#13216 by [@kanishka0411](https://github.com/kanishka0411))
- Improved: Prevent race condition when refreshing note contents on mobile (#14486 by [@mrjo118](https://github.com/mrjo118))
- Improved: Refresh note when updated via the API when in edit mode (#14378 by [@mrjo118](https://github.com/mrjo118))
- Improved: Remember the viewing / editing mode (updated) (#14363) (#11521 by [@mrjo118](https://github.com/mrjo118))
- Improved: Remove redundant navigation history for notes or folders which were deleted (#13428 by [@mrjo118](https://github.com/mrjo118))
- Improved: Store note history settings in sync info (#14449) (#14336 by [@yugalkaushik](https://github.com/yugalkaushik))
- Improved: Trigger a note refresh when viewing or editing a note and the contents are updated via the API (#13671 by [@mrjo118](https://github.com/mrjo118))
- Improved: Updated packages @react-native-community/datetimepicker (v8.5.1), form-data (v4.0.5), react (v19.1.5), react-native-localize (v3.6.0), react-native-share (v12.2.1), react-native-svg (v15.14.0), react-native-webview (v13.16.0), sharp (v0.34.5), style-to-js (v1.1.21)
- Improved: Upgrade React Native to v0.81.6 (#14330 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Add a reveal in notebook option for notes (#14338) (#14255 by [@mrjo118](https://github.com/mrjo118))
- Fixed: Defer starting revision service maintenance until the initial sync has completed (#14394) (#13660 by [@mrjo118](https://github.com/mrjo118))
- Fixed: Fix additional space included above keyboard (#14272) (#14268 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix biometric 'try again' button is off-screen on certain devices (#14373) (#14195 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix heading links (#14201 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix import/export buttons have wrong background after task completes (#14282) (#14266 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix missing conflict scenario (#13624) (#13611 by [@mrjo118](https://github.com/mrjo118))
- Fixed: Fix profile data not deleted when removing profiles (#14369) (#13871 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix status 400 error on Tomcat WebDAV servers (#14332 by [@horvatkm](https://github.com/horvatkm))
- Fixed: Fix title bar underline missing on some Android devices (#14273) (#14267 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix unexpected conflicts created during sync (#14453 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix unexpected conflicts sometimes created after a full sync (#14388) (#14383 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Handle missing script assets in HTML export (#14442) (#12545 by [@kanishka0411](https://github.com/kanishka0411))
- Fixed: Hide the expand / collapse title button when the title is too long (#14203) (#14194 by [@mrjo118](https://github.com/mrjo118))
- Fixed: Improve dropdown menu positioning (#14288) (#14275 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Make the view / edit note button hidden when an editor plugin is visible (#14458) (#14452 by [@mrjo118](https://github.com/mrjo118))
- Fixed: Opt out of Android 16 predictive back behavior (#14281) (#14265 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Reset undo/redo button state when toggling an editor plugin (#14444) (#14387 by [@mrjo118](https://github.com/mrjo118))
- Fixed: Support include_deleted parameter for GET /folders endpoint (#14421) (#14335 by Sriram Varun Kumar)
- Fixed: Uses consistent padding in plugin info dialog (#14466) (#11793 by [@Nagmani001](https://github.com/Nagmani001))
- Fixed: When cleaning old revisions, ensure revisions are merged for all revision branches (#13795) (#13782 by [@mrjo118](https://github.com/mrjo118))
- Fixed: YouTube videos are displayed inline too (#14370) (#14352)
## [android-v3.6.12](https://github.com/laurent22/joplin/releases/tag/android-v3.6.12) - 2026-02-04T14:08:14Z
- New: Add more error information when the profile is corrupted (f075b56)

View File

@@ -1,5 +1,63 @@
# Joplin Desktop Changelog
## [v3.6.3](https://github.com/laurent22/joplin/releases/tag/v3.6.3) (Pre-release) - 2026-03-02T18:21:09Z
- New: Add context menu to non-image resources in Markdown editor ([#14402](https://github.com/laurent22/joplin/issues/14402))
- New: Add support for FrontMatter block rendering in notes ([#14256](https://github.com/laurent22/joplin/issues/14256))
- New: Api: Add a delete revisions for note API ([#13882](https://github.com/laurent22/joplin/issues/13882) by [@mrjo118](https://github.com/mrjo118))
- Improved: Add Close Window shortcut and menu item on Windows/Linux ([#14317](https://github.com/laurent22/joplin/issues/14317)) ([#13794](https://github.com/laurent22/joplin/issues/13794) by Ronald Eddy Jr)
- Improved: Add a text layer over OCR-ed PDF files to make them accessible ([#14390](https://github.com/laurent22/joplin/issues/14390)) ([#12400](https://github.com/laurent22/joplin/issues/12400))
- Improved: Add editor and sync target to about dialog ([#14443](https://github.com/laurent22/joplin/issues/14443)) ([#9336](https://github.com/laurent22/joplin/issues/9336) by Ashutosh Singh)
- Improved: Add new option to disable the Joplin icon for internal note links ([#14503](https://github.com/laurent22/joplin/issues/14503)) ([#12220](https://github.com/laurent22/joplin/issues/12220) by [@Harsh16gupta](https://github.com/Harsh16gupta))
- Improved: Add waving hand emoji to welcome notebook ([#14398](https://github.com/laurent22/joplin/issues/14398)) ([#13755](https://github.com/laurent22/joplin/issues/13755) by Ashutosh Singh)
- Improved: Display context menu when right-clicking an image in Markdown editor ([#14209](https://github.com/laurent22/joplin/issues/14209))
- Improved: Display percentage completion of checkbox lists in note list ([#14312](https://github.com/laurent22/joplin/issues/14312)) ([#14292](https://github.com/laurent22/joplin/issues/14292))
- Improved: Exclude trashed notebooks from the list of notebooks on the move to notebook dialog ([#13572](https://github.com/laurent22/joplin/issues/13572) by [@mrjo118](https://github.com/mrjo118))
- Improved: Fix Escape key not closing PromptDialog ([#14325](https://github.com/laurent22/joplin/issues/14325)) ([#14301](https://github.com/laurent22/joplin/issues/14301) by Ronald Eddy Jr)
- Improved: Importing from OneNote: Enable stricter path sanitization on Windows ([#14321](https://github.com/laurent22/joplin/issues/14321) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Importing from OneNote: Improve handwriting import ([#14305](https://github.com/laurent22/joplin/issues/14305) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Importing from OneNote: Make "module not found" message more useful ([#14324](https://github.com/laurent22/joplin/issues/14324) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Markdown Editor: Make code block highlighting closer to the viewer ([#14168](https://github.com/laurent22/joplin/issues/14168)) ([#14158](https://github.com/laurent22/joplin/issues/14158) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Markdown editor: Enable in-editor rendering by default ([#13878](https://github.com/laurent22/joplin/issues/13878)) ([#13215](https://github.com/laurent22/joplin/issues/13215) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Markdown: Allow specifying the start/end of audio, videos, and PDFs ([#14257](https://github.com/laurent22/joplin/issues/14257)) ([#9745](https://github.com/laurent22/joplin/issues/9745) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Move editor settings to dedicated editor section ([#14403](https://github.com/laurent22/joplin/issues/14403)) ([#13216](https://github.com/laurent22/joplin/issues/13216) by [@kanishka0411](https://github.com/kanishka0411))
- Improved: OneNote import: Import invalid attachments as empty attachments ([#14177](https://github.com/laurent22/joplin/issues/14177) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Store note history settings in sync info ([#14449](https://github.com/laurent22/joplin/issues/14449)) ([#14336](https://github.com/laurent22/joplin/issues/14336) by [@yugalkaushik](https://github.com/yugalkaushik))
- Improved: Sync: Make resource processing in read-only shares more reliable ([#14204](https://github.com/laurent22/joplin/issues/14204) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Synchronise pending changes when closing the app ([#13845](https://github.com/laurent22/joplin/issues/13845) by [@mrjo118](https://github.com/mrjo118))
- Improved: Updated packages @axe-core/playwright (v4.11.0), @playwright/test (v1.56.1), @rollup/plugin-commonjs (v28.0.9), @rollup/plugin-replace (v6.0.3), dayjs (v1.11.19), electron-updater (v6.6.8), form-data (v4.0.5), nan (v2.23.1), react (v19.1.5), sass (v1.93.3), sharp (v0.34.5), style-to-js (v1.1.21), turndown (v7.2.2)
- Improved: Upgrade tar to v7.5.8 ([#14464](https://github.com/laurent22/joplin/issues/14464) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: "Copy dev mode command to clipboard" does not work when path contains spaces ([#14432](https://github.com/laurent22/joplin/issues/14432)) ([#14328](https://github.com/laurent22/joplin/issues/14328) by Sriram Varun Kumar)
- Fixed: Copy and paste from markdown preview includes search highlight effect ([#14493](https://github.com/laurent22/joplin/issues/14493)) ([#12385](https://github.com/laurent22/joplin/issues/12385) by [@yentropysack](https://github.com/yentropysack))
- Fixed: Copying from markdown preview including theme background colour ([#14474](https://github.com/laurent22/joplin/issues/14474)) ([#12401](https://github.com/laurent22/joplin/issues/12401) by [@Harsh16gupta](https://github.com/Harsh16gupta))
- Fixed: Dark mode not respected in emoji window ([#14303](https://github.com/laurent22/joplin/issues/14303)) ([#14080](https://github.com/laurent22/joplin/issues/14080) by Lakshay Manchanda)
- Fixed: Defer starting revision service maintenance until the initial sync has completed ([#14394](https://github.com/laurent22/joplin/issues/14394)) ([#13660](https://github.com/laurent22/joplin/issues/13660) by [@mrjo118](https://github.com/mrjo118))
- Fixed: Fix UI freeze when closing plugin dialog with Escape key ([#14477](https://github.com/laurent22/joplin/issues/14477) by Sriram Varun Kumar)
- Fixed: Fix editor plugins receiving stale note body during navigation ([#14513](https://github.com/laurent22/joplin/issues/14513))
- Fixed: Fix missing conflict scenario ([#13624](https://github.com/laurent22/joplin/issues/13624)) ([#13611](https://github.com/laurent22/joplin/issues/13611) by [@mrjo118](https://github.com/mrjo118))
- Fixed: Fix sidebar scroll jump when expanding/collapsing folders ([#14467](https://github.com/laurent22/joplin/issues/14467)) ([#13679](https://github.com/laurent22/joplin/issues/13679) by [@kanishka0411](https://github.com/kanishka0411))
- Fixed: Fix status 400 error on Tomcat WebDAV servers ([#14332](https://github.com/laurent22/joplin/issues/14332) by [@horvatkm](https://github.com/horvatkm))
- Fixed: Fix undo/redo menu items in the Rich Text and Markdown editors ([#14218](https://github.com/laurent22/joplin/issues/14218)) ([#14216](https://github.com/laurent22/joplin/issues/14216) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix unexpected conflicts created during sync ([#14453](https://github.com/laurent22/joplin/issues/14453) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix unexpected conflicts sometimes created after a full sync ([#14388](https://github.com/laurent22/joplin/issues/14388)) ([#14383](https://github.com/laurent22/joplin/issues/14383) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Handle missing script assets in HTML export ([#14442](https://github.com/laurent22/joplin/issues/14442)) ([#12545](https://github.com/laurent22/joplin/issues/12545) by [@kanishka0411](https://github.com/kanishka0411))
- Fixed: Improve context menu handling in secondary windows ([#14262](https://github.com/laurent22/joplin/issues/14262)) ([#13540](https://github.com/laurent22/joplin/issues/13540) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Make conflicts caused by resource duplication less likely ([#14188](https://github.com/laurent22/joplin/issues/14188)) ([#13793](https://github.com/laurent22/joplin/issues/13793) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Make notebook search accent-insensitive in GotoAnything ([#14360](https://github.com/laurent22/joplin/issues/14360)) ([#14350](https://github.com/laurent22/joplin/issues/14350) by [@yousef-genedy](https://github.com/yousef-genedy))
- Fixed: Markdown editor: Fix error logged in "hide markdown" mode for certain markup ([#14179](https://github.com/laurent22/joplin/issues/14179) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Markdown editor: Scroll jumps in notes with many inline images ([#13955](https://github.com/laurent22/joplin/issues/13955)) ([#13933](https://github.com/laurent22/joplin/issues/13933) by [@bwat47](https://github.com/bwat47))
- Fixed: Markdown editor: Upgrade most CodeMirror dependencies ([#14186](https://github.com/laurent22/joplin/issues/14186)) ([#14009](https://github.com/laurent22/joplin/issues/14009) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: OneNote import: Fix onepkg import stops after the first section fails to import ([#14246](https://github.com/laurent22/joplin/issues/14246) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: OneNote importer: Skip importing ink when ID lookup fails ([#14230](https://github.com/laurent22/joplin/issues/14230)) ([#14210](https://github.com/laurent22/joplin/issues/14210) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Prevent 4th backtick when closing fenced code block ([#14423](https://github.com/laurent22/joplin/issues/14423)) ([#12569](https://github.com/laurent22/joplin/issues/12569) by [@Harsh16gupta](https://github.com/Harsh16gupta))
- Fixed: Remove empty hidden divs from ENEX imports ([#14411](https://github.com/laurent22/joplin/issues/14411)) ([#14139](https://github.com/laurent22/joplin/issues/14139) by [@yugalkaushik](https://github.com/yugalkaushik))
- Fixed: Support include_deleted parameter for GET /folders endpoint ([#14421](https://github.com/laurent22/joplin/issues/14421)) ([#14335](https://github.com/laurent22/joplin/issues/14335) by Sriram Varun Kumar)
- Fixed: Unlinked resource deletion: Fix resources attached only via reference links are auto-deleted ([#14178](https://github.com/laurent22/joplin/issues/14178) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: When cleaning old revisions, ensure revisions are merged for all revision branches ([#13795](https://github.com/laurent22/joplin/issues/13795)) ([#13782](https://github.com/laurent22/joplin/issues/13782) by [@mrjo118](https://github.com/mrjo118))
- Fixed: YouTube videos are displayed inline too ([#14370](https://github.com/laurent22/joplin/issues/14370)) ([#14352](https://github.com/laurent22/joplin/issues/14352))
- Fixed: `.onepkg` file import: Fix import failure when notebook titles contain certain Unicode characters ([#14090](https://github.com/laurent22/joplin/issues/14090)) ([#14084](https://github.com/laurent22/joplin/issues/14084) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Security: Strengthen Content-Security-Policy ([#14316](https://github.com/laurent22/joplin/issues/14316) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
## [v3.5.13](https://github.com/laurent22/joplin/releases/tag/v3.5.13) - 2026-02-25T21:19:11Z
- Improved: Importing from OneNote: Enable stricter path sanitization on Windows ([#14321](https://github.com/laurent22/joplin/issues/14321) by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))

View File

@@ -1,5 +1,39 @@
# Joplin iOS Changelog
## [ios-v13.6.2](https://github.com/laurent22/joplin/releases/tag/ios-v13.6.2) - 2026-03-02T17:51:24Z
- New: Add an import txt function (#13742 by [@mrjo118](https://github.com/mrjo118))
- Improved: Add new option to disable the Joplin icon for internal note links (#14503) (#12220 by [@Harsh16gupta](https://github.com/Harsh16gupta))
- Improved: Add waving hand emoji to welcome notebook (#14398) (#13755 by Ashutosh Singh)
- Improved: Markdown editor: Enable in-editor rendering by default (#13878) (#13215 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Move editor settings to dedicated editor section (#14403) (#13216 by [@kanishka0411](https://github.com/kanishka0411))
- Improved: Prevent race condition when refreshing note contents on mobile (#14486 by [@mrjo118](https://github.com/mrjo118))
- Improved: Refresh note when updated via the API when in edit mode (#14378 by [@mrjo118](https://github.com/mrjo118))
- Improved: Remember the viewing / editing mode (updated) (#14363) (#11521 by [@mrjo118](https://github.com/mrjo118))
- Improved: Remove redundant navigation history for notes or folders which were deleted (#13428 by [@mrjo118](https://github.com/mrjo118))
- Improved: Store note history settings in sync info (#14449) (#14336 by [@yugalkaushik](https://github.com/yugalkaushik))
- Improved: Trigger a note refresh when viewing or editing a note and the contents are updated via the API (#13671 by [@mrjo118](https://github.com/mrjo118))
- Improved: Updated packages @react-native-community/datetimepicker (v8.5.1), form-data (v4.0.5), react (v19.1.5), react-native-localize (v3.6.0), react-native-share (v12.2.1), react-native-svg (v15.14.0), react-native-webview (v13.16.0), sharp (v0.34.5), style-to-js (v1.1.21)
- Improved: Upgrade React Native to v0.81.6 (#14330 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Add a reveal in notebook option for notes (#14338) (#14255 by [@mrjo118](https://github.com/mrjo118))
- Fixed: Defer starting revision service maintenance until the initial sync has completed (#14394) (#13660 by [@mrjo118](https://github.com/mrjo118))
- Fixed: Fix biometric 'try again' button is off-screen on certain devices (#14373) (#14195 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix heading links (#14201 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix missing conflict scenario (#13624) (#13611 by [@mrjo118](https://github.com/mrjo118))
- Fixed: Fix profile data not deleted when removing profiles (#14369) (#13871 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix status 400 error on Tomcat WebDAV servers (#14332 by [@horvatkm](https://github.com/horvatkm))
- Fixed: Fix unexpected conflicts created during sync (#14453 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix unexpected conflicts sometimes created after a full sync (#14388) (#14383 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Handle missing script assets in HTML export (#14442) (#12545 by [@kanishka0411](https://github.com/kanishka0411))
- Fixed: Hide the expand / collapse title button when the title is too long (#14203) (#14194 by [@mrjo118](https://github.com/mrjo118))
- Fixed: Improve dropdown menu positioning (#14288) (#14275 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Make the view / edit note button hidden when an editor plugin is visible (#14458) (#14452 by [@mrjo118](https://github.com/mrjo118))
- Fixed: Reset undo/redo button state when toggling an editor plugin (#14444) (#14387 by [@mrjo118](https://github.com/mrjo118))
- Fixed: Support include_deleted parameter for GET /folders endpoint (#14421) (#14335 by Sriram Varun Kumar)
- Fixed: Uses consistent padding in plugin info dialog (#14466) (#11793 by [@Nagmani001](https://github.com/Nagmani001))
- Fixed: When cleaning old revisions, ensure revisions are merged for all revision branches (#13795) (#13782 by [@mrjo118](https://github.com/mrjo118))
- Fixed: YouTube videos are displayed inline too (#14370) (#14352)
## [ios-v13.6.1](https://github.com/laurent22/joplin/releases/tag/ios-v13.6.1) - 2026-02-04T14:45:26Z
- New: Add more error information when the profile is corrupted (f075b56)

View File

@@ -1,5 +1,9 @@
# Transcribe Changelog
## [transcribe-v3.6.6](https://github.com/laurent22/joplin/releases/tag/transcribe-v3.6.6) - 2026-02-26T21:08:33Z
- Improved: Upgraded image to node: 24-bookworm to fix build issue (513341f)
## [transcribe-v3.6.5](https://github.com/laurent22/joplin/releases/tag/transcribe-v3.6.5) - 2026-02-26T15:06:49Z
- Fixed: Fixed location of llamacpp library (df10bbd)

View File

@@ -1,14 +1,14 @@
---
updated: 2026-02-01T02:35:07Z
updated: 2026-03-01T02:32:04Z
---
# Joplin statistics
| Name | Value |
| ----- | ----- |
| Total Windows downloads | 7,693,776 |
| Total macOs downloads | 2,092,639 |
| Total Linux downloads | 1,732,101 |
| Total Windows downloads | 7,836,256 |
| Total macOs downloads | 2,103,860 |
| Total Linux downloads | 1,758,947 |
| Windows % | 67% |
| macOS % | 18% |
| Linux % | 15% |
@@ -17,278 +17,279 @@ updated: 2026-02-01T02:35:07Z
| Version | Date | Windows | macOS | Linux | Total |
| ----- | ----- | ----- | ----- | ----- | ----- |
| [v3.6.2](https://github.com/laurent22/joplin/releases/tag/v3.6.2) (p) | 2026-01-18T20:10:43Z | 2,014 | 375 | 652 | 3,041 |
| [v3.6.1](https://github.com/laurent22/joplin/releases/tag/v3.6.1) (p) | 2026-01-17T14:17:29Z | 451 | 86 | 90 | 627 |
| [v3.5.12](https://github.com/laurent22/joplin/releases/tag/v3.5.12) | 2026-01-17T14:20:33Z | 109,044 | 8,240 | 15,151 | 132,435 |
| [v3.5.11](https://github.com/laurent22/joplin/releases/tag/v3.5.11) | 2026-01-12T15:17:25Z | 74,153 | 6,083 | 5,681 | 85,917 |
| [v3.5.10](https://github.com/laurent22/joplin/releases/tag/v3.5.10) (p) | 2026-01-08T20:18:15Z | 999 | 167 | 256 | 1,422 |
| [v3.5.9](https://github.com/laurent22/joplin/releases/tag/v3.5.9) (p) | 2025-11-30T19:11:11Z | 5,307 | 948 | 2,084 | 8,339 |
| [v3.5.7](https://github.com/laurent22/joplin/releases/tag/v3.5.7) (p) | 2025-11-22T08:35:36Z | 1,835 | 329 | 1,116 | 3,280 |
| [v3.5.6](https://github.com/laurent22/joplin/releases/tag/v3.5.6) (p) | 2025-10-29T14:48:46Z | 4,250 | 675 | 1,462 | 6,387 |
| [v3.5.5](https://github.com/laurent22/joplin/releases/tag/v3.5.5) (p) | 2025-10-18T10:31:09Z | 2,436 | 374 | 587 | 3,397 |
| [v3.5.4](https://github.com/laurent22/joplin/releases/tag/v3.5.4) (p) | 2025-10-10T17:19:58Z | 2,521 | 264 | 336 | 3,121 |
| [v3.4.12](https://github.com/laurent22/joplin/releases/tag/v3.4.12) | 2025-09-09T21:35:47Z | 367,244 | 34,261 | 100,031 | 501,536 |
| [v3.4.10](https://github.com/laurent22/joplin/releases/tag/v3.4.10) | 2025-09-01T12:25:22Z | 77,546 | 7,864 | 10,380 | 95,790 |
| [v3.4.7](https://github.com/laurent22/joplin/releases/tag/v3.4.7) (p) | 2025-08-23T10:49:54Z | 8,074 | 385 | 603 | 9,062 |
| [v3.4.6](https://github.com/laurent22/joplin/releases/tag/v3.4.6) (p) | 2025-08-20T20:30:35Z | 42,918 | 5,175 | 2,645 | 50,738 |
| [v3.5.13](https://github.com/laurent22/joplin/releases/tag/v3.5.13) | 2026-02-25T21:19:11Z | 41,765 | 3,352 | 2,932 | 48,049 |
| [v3.6.2](https://github.com/laurent22/joplin/releases/tag/v3.6.2) (p) | 2026-01-18T20:10:43Z | 3,758 | 947 | 2,053 | 6,758 |
| [v3.6.1](https://github.com/laurent22/joplin/releases/tag/v3.6.1) (p) | 2026-01-17T14:17:29Z | 479 | 92 | 101 | 672 |
| [v3.5.12](https://github.com/laurent22/joplin/releases/tag/v3.5.12) | 2026-01-17T14:20:33Z | 196,916 | 14,452 | 36,257 | 247,625 |
| [v3.5.11](https://github.com/laurent22/joplin/releases/tag/v3.5.11) | 2026-01-12T15:17:25Z | 74,487 | 6,148 | 5,714 | 86,349 |
| [v3.5.10](https://github.com/laurent22/joplin/releases/tag/v3.5.10) (p) | 2026-01-08T20:18:15Z | 1,002 | 171 | 258 | 1,431 |
| [v3.5.9](https://github.com/laurent22/joplin/releases/tag/v3.5.9) (p) | 2025-11-30T19:11:11Z | 5,378 | 949 | 2,090 | 8,417 |
| [v3.5.7](https://github.com/laurent22/joplin/releases/tag/v3.5.7) (p) | 2025-11-22T08:35:36Z | 1,837 | 330 | 1,123 | 3,290 |
| [v3.5.6](https://github.com/laurent22/joplin/releases/tag/v3.5.6) (p) | 2025-10-29T14:48:46Z | 4,254 | 684 | 1,464 | 6,402 |
| [v3.5.5](https://github.com/laurent22/joplin/releases/tag/v3.5.5) (p) | 2025-10-18T10:31:09Z | 2,437 | 374 | 588 | 3,399 |
| [v3.5.4](https://github.com/laurent22/joplin/releases/tag/v3.5.4) (p) | 2025-10-10T17:19:58Z | 2,525 | 264 | 339 | 3,128 |
| [v3.4.12](https://github.com/laurent22/joplin/releases/tag/v3.4.12) | 2025-09-09T21:35:47Z | 367,639 | 34,318 | 100,212 | 502,169 |
| [v3.4.10](https://github.com/laurent22/joplin/releases/tag/v3.4.10) | 2025-09-01T12:25:22Z | 77,785 | 7,907 | 10,416 | 96,108 |
| [v3.4.7](https://github.com/laurent22/joplin/releases/tag/v3.4.7) (p) | 2025-08-23T10:49:54Z | 8,098 | 385 | 606 | 9,089 |
| [v3.4.6](https://github.com/laurent22/joplin/releases/tag/v3.4.6) (p) | 2025-08-20T20:30:35Z | 43,079 | 5,201 | 2,666 | 50,946 |
| [v3.4.5](https://github.com/laurent22/joplin/releases/tag/v3.4.5) (p) | 2025-08-10T12:49:30Z | 1,788 | 399 | 450 | 2,637 |
| [v3.4.4](https://github.com/laurent22/joplin/releases/tag/v3.4.4) (p) | 2025-08-13T16:46:39Z | 113 | 60 | 42 | 215 |
| [v3.4.3](https://github.com/laurent22/joplin/releases/tag/v3.4.3) (p) | 2025-07-25T19:49:44Z | 2,005 | 505 | 569 | 3,079 |
| [v3.4.3](https://github.com/laurent22/joplin/releases/tag/v3.4.3) (p) | 2025-07-25T19:49:44Z | 2,005 | 505 | 571 | 3,081 |
| [v3.4.2](https://github.com/laurent22/joplin/releases/tag/v3.4.2) (p) | 2025-07-24T10:43:55Z | 645 | 161 | 130 | 936 |
| [v3.3.13](https://github.com/laurent22/joplin/releases/tag/v3.3.13) | 2025-06-09T20:13:30Z | 266,081 | 27,230 | 61,031 | 354,342 |
| [v3.4.1](https://github.com/laurent22/joplin/releases/tag/v3.4.1) (p) | 2025-05-20T09:59:39Z | 3,967 | 1,163 | 1,332 | 6,462 |
| [v3.3.12](https://github.com/laurent22/joplin/releases/tag/v3.3.12) | 2025-05-04T18:12:23Z | 165,618 | 21,592 | 28,244 | 215,454 |
| [v3.3.10](https://github.com/laurent22/joplin/releases/tag/v3.3.10) | 2025-05-02T19:46:15Z | 32,250 | 5,165 | 1,847 | 39,262 |
| [v3.3.9](https://github.com/laurent22/joplin/releases/tag/v3.3.9) | 2025-05-01T21:02:12Z | 31,181 | 6,157 | 1,203 | 38,541 |
| [v3.3.7](https://github.com/laurent22/joplin/releases/tag/v3.3.7) (p) | 2025-04-29T13:47:19Z | 771 | 0 | 182 | 953 |
| [v3.3.13](https://github.com/laurent22/joplin/releases/tag/v3.3.13) | 2025-06-09T20:13:30Z | 266,817 | 27,292 | 61,368 | 355,477 |
| [v3.4.1](https://github.com/laurent22/joplin/releases/tag/v3.4.1) (p) | 2025-05-20T09:59:39Z | 3,968 | 1,163 | 1,332 | 6,463 |
| [v3.3.12](https://github.com/laurent22/joplin/releases/tag/v3.3.12) | 2025-05-04T18:12:23Z | 166,534 | 21,595 | 28,246 | 216,375 |
| [v3.3.10](https://github.com/laurent22/joplin/releases/tag/v3.3.10) | 2025-05-02T19:46:15Z | 32,967 | 5,165 | 1,848 | 39,980 |
| [v3.3.9](https://github.com/laurent22/joplin/releases/tag/v3.3.9) | 2025-05-01T21:02:12Z | 31,912 | 6,158 | 1,204 | 39,274 |
| [v3.3.7](https://github.com/laurent22/joplin/releases/tag/v3.3.7) (p) | 2025-04-29T13:47:19Z | 772 | 0 | 182 | 954 |
| [v3.3.6](https://github.com/laurent22/joplin/releases/tag/v3.3.6) (p) | 2025-04-24T12:27:20Z | 1,071 | 303 | 262 | 1,636 |
| [v3.3.5](https://github.com/laurent22/joplin/releases/tag/v3.3.5) (p) | 2025-04-17T13:40:31Z | 1,402 | 319 | 316 | 2,037 |
| [v3.3.4](https://github.com/laurent22/joplin/releases/tag/v3.3.4) (p) | 2025-04-07T20:23:35Z | 1,644 | 427 | 382 | 2,453 |
| [v3.3.3](https://github.com/laurent22/joplin/releases/tag/v3.3.3) (p) | 2025-03-16T11:52:33Z | 2,717 | 844 | 991 | 4,552 |
| [v3.2.13](https://github.com/laurent22/joplin/releases/tag/v3.2.13) | 2025-02-28T14:38:21Z | 224,897 | 33,047 | 44,720 | 302,664 |
| [v3.3.4](https://github.com/laurent22/joplin/releases/tag/v3.3.4) (p) | 2025-04-07T20:23:35Z | 1,644 | 427 | 383 | 2,454 |
| [v3.3.3](https://github.com/laurent22/joplin/releases/tag/v3.3.3) (p) | 2025-03-16T11:52:33Z | 2,718 | 844 | 992 | 4,554 |
| [v3.2.13](https://github.com/laurent22/joplin/releases/tag/v3.2.13) | 2025-02-28T14:38:21Z | 225,138 | 33,084 | 44,791 | 303,013 |
| [v3.3.2](https://github.com/laurent22/joplin/releases/tag/v3.3.2) (p) | 2025-02-19T17:34:26Z | 2,375 | 595 | 665 | 3,635 |
| [v3.3.1](https://github.com/laurent22/joplin/releases/tag/v3.3.1) (p) | 2025-02-16T17:06:26Z | 871 | 217 | 186 | 1,274 |
| [v3.2.12](https://github.com/laurent22/joplin/releases/tag/v3.2.12) | 2025-01-23T23:52:04Z | 153,757 | 25,408 | 28,064 | 207,229 |
| [v3.2.11](https://github.com/laurent22/joplin/releases/tag/v3.2.11) | 2025-01-13T17:48:21Z | 67,721 | 14,884 | 6,903 | 89,508 |
| [v3.2.10](https://github.com/laurent22/joplin/releases/tag/v3.2.10) (p) | 2025-01-10T10:17:28Z | 2,987 | 195 | 205 | 3,387 |
| [v3.2.9](https://github.com/laurent22/joplin/releases/tag/v3.2.9) (p) | 2025-01-09T22:58:42Z | 394 | 120 | 70 | 584 |
| [v3.2.7](https://github.com/laurent22/joplin/releases/tag/v3.2.7) (p) | 2025-01-06T16:35:41Z | 909 | 185 | 907 | 2,001 |
| [v3.2.6](https://github.com/laurent22/joplin/releases/tag/v3.2.6) (p) | 2024-12-23T21:54:40Z | 1,789 | 364 | 494 | 2,647 |
| [v3.3.1](https://github.com/laurent22/joplin/releases/tag/v3.3.1) (p) | 2025-02-16T17:06:26Z | 872 | 220 | 187 | 1,279 |
| [v3.2.12](https://github.com/laurent22/joplin/releases/tag/v3.2.12) | 2025-01-23T23:52:04Z | 153,893 | 25,453 | 28,112 | 207,458 |
| [v3.2.11](https://github.com/laurent22/joplin/releases/tag/v3.2.11) | 2025-01-13T17:48:21Z | 67,898 | 14,909 | 6,928 | 89,735 |
| [v3.2.10](https://github.com/laurent22/joplin/releases/tag/v3.2.10) (p) | 2025-01-10T10:17:28Z | 3,032 | 195 | 205 | 3,432 |
| [v3.2.9](https://github.com/laurent22/joplin/releases/tag/v3.2.9) (p) | 2025-01-09T22:58:42Z | 394 | 123 | 70 | 587 |
| [v3.2.7](https://github.com/laurent22/joplin/releases/tag/v3.2.7) (p) | 2025-01-06T16:35:41Z | 909 | 185 | 912 | 2,006 |
| [v3.2.6](https://github.com/laurent22/joplin/releases/tag/v3.2.6) (p) | 2024-12-23T21:54:40Z | 1,791 | 364 | 496 | 2,651 |
| [v3.2.5](https://github.com/laurent22/joplin/releases/tag/v3.2.5) (p) | 2024-12-18T10:41:13Z | 1,030 | 229 | 238 | 1,497 |
| [v3.2.4](https://github.com/laurent22/joplin/releases/tag/v3.2.4) (p) | 2024-12-12T17:59:52Z | 1,042 | 182 | 253 | 1,477 |
| [v3.2.3](https://github.com/laurent22/joplin/releases/tag/v3.2.3) (p) | 2024-11-18T00:09:05Z | 2,736 | 585 | 912 | 4,233 |
| [v3.2.1](https://github.com/laurent22/joplin/releases/tag/v3.2.1) (p) | 2024-11-10T16:16:27Z | 1,285 | 268 | 366 | 1,919 |
| [v3.1.24](https://github.com/laurent22/joplin/releases/tag/v3.1.24) | 2024-11-09T15:08:29Z | 210,156 | 33,577 | 43,939 | 287,672 |
| [v3.1.23](https://github.com/laurent22/joplin/releases/tag/v3.1.23) | 2024-11-07T10:56:45Z | 28,642 | 6,768 | 1,563 | 36,973 |
| [v3.1.22](https://github.com/laurent22/joplin/releases/tag/v3.1.22) | 2024-11-05T08:59:32Z | 30,552 | 8,732 | 1,240 | 40,524 |
| [v3.1.20](https://github.com/laurent22/joplin/releases/tag/v3.1.20) | 2024-10-22T12:21:32Z | 96,993 | 19,413 | 13,951 | 130,357 |
| [v3.2.3](https://github.com/laurent22/joplin/releases/tag/v3.2.3) (p) | 2024-11-18T00:09:05Z | 2,738 | 586 | 914 | 4,238 |
| [v3.2.1](https://github.com/laurent22/joplin/releases/tag/v3.2.1) (p) | 2024-11-10T16:16:27Z | 1,287 | 269 | 366 | 1,922 |
| [v3.1.24](https://github.com/laurent22/joplin/releases/tag/v3.1.24) | 2024-11-09T15:08:29Z | 210,337 | 33,591 | 43,977 | 287,905 |
| [v3.1.23](https://github.com/laurent22/joplin/releases/tag/v3.1.23) | 2024-11-07T10:56:45Z | 28,730 | 6,768 | 1,563 | 37,061 |
| [v3.1.22](https://github.com/laurent22/joplin/releases/tag/v3.1.22) | 2024-11-05T08:59:32Z | 30,666 | 8,732 | 1,241 | 40,639 |
| [v3.1.20](https://github.com/laurent22/joplin/releases/tag/v3.1.20) | 2024-10-22T12:21:32Z | 97,191 | 19,414 | 13,951 | 130,556 |
| [v3.1.18](https://github.com/laurent22/joplin/releases/tag/v3.1.18) (p) | 2024-10-11T23:27:10Z | 1,541 | 311 | 605 | 2,457 |
| [v3.1.17](https://github.com/laurent22/joplin/releases/tag/v3.1.17) (p) | 2024-09-26T11:57:54Z | 1,732 | 384 | 548 | 2,664 |
| [v3.1.15](https://github.com/laurent22/joplin/releases/tag/v3.1.15) (p) | 2024-09-17T09:15:10Z | 1,216 | 255 | 513 | 1,984 |
| [v3.1.8](https://github.com/laurent22/joplin/releases/tag/v3.1.8) (p) | 2024-09-08T20:32:44Z | 1,256 | 288 | 351 | 1,895 |
| [v3.1.6](https://github.com/laurent22/joplin/releases/tag/v3.1.6) (p) | 2024-09-02T13:19:40Z | 996 | 260 | 442 | 1,698 |
| [v3.1.4](https://github.com/laurent22/joplin/releases/tag/v3.1.4) (p) | 2024-08-27T17:46:38Z | 979 | 207 | 266 | 1,452 |
| [v3.0.15](https://github.com/laurent22/joplin/releases/tag/v3.0.15) | 2024-08-21T09:19:58Z | 205,455 | 37,888 | 45,228 | 288,571 |
| [v3.1.4](https://github.com/laurent22/joplin/releases/tag/v3.1.4) (p) | 2024-08-27T17:46:38Z | 979 | 208 | 266 | 1,453 |
| [v3.0.15](https://github.com/laurent22/joplin/releases/tag/v3.0.15) | 2024-08-21T09:19:58Z | 205,634 | 37,890 | 45,233 | 288,757 |
| [v3.1.3](https://github.com/laurent22/joplin/releases/tag/v3.1.3) (p) | 2024-08-17T13:08:21Z | 1,272 | 317 | 504 | 2,093 |
| [v3.1.2](https://github.com/laurent22/joplin/releases/tag/v3.1.2) (p) | 2024-08-16T09:00:59Z | 484 | 147 | 107 | 738 |
| [v3.1.1](https://github.com/laurent22/joplin/releases/tag/v3.1.1) (p) | 2024-08-10T11:36:02Z | 1,116 | 229 | 282 | 1,627 |
| [v2.14.23](https://github.com/laurent22/joplin/releases/tag/v2.14.23) | 2024-08-07T11:15:25Z | 10,883 | 2,794 | 656 | 14,333 |
| [v3.0.14](https://github.com/laurent22/joplin/releases/tag/v3.0.14) | 2024-07-28T13:55:50Z | 91,293 | 18,600 | 18,874 | 128,767 |
| [v3.0.12](https://github.com/laurent22/joplin/releases/tag/v3.0.12) | 2024-07-02T17:11:14Z | 46,275 | 12,907 | 7,403 | 66,585 |
| [v3.1.2](https://github.com/laurent22/joplin/releases/tag/v3.1.2) (p) | 2024-08-16T09:00:59Z | 484 | 148 | 109 | 741 |
| [v3.1.1](https://github.com/laurent22/joplin/releases/tag/v3.1.1) (p) | 2024-08-10T11:36:02Z | 1,116 | 231 | 282 | 1,629 |
| [v2.14.23](https://github.com/laurent22/joplin/releases/tag/v2.14.23) | 2024-08-07T11:15:25Z | 10,895 | 2,815 | 657 | 14,367 |
| [v3.0.14](https://github.com/laurent22/joplin/releases/tag/v3.0.14) | 2024-07-28T13:55:50Z | 91,488 | 18,601 | 18,874 | 128,963 |
| [v3.0.12](https://github.com/laurent22/joplin/releases/tag/v3.0.12) | 2024-07-02T17:11:14Z | 46,465 | 12,915 | 7,409 | 66,789 |
| [v3.0.11](https://github.com/laurent22/joplin/releases/tag/v3.0.11) (p) | 2024-06-29T10:20:02Z | 875 | 181 | 284 | 1,340 |
| [v3.0.10](https://github.com/laurent22/joplin/releases/tag/v3.0.10) (p) | 2024-06-19T15:24:07Z | 1,651 | 307 | 577 | 2,535 |
| [v3.0.10](https://github.com/laurent22/joplin/releases/tag/v3.0.10) (p) | 2024-06-19T15:24:07Z | 1,651 | 308 | 578 | 2,537 |
| [v3.0.9](https://github.com/laurent22/joplin/releases/tag/v3.0.9) (p) | 2024-06-12T19:07:50Z | 1,303 | 277 | 395 | 1,975 |
| [v3.0.8](https://github.com/laurent22/joplin/releases/tag/v3.0.8) (p) | 2024-05-22T14:20:45Z | 2,696 | 0 | 955 | 3,651 |
| [v2.14.22](https://github.com/laurent22/joplin/releases/tag/v2.14.22) | 2024-05-22T19:19:02Z | 144,593 | 30,950 | 25,579 | 201,122 |
| [v3.0.6](https://github.com/laurent22/joplin/releases/tag/v3.0.6) (p) | 2024-04-27T13:16:04Z | 3,040 | 705 | 879 | 4,624 |
| [v3.0.3](https://github.com/laurent22/joplin/releases/tag/v3.0.3) (p) | 2024-04-18T15:41:38Z | 1,518 | 339 | 348 | 2,205 |
| [v3.0.2](https://github.com/laurent22/joplin/releases/tag/v3.0.2) (p) | 2024-03-21T18:18:49Z | 2,909 | 762 | 1,125 | 4,796 |
| [v2.14.20](https://github.com/laurent22/joplin/releases/tag/v2.14.20) | 2024-03-18T17:05:17Z | 194,812 | 39,645 | 38,415 | 272,872 |
| [v2.14.19](https://github.com/laurent22/joplin/releases/tag/v2.14.19) | 2024-03-08T10:45:16Z | 66,070 | 18,495 | 8,581 | 93,146 |
| [v2.14.17](https://github.com/laurent22/joplin/releases/tag/v2.14.17) | 2024-03-01T18:10:26Z | 64,523 | 18,659 | 7,646 | 90,828 |
| [v2.14.16](https://github.com/laurent22/joplin/releases/tag/v2.14.16) (p) | 2024-02-22T22:49:10Z | 1,383 | 302 | 394 | 2,079 |
| [v2.14.15](https://github.com/laurent22/joplin/releases/tag/v2.14.15) (p) | 2024-02-19T11:24:57Z | 886 | 197 | 208 | 1,291 |
| [v3.0.8](https://github.com/laurent22/joplin/releases/tag/v3.0.8) (p) | 2024-05-22T14:20:45Z | 2,697 | 0 | 955 | 3,652 |
| [v2.14.22](https://github.com/laurent22/joplin/releases/tag/v2.14.22) | 2024-05-22T19:19:02Z | 144,684 | 30,993 | 25,604 | 201,281 |
| [v3.0.6](https://github.com/laurent22/joplin/releases/tag/v3.0.6) (p) | 2024-04-27T13:16:04Z | 3,041 | 705 | 879 | 4,625 |
| [v3.0.3](https://github.com/laurent22/joplin/releases/tag/v3.0.3) (p) | 2024-04-18T15:41:38Z | 1,518 | 341 | 348 | 2,207 |
| [v3.0.2](https://github.com/laurent22/joplin/releases/tag/v3.0.2) (p) | 2024-03-21T18:18:49Z | 2,913 | 764 | 1,125 | 4,802 |
| [v2.14.20](https://github.com/laurent22/joplin/releases/tag/v2.14.20) | 2024-03-18T17:05:17Z | 194,938 | 39,701 | 38,483 | 273,122 |
| [v2.14.19](https://github.com/laurent22/joplin/releases/tag/v2.14.19) | 2024-03-08T10:45:16Z | 66,124 | 18,505 | 8,594 | 93,223 |
| [v2.14.17](https://github.com/laurent22/joplin/releases/tag/v2.14.17) | 2024-03-01T18:10:26Z | 64,551 | 18,660 | 7,648 | 90,859 |
| [v2.14.16](https://github.com/laurent22/joplin/releases/tag/v2.14.16) (p) | 2024-02-22T22:49:10Z | 1,386 | 304 | 394 | 2,084 |
| [v2.14.15](https://github.com/laurent22/joplin/releases/tag/v2.14.15) (p) | 2024-02-19T11:24:57Z | 886 | 198 | 208 | 1,292 |
| [v2.14.14](https://github.com/laurent22/joplin/releases/tag/v2.14.14) (p) | 2024-02-10T16:03:08Z | 1,305 | 259 | 399 | 1,963 |
| [v2.14.13](https://github.com/laurent22/joplin/releases/tag/v2.14.13) (p) | 2024-02-09T16:31:54Z | 457 | 135 | 106 | 698 |
| [v2.14.12](https://github.com/laurent22/joplin/releases/tag/v2.14.12) (p) | 2024-02-03T12:11:47Z | 1,014 | 233 | 268 | 1,515 |
| [v2.14.11](https://github.com/laurent22/joplin/releases/tag/v2.14.11) (p) | 2024-01-26T11:53:05Z | 1,307 | 275 | 493 | 2,075 |
| [v2.14.10](https://github.com/laurent22/joplin/releases/tag/v2.14.10) (p) | 2024-01-18T22:45:04Z | 2,130 | 286 | 390 | 2,806 |
| [v2.13.15](https://github.com/laurent22/joplin/releases/tag/v2.13.15) | 2024-01-15T13:01:19Z | 151,241 | 34,191 | 29,307 | 214,739 |
| [v2.13.14](https://github.com/laurent22/joplin/releases/tag/v2.13.14) | 2024-01-13T19:11:04Z | 20,878 | 7,846 | 2,383 | 31,107 |
| [v2.14.9](https://github.com/laurent22/joplin/releases/tag/v2.14.9) (p) | 2024-01-11T22:17:59Z | 1,092 | 0 | 261 | 1,353 |
| [v2.14.8](https://github.com/laurent22/joplin/releases/tag/v2.14.8) (p) | 2024-01-09T22:57:07Z | 791 | 253 | 199 | 1,243 |
| [v2.14.7](https://github.com/laurent22/joplin/releases/tag/v2.14.7) (p) | 2024-01-08T11:51:49Z | 653 | 144 | 194 | 991 |
| [v2.14.6](https://github.com/laurent22/joplin/releases/tag/v2.14.6) (p) | 2024-01-06T16:38:32Z | 761 | 166 | 168 | 1,095 |
| [v2.13.13](https://github.com/laurent22/joplin/releases/tag/v2.13.13) | 2024-01-06T13:33:11Z | 55,100 | 15,954 | 6,368 | 77,422 |
| [v2.13.12](https://github.com/laurent22/joplin/releases/tag/v2.13.12) | 2023-12-31T16:08:02Z | 46,242 | 14,316 | 5,111 | 65,669 |
| [v2.13.11](https://github.com/laurent22/joplin/releases/tag/v2.13.11) | 2023-12-24T12:58:53Z | 47,035 | 13,283 | 6,023 | 66,341 |
| [v2.13.10](https://github.com/laurent22/joplin/releases/tag/v2.13.10) | 2023-12-22T10:11:08Z | 20,446 | 8,784 | 1,612 | 30,842 |
| [v2.13.9](https://github.com/laurent22/joplin/releases/tag/v2.13.9) | 2023-12-09T17:18:58Z | 69,242 | 21,942 | 8,654 | 99,838 |
| [v2.13.8](https://github.com/laurent22/joplin/releases/tag/v2.13.8) | 2023-12-03T12:07:08Z | 51,644 | 17,923 | 5,238 | 74,805 |
| [v2.14.13](https://github.com/laurent22/joplin/releases/tag/v2.14.13) (p) | 2024-02-09T16:31:54Z | 457 | 135 | 110 | 702 |
| [v2.14.12](https://github.com/laurent22/joplin/releases/tag/v2.14.12) (p) | 2024-02-03T12:11:47Z | 1,016 | 233 | 269 | 1,518 |
| [v2.14.11](https://github.com/laurent22/joplin/releases/tag/v2.14.11) (p) | 2024-01-26T11:53:05Z | 1,309 | 275 | 497 | 2,081 |
| [v2.14.10](https://github.com/laurent22/joplin/releases/tag/v2.14.10) (p) | 2024-01-18T22:45:04Z | 2,130 | 288 | 391 | 2,809 |
| [v2.13.15](https://github.com/laurent22/joplin/releases/tag/v2.13.15) | 2024-01-15T13:01:19Z | 151,320 | 34,201 | 29,330 | 214,851 |
| [v2.13.14](https://github.com/laurent22/joplin/releases/tag/v2.13.14) | 2024-01-13T19:11:04Z | 20,914 | 7,847 | 2,393 | 31,154 |
| [v2.14.9](https://github.com/laurent22/joplin/releases/tag/v2.14.9) (p) | 2024-01-11T22:17:59Z | 1,093 | 0 | 263 | 1,356 |
| [v2.14.8](https://github.com/laurent22/joplin/releases/tag/v2.14.8) (p) | 2024-01-09T22:57:07Z | 797 | 254 | 201 | 1,252 |
| [v2.14.7](https://github.com/laurent22/joplin/releases/tag/v2.14.7) (p) | 2024-01-08T11:51:49Z | 663 | 148 | 195 | 1,006 |
| [v2.14.6](https://github.com/laurent22/joplin/releases/tag/v2.14.6) (p) | 2024-01-06T16:38:32Z | 761 | 167 | 168 | 1,096 |
| [v2.13.13](https://github.com/laurent22/joplin/releases/tag/v2.13.13) | 2024-01-06T13:33:11Z | 55,139 | 15,955 | 6,368 | 77,462 |
| [v2.13.12](https://github.com/laurent22/joplin/releases/tag/v2.13.12) | 2023-12-31T16:08:02Z | 46,278 | 14,326 | 5,111 | 65,715 |
| [v2.13.11](https://github.com/laurent22/joplin/releases/tag/v2.13.11) | 2023-12-24T12:58:53Z | 47,062 | 13,283 | 6,024 | 66,369 |
| [v2.13.10](https://github.com/laurent22/joplin/releases/tag/v2.13.10) | 2023-12-22T10:11:08Z | 20,495 | 8,818 | 1,636 | 30,949 |
| [v2.13.9](https://github.com/laurent22/joplin/releases/tag/v2.13.9) | 2023-12-09T17:18:58Z | 69,311 | 21,942 | 8,657 | 99,910 |
| [v2.13.8](https://github.com/laurent22/joplin/releases/tag/v2.13.8) | 2023-12-03T12:07:08Z | 51,673 | 17,923 | 5,238 | 74,834 |
| [v2.13.6](https://github.com/laurent22/joplin/releases/tag/v2.13.6) (p) | 2023-11-17T19:24:03Z | 2,192 | 463 | 598 | 3,253 |
| [v2.13.5](https://github.com/laurent22/joplin/releases/tag/v2.13.5) (p) | 2023-11-09T20:24:09Z | 1,504 | 356 | 459 | 2,319 |
| [v2.13.4](https://github.com/laurent22/joplin/releases/tag/v2.13.4) (p) | 2023-10-31T00:01:00Z | 1,581 | 388 | 505 | 2,474 |
| [v2.13.5](https://github.com/laurent22/joplin/releases/tag/v2.13.5) (p) | 2023-11-09T20:24:09Z | 1,506 | 357 | 459 | 2,322 |
| [v2.13.4](https://github.com/laurent22/joplin/releases/tag/v2.13.4) (p) | 2023-10-31T00:01:00Z | 1,584 | 389 | 505 | 2,478 |
| [v2.13.3](https://github.com/laurent22/joplin/releases/tag/v2.13.3) (p) | 2023-10-24T09:25:33Z | 1,315 | 285 | 305 | 1,905 |
| [v2.12.19](https://github.com/laurent22/joplin/releases/tag/v2.12.19) | 2023-10-21T09:39:18Z | 168,293 | 43,680 | 27,916 | 239,889 |
| [v2.12.19](https://github.com/laurent22/joplin/releases/tag/v2.12.19) | 2023-10-21T09:39:18Z | 168,335 | 43,710 | 27,945 | 239,990 |
| [v2.13.2](https://github.com/laurent22/joplin/releases/tag/v2.13.2) (p) | 2023-10-06T17:00:07Z | 2,052 | 505 | 709 | 3,266 |
| [v2.12.18](https://github.com/laurent22/joplin/releases/tag/v2.12.18) | 2023-09-22T14:37:24Z | 110,388 | 36,535 | 18,735 | 165,658 |
| [v2.12.17](https://github.com/laurent22/joplin/releases/tag/v2.12.17) | 2023-09-14T21:54:52Z | 48,185 | 21,045 | 6,654 | 75,884 |
| [v2.13.1](https://github.com/laurent22/joplin/releases/tag/v2.13.1) (p) | 2023-09-13T09:31:50Z | 1,396 | 431 | 690 | 2,517 |
| [v2.12.16](https://github.com/laurent22/joplin/releases/tag/v2.12.16) | 2023-09-11T22:33:37Z | 28,829 | 14,678 | 2,472 | 45,979 |
| [v2.12.15](https://github.com/laurent22/joplin/releases/tag/v2.12.15) | 2023-08-27T11:35:39Z | 65,826 | 28,205 | 8,501 | 102,532 |
| [v2.12.12](https://github.com/laurent22/joplin/releases/tag/v2.12.12) (p) | 2023-08-19T22:44:56Z | 3,449 | 400 | 431 | 4,280 |
| [v2.12.10](https://github.com/laurent22/joplin/releases/tag/v2.12.10) (p) | 2023-07-30T18:25:58Z | 8,097 | 3,821 | 918 | 12,836 |
| [v2.12.9](https://github.com/laurent22/joplin/releases/tag/v2.12.9) (p) | 2023-07-25T16:06:08Z | 2,897 | 375 | 324 | 3,596 |
| [v2.12.7](https://github.com/laurent22/joplin/releases/tag/v2.12.7) (p) | 2023-07-13T12:55:31Z | 2,209 | 669 | 596 | 3,474 |
| [v2.12.5](https://github.com/laurent22/joplin/releases/tag/v2.12.5) (p) | 2023-07-12T15:03:46Z | 2,170 | 174 | 165 | 2,509 |
| [v2.12.4](https://github.com/laurent22/joplin/releases/tag/v2.12.4) (p) | 2023-07-07T22:36:53Z | 2,820 | 2,136 | 226 | 5,182 |
| [v2.12.3](https://github.com/laurent22/joplin/releases/tag/v2.12.3) (p) | 2023-07-07T10:16:55Z | 429 | 213 | 104 | 746 |
| [v2.11.11](https://github.com/laurent22/joplin/releases/tag/v2.11.11) | 2023-06-23T15:16:37Z | 190,946 | 67,271 | 38,964 | 297,181 |
| [v2.11.9](https://github.com/laurent22/joplin/releases/tag/v2.11.9) (p) | 2023-06-06T16:23:27Z | 2,321 | 583 | 754 | 3,658 |
| [v2.12.18](https://github.com/laurent22/joplin/releases/tag/v2.12.18) | 2023-09-22T14:37:24Z | 110,428 | 36,558 | 18,758 | 165,744 |
| [v2.12.17](https://github.com/laurent22/joplin/releases/tag/v2.12.17) | 2023-09-14T21:54:52Z | 48,187 | 21,069 | 6,677 | 75,933 |
| [v2.13.1](https://github.com/laurent22/joplin/releases/tag/v2.13.1) (p) | 2023-09-13T09:31:50Z | 1,399 | 431 | 690 | 2,520 |
| [v2.12.16](https://github.com/laurent22/joplin/releases/tag/v2.12.16) | 2023-09-11T22:33:37Z | 28,829 | 14,703 | 2,495 | 46,027 |
| [v2.12.15](https://github.com/laurent22/joplin/releases/tag/v2.12.15) | 2023-08-27T11:35:39Z | 65,876 | 28,270 | 8,561 | 102,707 |
| [v2.12.12](https://github.com/laurent22/joplin/releases/tag/v2.12.12) (p) | 2023-08-19T22:44:56Z | 3,493 | 402 | 431 | 4,326 |
| [v2.12.10](https://github.com/laurent22/joplin/releases/tag/v2.12.10) (p) | 2023-07-30T18:25:58Z | 8,131 | 3,822 | 920 | 12,873 |
| [v2.12.9](https://github.com/laurent22/joplin/releases/tag/v2.12.9) (p) | 2023-07-25T16:06:08Z | 2,935 | 375 | 324 | 3,634 |
| [v2.12.7](https://github.com/laurent22/joplin/releases/tag/v2.12.7) (p) | 2023-07-13T12:55:31Z | 2,210 | 669 | 596 | 3,475 |
| [v2.12.5](https://github.com/laurent22/joplin/releases/tag/v2.12.5) (p) | 2023-07-12T15:03:46Z | 2,219 | 177 | 166 | 2,562 |
| [v2.12.4](https://github.com/laurent22/joplin/releases/tag/v2.12.4) (p) | 2023-07-07T22:36:53Z | 2,893 | 2,205 | 226 | 5,324 |
| [v2.12.3](https://github.com/laurent22/joplin/releases/tag/v2.12.3) (p) | 2023-07-07T10:16:55Z | 430 | 213 | 104 | 747 |
| [v2.11.11](https://github.com/laurent22/joplin/releases/tag/v2.11.11) | 2023-06-23T15:16:37Z | 191,074 | 67,298 | 38,995 | 297,367 |
| [v2.11.9](https://github.com/laurent22/joplin/releases/tag/v2.11.9) (p) | 2023-06-06T16:23:27Z | 2,322 | 583 | 754 | 3,659 |
| [v2.11.6](https://github.com/laurent22/joplin/releases/tag/v2.11.6) (p) | 2023-05-31T20:13:08Z | 1,184 | 443 | 353 | 1,980 |
| [v2.11.5](https://github.com/laurent22/joplin/releases/tag/v2.11.5) (p) | 2023-05-28T00:41:40Z | 1,045 | 315 | 291 | 1,651 |
| [v2.10.19](https://github.com/laurent22/joplin/releases/tag/v2.10.19) | 2023-05-17T12:25:41Z | 125,850 | 48,358 | 22,505 | 196,713 |
| [v2.11.4](https://github.com/laurent22/joplin/releases/tag/v2.11.4) (p) | 2023-05-16T10:02:21Z | 1,098 | 477 | 424 | 1,999 |
| [v2.10.19](https://github.com/laurent22/joplin/releases/tag/v2.10.19) | 2023-05-17T12:25:41Z | 125,945 | 48,384 | 22,534 | 196,863 |
| [v2.11.4](https://github.com/laurent22/joplin/releases/tag/v2.11.4) (p) | 2023-05-16T10:02:21Z | 1,104 | 479 | 425 | 2,008 |
| [v2.11.3](https://github.com/laurent22/joplin/releases/tag/v2.11.3) (p) | 2023-05-16T09:09:57Z | 158 | 49 | 46 | 253 |
| [v2.10.18](https://github.com/laurent22/joplin/releases/tag/v2.10.18) | 2023-05-09T13:27:43Z | 57,557 | 24,272 | 6,818 | 88,647 |
| [v2.10.17](https://github.com/laurent22/joplin/releases/tag/v2.10.17) | 2023-05-08T17:27:28Z | 19,888 | 11,518 | 900 | 32,306 |
| [v2.10.16](https://github.com/laurent22/joplin/releases/tag/v2.10.16) | 2023-04-27T09:27:45Z | 10,497 | 4,265 | 792 | 15,554 |
| [v2.10.18](https://github.com/laurent22/joplin/releases/tag/v2.10.18) | 2023-05-09T13:27:43Z | 57,628 | 24,297 | 6,842 | 88,767 |
| [v2.10.17](https://github.com/laurent22/joplin/releases/tag/v2.10.17) | 2023-05-08T17:27:28Z | 19,928 | 11,519 | 900 | 32,347 |
| [v2.10.16](https://github.com/laurent22/joplin/releases/tag/v2.10.16) | 2023-04-27T09:27:45Z | 10,563 | 4,266 | 792 | 15,621 |
| [v2.10.15](https://github.com/laurent22/joplin/releases/tag/v2.10.15) (p) | 2023-04-26T22:02:16Z | 392 | 149 | 65 | 606 |
| [v2.10.13](https://github.com/laurent22/joplin/releases/tag/v2.10.13) (p) | 2023-04-03T16:53:46Z | 5,207 | 831 | 1,081 | 7,119 |
| [v2.10.12](https://github.com/laurent22/joplin/releases/tag/v2.10.12) (p) | 2023-03-23T12:17:13Z | 4,177 | 521 | 607 | 5,305 |
| [v2.10.11](https://github.com/laurent22/joplin/releases/tag/v2.10.11) (p) | 2023-03-17T10:54:02Z | 3,548 | 386 | 409 | 4,343 |
| [v2.10.10](https://github.com/laurent22/joplin/releases/tag/v2.10.10) (p) | 2023-03-13T23:16:37Z | 3,039 | 286 | 258 | 3,583 |
| [v2.10.9](https://github.com/laurent22/joplin/releases/tag/v2.10.9) (p) | 2023-03-12T16:16:45Z | 2,590 | 216 | 299 | 3,105 |
| [v2.10.8](https://github.com/laurent22/joplin/releases/tag/v2.10.8) (p) | 2023-02-26T12:53:55Z | 5,070 | 574 | 875 | 6,519 |
| [v2.10.7](https://github.com/laurent22/joplin/releases/tag/v2.10.7) (p) | 2023-02-24T10:56:20Z | 2,745 | 193 | 282 | 3,220 |
| [v2.10.6](https://github.com/laurent22/joplin/releases/tag/v2.10.6) (p) | 2023-02-20T14:00:05Z | 3,531 | 344 | 292 | 4,167 |
| [v2.10.5](https://github.com/laurent22/joplin/releases/tag/v2.10.5) | 2023-01-16T15:00:53Z | 388 | 108 | 330 | 826 |
| [v2.10.4](https://github.com/laurent22/joplin/releases/tag/v2.10.4) (p) | 2023-01-05T13:09:20Z | 8,754 | 1,308 | 1,815 | 11,877 |
| [v2.10.3](https://github.com/laurent22/joplin/releases/tag/v2.10.3) (p) | 2022-12-31T15:53:23Z | 3,323 | 316 | 421 | 4,060 |
| [v2.10.2](https://github.com/laurent22/joplin/releases/tag/v2.10.2) (p) | 2022-12-18T18:05:08Z | 4,747 | 594 | 643 | 5,984 |
| [v2.9.17](https://github.com/laurent22/joplin/releases/tag/v2.9.17) | 2022-11-15T10:28:37Z | 336,728 | 108,813 | 83,415 | 528,956 |
| [v2.9.12](https://github.com/laurent22/joplin/releases/tag/v2.9.12) (p) | 2022-11-01T17:06:05Z | 11,926 | 614 | 548 | 13,088 |
| [v2.9.11](https://github.com/laurent22/joplin/releases/tag/v2.9.11) (p) | 2022-10-23T16:09:58Z | 4,071 | 533 | 764 | 5,368 |
| [v2.9.4](https://github.com/laurent22/joplin/releases/tag/v2.9.4) (p) | 2022-08-18T16:52:26Z | 9,112 | 1,871 | 2,203 | 13,186 |
| [v2.10.13](https://github.com/laurent22/joplin/releases/tag/v2.10.13) (p) | 2023-04-03T16:53:46Z | 5,243 | 832 | 1,081 | 7,156 |
| [v2.10.12](https://github.com/laurent22/joplin/releases/tag/v2.10.12) (p) | 2023-03-23T12:17:13Z | 4,208 | 521 | 607 | 5,336 |
| [v2.10.11](https://github.com/laurent22/joplin/releases/tag/v2.10.11) (p) | 2023-03-17T10:54:02Z | 3,586 | 388 | 413 | 4,387 |
| [v2.10.10](https://github.com/laurent22/joplin/releases/tag/v2.10.10) (p) | 2023-03-13T23:16:37Z | 3,077 | 287 | 259 | 3,623 |
| [v2.10.9](https://github.com/laurent22/joplin/releases/tag/v2.10.9) (p) | 2023-03-12T16:16:45Z | 2,620 | 216 | 299 | 3,135 |
| [v2.10.8](https://github.com/laurent22/joplin/releases/tag/v2.10.8) (p) | 2023-02-26T12:53:55Z | 5,108 | 574 | 875 | 6,557 |
| [v2.10.7](https://github.com/laurent22/joplin/releases/tag/v2.10.7) (p) | 2023-02-24T10:56:20Z | 2,781 | 194 | 283 | 3,258 |
| [v2.10.6](https://github.com/laurent22/joplin/releases/tag/v2.10.6) (p) | 2023-02-20T14:00:05Z | 3,570 | 344 | 292 | 4,206 |
| [v2.10.5](https://github.com/laurent22/joplin/releases/tag/v2.10.5) | 2023-01-16T15:00:53Z | 388 | 108 | 332 | 828 |
| [v2.10.4](https://github.com/laurent22/joplin/releases/tag/v2.10.4) (p) | 2023-01-05T13:09:20Z | 8,791 | 1,308 | 1,816 | 11,915 |
| [v2.10.3](https://github.com/laurent22/joplin/releases/tag/v2.10.3) (p) | 2022-12-31T15:53:23Z | 3,360 | 316 | 421 | 4,097 |
| [v2.10.2](https://github.com/laurent22/joplin/releases/tag/v2.10.2) (p) | 2022-12-18T18:05:08Z | 4,786 | 596 | 643 | 6,025 |
| [v2.9.17](https://github.com/laurent22/joplin/releases/tag/v2.9.17) | 2022-11-15T10:28:37Z | 336,822 | 108,817 | 83,418 | 529,057 |
| [v2.9.12](https://github.com/laurent22/joplin/releases/tag/v2.9.12) (p) | 2022-11-01T17:06:05Z | 11,960 | 614 | 548 | 13,122 |
| [v2.9.11](https://github.com/laurent22/joplin/releases/tag/v2.9.11) (p) | 2022-10-23T16:09:58Z | 4,104 | 534 | 765 | 5,403 |
| [v2.9.4](https://github.com/laurent22/joplin/releases/tag/v2.9.4) (p) | 2022-08-18T16:52:26Z | 9,142 | 1,871 | 2,203 | 13,216 |
| [v2.9.3](https://github.com/laurent22/joplin/releases/tag/v2.9.3) (p) | 2022-08-18T13:11:09Z | 368 | 95 | 279 | 742 |
| [v2.9.2](https://github.com/laurent22/joplin/releases/tag/v2.9.2) (p) | 2022-08-12T18:12:12Z | 1,539 | 450 | 0 | 1,989 |
| [v2.9.1](https://github.com/laurent22/joplin/releases/tag/v2.9.1) (p) | 2022-07-11T09:59:32Z | 8,557 | 1,345 | 1,415 | 11,317 |
| [v2.8.8](https://github.com/laurent22/joplin/releases/tag/v2.8.8) | 2022-05-17T14:48:06Z | 352,778 | 114,442 | 113,633 | 580,853 |
| [v2.8.7](https://github.com/laurent22/joplin/releases/tag/v2.8.7) (p) | 2022-05-06T11:34:27Z | 5,070 | 368 | 432 | 5,870 |
| [v2.8.6](https://github.com/laurent22/joplin/releases/tag/v2.8.6) (p) | 2022-05-03T10:08:25Z | 4,686 | 405 | 336 | 5,427 |
| [v2.8.5](https://github.com/laurent22/joplin/releases/tag/v2.8.5) (p) | 2022-04-27T13:51:50Z | 4,794 | 373 | 360 | 5,527 |
| [v2.8.4](https://github.com/laurent22/joplin/releases/tag/v2.8.4) (p) | 2022-04-19T18:00:09Z | 5,263 | 592 | 336 | 6,191 |
| [v2.8.2](https://github.com/laurent22/joplin/releases/tag/v2.8.2) (p) | 2022-04-14T11:35:45Z | 4,688 | 282 | 284 | 5,254 |
| [v2.7.15](https://github.com/laurent22/joplin/releases/tag/v2.7.15) | 2022-03-17T13:03:23Z | 157,038 | 56,788 | 51,302 | 265,128 |
| [v2.7.14](https://github.com/laurent22/joplin/releases/tag/v2.7.14) | 2022-02-27T11:30:53Z | 35,278 | 16,787 | 4,817 | 56,882 |
| [v2.7.13](https://github.com/laurent22/joplin/releases/tag/v2.7.13) | 2022-02-24T17:42:12Z | 55,718 | 25,731 | 11,732 | 93,181 |
| [v2.7.12](https://github.com/laurent22/joplin/releases/tag/v2.7.12) (p) | 2022-02-14T15:06:14Z | 5,452 | 467 | 507 | 6,426 |
| [v2.7.11](https://github.com/laurent22/joplin/releases/tag/v2.7.11) (p) | 2022-02-12T13:00:02Z | 4,606 | 198 | 177 | 4,981 |
| [v2.7.10](https://github.com/laurent22/joplin/releases/tag/v2.7.10) (p) | 2022-02-11T18:19:09Z | 4,108 | 129 | 98 | 4,335 |
| [v2.7.8](https://github.com/laurent22/joplin/releases/tag/v2.7.8) (p) | 2022-01-19T09:35:27Z | 6,348 | 773 | 832 | 7,953 |
| [v2.7.7](https://github.com/laurent22/joplin/releases/tag/v2.7.7) (p) | 2022-01-18T14:05:07Z | 4,439 | 159 | 146 | 4,744 |
| [v2.7.6](https://github.com/laurent22/joplin/releases/tag/v2.7.6) (p) | 2022-01-17T17:08:28Z | 4,366 | 186 | 123 | 4,675 |
| [v2.6.10](https://github.com/laurent22/joplin/releases/tag/v2.6.10) | 2021-12-19T11:31:16Z | 137,262 | 51,219 | 49,338 | 237,819 |
| [v2.6.9](https://github.com/laurent22/joplin/releases/tag/v2.6.9) | 2021-12-17T11:57:32Z | 20,109 | 9,505 | 3,200 | 32,814 |
| [v2.6.7](https://github.com/laurent22/joplin/releases/tag/v2.6.7) (p) | 2021-12-16T10:47:23Z | 4,579 | 182 | 115 | 4,876 |
| [v2.6.6](https://github.com/laurent22/joplin/releases/tag/v2.6.6) (p) | 2021-12-13T12:31:43Z | 4,584 | 259 | 177 | 5,020 |
| [v2.6.5](https://github.com/laurent22/joplin/releases/tag/v2.6.5) (p) | 2021-12-13T10:07:04Z | 3,897 | 54 | 39 | 3,990 |
| [v2.6.4](https://github.com/laurent22/joplin/releases/tag/v2.6.4) (p) | 2021-12-09T19:53:43Z | 4,658 | 293 | 209 | 5,160 |
| [v2.6.2](https://github.com/laurent22/joplin/releases/tag/v2.6.2) (p) | 2021-11-18T12:19:12Z | 6,428 | 795 | 704 | 7,927 |
| [v2.5.12](https://github.com/laurent22/joplin/releases/tag/v2.5.12) | 2021-11-08T11:07:11Z | 83,917 | 32,512 | 25,245 | 141,674 |
| [v2.5.10](https://github.com/laurent22/joplin/releases/tag/v2.5.10) | 2021-11-01T08:22:42Z | 47,957 | 19,054 | 10,103 | 77,114 |
| [v2.5.8](https://github.com/laurent22/joplin/releases/tag/v2.5.8) | 2021-10-31T11:38:03Z | 16,658 | 6,584 | 2,337 | 25,579 |
| [v2.5.7](https://github.com/laurent22/joplin/releases/tag/v2.5.7) (p) | 2021-10-29T14:47:33Z | 4,160 | 206 | 170 | 4,536 |
| [v2.5.6](https://github.com/laurent22/joplin/releases/tag/v2.5.6) (p) | 2021-10-28T22:03:09Z | 4,217 | 180 | 110 | 4,507 |
| [v2.5.4](https://github.com/laurent22/joplin/releases/tag/v2.5.4) (p) | 2021-10-19T10:10:54Z | 5,710 | 570 | 591 | 6,871 |
| [v2.4.12](https://github.com/laurent22/joplin/releases/tag/v2.4.12) | 2021-10-13T17:24:34Z | 48,525 | 19,992 | 9,793 | 78,310 |
| [v2.5.1](https://github.com/laurent22/joplin/releases/tag/v2.5.1) (p) | 2021-10-02T09:51:58Z | 6,804 | 909 | 949 | 8,662 |
| [v2.4.9](https://github.com/laurent22/joplin/releases/tag/v2.4.9) | 2021-09-29T19:08:58Z | 60,201 | 23,269 | 15,944 | 99,414 |
| [v2.4.8](https://github.com/laurent22/joplin/releases/tag/v2.4.8) (p) | 2021-09-22T19:01:46Z | 10,571 | 1,776 | 537 | 12,884 |
| [v2.4.7](https://github.com/laurent22/joplin/releases/tag/v2.4.7) (p) | 2021-09-19T12:53:22Z | 4,725 | 261 | 213 | 5,199 |
| [v2.4.6](https://github.com/laurent22/joplin/releases/tag/v2.4.6) (p) | 2021-09-09T18:57:17Z | 5,481 | 463 | 522 | 6,466 |
| [v2.4.5](https://github.com/laurent22/joplin/releases/tag/v2.4.5) (p) | 2021-09-06T18:03:28Z | 4,720 | 279 | 227 | 5,226 |
| [v2.4.4](https://github.com/laurent22/joplin/releases/tag/v2.4.4) (p) | 2021-08-30T16:02:51Z | 4,941 | 382 | 377 | 5,700 |
| [v2.4.3](https://github.com/laurent22/joplin/releases/tag/v2.4.3) (p) | 2021-08-28T15:27:32Z | 4,426 | 209 | 184 | 4,819 |
| [v2.4.2](https://github.com/laurent22/joplin/releases/tag/v2.4.2) (p) | 2021-08-27T17:13:21Z | 4,208 | 153 | 94 | 4,455 |
| [v2.4.1](https://github.com/laurent22/joplin/releases/tag/v2.4.1) (p) | 2021-08-21T11:52:30Z | 5,014 | 374 | 339 | 5,727 |
| [v2.3.5](https://github.com/laurent22/joplin/releases/tag/v2.3.5) | 2021-08-17T06:43:30Z | 86,356 | 31,472 | 33,172 | 151,000 |
| [v2.3.3](https://github.com/laurent22/joplin/releases/tag/v2.3.3) | 2021-08-14T09:19:40Z | 19,419 | 6,889 | 4,065 | 30,373 |
| [v2.2.7](https://github.com/laurent22/joplin/releases/tag/v2.2.7) | 2021-08-11T11:03:26Z | 19,302 | 7,527 | 2,610 | 29,439 |
| [v2.2.6](https://github.com/laurent22/joplin/releases/tag/v2.2.6) (p) | 2021-08-09T19:29:20Z | 11,445 | 4,621 | 960 | 17,026 |
| [v2.2.5](https://github.com/laurent22/joplin/releases/tag/v2.2.5) (p) | 2021-08-07T10:35:24Z | 4,759 | 280 | 210 | 5,249 |
| [v2.2.4](https://github.com/laurent22/joplin/releases/tag/v2.2.4) (p) | 2021-08-05T16:42:48Z | 4,419 | 210 | 135 | 4,764 |
| [v2.2.2](https://github.com/laurent22/joplin/releases/tag/v2.2.2) (p) | 2021-07-19T10:28:35Z | 6,366 | 739 | 650 | 7,755 |
| [v2.1.9](https://github.com/laurent22/joplin/releases/tag/v2.1.9) | 2021-07-19T10:28:43Z | 50,975 | 18,974 | 16,829 | 86,778 |
| [v2.2.1](https://github.com/laurent22/joplin/releases/tag/v2.2.1) (p) | 2021-07-09T17:38:25Z | 5,977 | 419 | 397 | 6,793 |
| [v2.1.8](https://github.com/laurent22/joplin/releases/tag/v2.1.8) | 2021-07-03T08:25:16Z | 34,252 | 12,211 | 12,742 | 59,205 |
| [v2.1.7](https://github.com/laurent22/joplin/releases/tag/v2.1.7) | 2021-06-26T19:48:55Z | 17,534 | 6,414 | 3,644 | 27,592 |
| [v2.1.5](https://github.com/laurent22/joplin/releases/tag/v2.1.5) (p) | 2021-06-23T15:08:52Z | 4,853 | 256 | 204 | 5,313 |
| [v2.1.3](https://github.com/laurent22/joplin/releases/tag/v2.1.3) (p) | 2021-06-19T16:32:51Z | 4,933 | 314 | 219 | 5,466 |
| [v2.0.11](https://github.com/laurent22/joplin/releases/tag/v2.0.11) | 2021-06-16T17:55:49Z | 27,362 | 9,285 | 9,917 | 46,564 |
| [v2.0.10](https://github.com/laurent22/joplin/releases/tag/v2.0.10) | 2021-06-16T07:58:29Z | 6,482 | 950 | 402 | 7,834 |
| [v2.0.9](https://github.com/laurent22/joplin/releases/tag/v2.0.9) (p) | 2021-06-12T09:30:30Z | 4,956 | 312 | 904 | 6,172 |
| [v2.0.8](https://github.com/laurent22/joplin/releases/tag/v2.0.8) (p) | 2021-06-10T16:15:08Z | 4,360 | 250 | 604 | 5,214 |
| [v2.9.2](https://github.com/laurent22/joplin/releases/tag/v2.9.2) (p) | 2022-08-12T18:12:12Z | 1,540 | 450 | 0 | 1,990 |
| [v2.9.1](https://github.com/laurent22/joplin/releases/tag/v2.9.1) (p) | 2022-07-11T09:59:32Z | 8,593 | 1,345 | 1,415 | 11,353 |
| [v2.8.8](https://github.com/laurent22/joplin/releases/tag/v2.8.8) | 2022-05-17T14:48:06Z | 352,877 | 114,454 | 113,638 | 580,969 |
| [v2.8.7](https://github.com/laurent22/joplin/releases/tag/v2.8.7) (p) | 2022-05-06T11:34:27Z | 5,100 | 368 | 432 | 5,900 |
| [v2.8.6](https://github.com/laurent22/joplin/releases/tag/v2.8.6) (p) | 2022-05-03T10:08:25Z | 4,718 | 405 | 336 | 5,459 |
| [v2.8.5](https://github.com/laurent22/joplin/releases/tag/v2.8.5) (p) | 2022-04-27T13:51:50Z | 4,828 | 374 | 360 | 5,562 |
| [v2.8.4](https://github.com/laurent22/joplin/releases/tag/v2.8.4) (p) | 2022-04-19T18:00:09Z | 5,288 | 592 | 336 | 6,216 |
| [v2.8.2](https://github.com/laurent22/joplin/releases/tag/v2.8.2) (p) | 2022-04-14T11:35:45Z | 4,722 | 283 | 284 | 5,289 |
| [v2.7.15](https://github.com/laurent22/joplin/releases/tag/v2.7.15) | 2022-03-17T13:03:23Z | 157,076 | 56,788 | 51,302 | 265,166 |
| [v2.7.14](https://github.com/laurent22/joplin/releases/tag/v2.7.14) | 2022-02-27T11:30:53Z | 35,311 | 16,787 | 4,818 | 56,916 |
| [v2.7.13](https://github.com/laurent22/joplin/releases/tag/v2.7.13) | 2022-02-24T17:42:12Z | 55,755 | 25,731 | 11,732 | 93,218 |
| [v2.7.12](https://github.com/laurent22/joplin/releases/tag/v2.7.12) (p) | 2022-02-14T15:06:14Z | 5,479 | 467 | 508 | 6,454 |
| [v2.7.11](https://github.com/laurent22/joplin/releases/tag/v2.7.11) (p) | 2022-02-12T13:00:02Z | 4,634 | 198 | 177 | 5,009 |
| [v2.7.10](https://github.com/laurent22/joplin/releases/tag/v2.7.10) (p) | 2022-02-11T18:19:09Z | 4,144 | 131 | 98 | 4,373 |
| [v2.7.8](https://github.com/laurent22/joplin/releases/tag/v2.7.8) (p) | 2022-01-19T09:35:27Z | 6,377 | 773 | 832 | 7,982 |
| [v2.7.7](https://github.com/laurent22/joplin/releases/tag/v2.7.7) (p) | 2022-01-18T14:05:07Z | 4,471 | 159 | 146 | 4,776 |
| [v2.7.6](https://github.com/laurent22/joplin/releases/tag/v2.7.6) (p) | 2022-01-17T17:08:28Z | 4,402 | 186 | 123 | 4,711 |
| [v2.6.10](https://github.com/laurent22/joplin/releases/tag/v2.6.10) | 2021-12-19T11:31:16Z | 137,296 | 51,219 | 49,338 | 237,853 |
| [v2.6.9](https://github.com/laurent22/joplin/releases/tag/v2.6.9) | 2021-12-17T11:57:32Z | 20,139 | 9,505 | 3,200 | 32,844 |
| [v2.6.7](https://github.com/laurent22/joplin/releases/tag/v2.6.7) (p) | 2021-12-16T10:47:23Z | 4,612 | 182 | 115 | 4,909 |
| [v2.6.6](https://github.com/laurent22/joplin/releases/tag/v2.6.6) (p) | 2021-12-13T12:31:43Z | 4,617 | 259 | 177 | 5,053 |
| [v2.6.5](https://github.com/laurent22/joplin/releases/tag/v2.6.5) (p) | 2021-12-13T10:07:04Z | 3,926 | 54 | 39 | 4,019 |
| [v2.6.4](https://github.com/laurent22/joplin/releases/tag/v2.6.4) (p) | 2021-12-09T19:53:43Z | 4,688 | 293 | 209 | 5,190 |
| [v2.6.2](https://github.com/laurent22/joplin/releases/tag/v2.6.2) (p) | 2021-11-18T12:19:12Z | 6,459 | 795 | 704 | 7,958 |
| [v2.5.12](https://github.com/laurent22/joplin/releases/tag/v2.5.12) | 2021-11-08T11:07:11Z | 83,952 | 32,512 | 25,246 | 141,710 |
| [v2.5.10](https://github.com/laurent22/joplin/releases/tag/v2.5.10) | 2021-11-01T08:22:42Z | 47,998 | 19,055 | 10,103 | 77,156 |
| [v2.5.8](https://github.com/laurent22/joplin/releases/tag/v2.5.8) | 2021-10-31T11:38:03Z | 16,691 | 6,585 | 2,337 | 25,613 |
| [v2.5.7](https://github.com/laurent22/joplin/releases/tag/v2.5.7) (p) | 2021-10-29T14:47:33Z | 4,185 | 206 | 170 | 4,561 |
| [v2.5.6](https://github.com/laurent22/joplin/releases/tag/v2.5.6) (p) | 2021-10-28T22:03:09Z | 4,250 | 180 | 110 | 4,540 |
| [v2.5.4](https://github.com/laurent22/joplin/releases/tag/v2.5.4) (p) | 2021-10-19T10:10:54Z | 5,746 | 570 | 592 | 6,908 |
| [v2.4.12](https://github.com/laurent22/joplin/releases/tag/v2.4.12) | 2021-10-13T17:24:34Z | 48,572 | 19,992 | 9,793 | 78,357 |
| [v2.5.1](https://github.com/laurent22/joplin/releases/tag/v2.5.1) (p) | 2021-10-02T09:51:58Z | 6,843 | 909 | 949 | 8,701 |
| [v2.4.9](https://github.com/laurent22/joplin/releases/tag/v2.4.9) | 2021-09-29T19:08:58Z | 60,241 | 23,270 | 15,949 | 99,460 |
| [v2.4.8](https://github.com/laurent22/joplin/releases/tag/v2.4.8) (p) | 2021-09-22T19:01:46Z | 10,605 | 1,776 | 537 | 12,918 |
| [v2.4.7](https://github.com/laurent22/joplin/releases/tag/v2.4.7) (p) | 2021-09-19T12:53:22Z | 4,764 | 261 | 213 | 5,238 |
| [v2.4.6](https://github.com/laurent22/joplin/releases/tag/v2.4.6) (p) | 2021-09-09T18:57:17Z | 5,518 | 463 | 522 | 6,503 |
| [v2.4.5](https://github.com/laurent22/joplin/releases/tag/v2.4.5) (p) | 2021-09-06T18:03:28Z | 4,761 | 279 | 227 | 5,267 |
| [v2.4.4](https://github.com/laurent22/joplin/releases/tag/v2.4.4) (p) | 2021-08-30T16:02:51Z | 4,980 | 382 | 378 | 5,740 |
| [v2.4.3](https://github.com/laurent22/joplin/releases/tag/v2.4.3) (p) | 2021-08-28T15:27:32Z | 4,467 | 209 | 184 | 4,860 |
| [v2.4.2](https://github.com/laurent22/joplin/releases/tag/v2.4.2) (p) | 2021-08-27T17:13:21Z | 4,245 | 153 | 94 | 4,492 |
| [v2.4.1](https://github.com/laurent22/joplin/releases/tag/v2.4.1) (p) | 2021-08-21T11:52:30Z | 5,048 | 374 | 339 | 5,761 |
| [v2.3.5](https://github.com/laurent22/joplin/releases/tag/v2.3.5) | 2021-08-17T06:43:30Z | 86,404 | 31,480 | 33,177 | 151,061 |
| [v2.3.3](https://github.com/laurent22/joplin/releases/tag/v2.3.3) | 2021-08-14T09:19:40Z | 19,462 | 6,889 | 4,065 | 30,416 |
| [v2.2.7](https://github.com/laurent22/joplin/releases/tag/v2.2.7) | 2021-08-11T11:03:26Z | 19,336 | 7,527 | 2,610 | 29,473 |
| [v2.2.6](https://github.com/laurent22/joplin/releases/tag/v2.2.6) (p) | 2021-08-09T19:29:20Z | 11,489 | 4,622 | 960 | 17,071 |
| [v2.2.5](https://github.com/laurent22/joplin/releases/tag/v2.2.5) (p) | 2021-08-07T10:35:24Z | 4,786 | 281 | 210 | 5,277 |
| [v2.2.4](https://github.com/laurent22/joplin/releases/tag/v2.2.4) (p) | 2021-08-05T16:42:48Z | 4,449 | 211 | 136 | 4,796 |
| [v2.2.2](https://github.com/laurent22/joplin/releases/tag/v2.2.2) (p) | 2021-07-19T10:28:35Z | 6,404 | 739 | 650 | 7,793 |
| [v2.1.9](https://github.com/laurent22/joplin/releases/tag/v2.1.9) | 2021-07-19T10:28:43Z | 51,032 | 18,974 | 16,829 | 86,835 |
| [v2.2.1](https://github.com/laurent22/joplin/releases/tag/v2.2.1) (p) | 2021-07-09T17:38:25Z | 6,020 | 419 | 397 | 6,836 |
| [v2.1.8](https://github.com/laurent22/joplin/releases/tag/v2.1.8) | 2021-07-03T08:25:16Z | 34,282 | 12,211 | 12,743 | 59,236 |
| [v2.1.7](https://github.com/laurent22/joplin/releases/tag/v2.1.7) | 2021-06-26T19:48:55Z | 17,572 | 6,414 | 3,645 | 27,631 |
| [v2.1.5](https://github.com/laurent22/joplin/releases/tag/v2.1.5) (p) | 2021-06-23T15:08:52Z | 4,889 | 256 | 204 | 5,349 |
| [v2.1.3](https://github.com/laurent22/joplin/releases/tag/v2.1.3) (p) | 2021-06-19T16:32:51Z | 4,967 | 314 | 219 | 5,500 |
| [v2.0.11](https://github.com/laurent22/joplin/releases/tag/v2.0.11) | 2021-06-16T17:55:49Z | 27,405 | 9,285 | 9,918 | 46,608 |
| [v2.0.10](https://github.com/laurent22/joplin/releases/tag/v2.0.10) | 2021-06-16T07:58:29Z | 6,519 | 950 | 402 | 7,871 |
| [v2.0.9](https://github.com/laurent22/joplin/releases/tag/v2.0.9) (p) | 2021-06-12T09:30:30Z | 4,996 | 313 | 904 | 6,213 |
| [v2.0.8](https://github.com/laurent22/joplin/releases/tag/v2.0.8) (p) | 2021-06-10T16:15:08Z | 4,390 | 250 | 604 | 5,244 |
| [v2.0.4](https://github.com/laurent22/joplin/releases/tag/v2.0.4) (p) | 2021-06-02T12:54:17Z | 1,647 | 406 | 395 | 2,448 |
| [v2.0.2](https://github.com/laurent22/joplin/releases/tag/v2.0.2) (p) | 2021-05-21T18:07:48Z | 6,441 | 505 | 1,683 | 8,629 |
| [v2.0.1](https://github.com/laurent22/joplin/releases/tag/v2.0.1) (p) | 2021-05-15T13:22:58Z | 959 | 290 | 1,042 | 2,291 |
| [v1.8.5](https://github.com/laurent22/joplin/releases/tag/v1.8.5) | 2021-05-10T11:58:14Z | 42,589 | 16,321 | 19,451 | 78,361 |
| [v1.8.4](https://github.com/laurent22/joplin/releases/tag/v1.8.4) (p) | 2021-05-09T18:05:05Z | 4,306 | 154 | 475 | 4,935 |
| [v1.8.3](https://github.com/laurent22/joplin/releases/tag/v1.8.3) (p) | 2021-05-04T10:38:16Z | 5,315 | 322 | 953 | 6,590 |
| [v1.8.2](https://github.com/laurent22/joplin/releases/tag/v1.8.2) (p) | 2021-04-25T10:50:51Z | 5,594 | 453 | 1,304 | 7,351 |
| [v1.8.1](https://github.com/laurent22/joplin/releases/tag/v1.8.1) (p) | 2021-03-29T10:46:41Z | 6,603 | 842 | 2,468 | 9,913 |
| [v1.7.11](https://github.com/laurent22/joplin/releases/tag/v1.7.11) | 2021-02-03T12:50:01Z | 121,812 | 43,035 | 64,480 | 229,327 |
| [v1.7.10](https://github.com/laurent22/joplin/releases/tag/v1.7.10) | 2021-01-30T13:25:29Z | 14,646 | 4,889 | 4,537 | 24,072 |
| [v2.0.2](https://github.com/laurent22/joplin/releases/tag/v2.0.2) (p) | 2021-05-21T18:07:48Z | 6,468 | 505 | 1,683 | 8,656 |
| [v2.0.1](https://github.com/laurent22/joplin/releases/tag/v2.0.1) (p) | 2021-05-15T13:22:58Z | 961 | 292 | 1,042 | 2,295 |
| [v1.8.5](https://github.com/laurent22/joplin/releases/tag/v1.8.5) | 2021-05-10T11:58:14Z | 42,632 | 16,323 | 19,453 | 78,408 |
| [v1.8.4](https://github.com/laurent22/joplin/releases/tag/v1.8.4) (p) | 2021-05-09T18:05:05Z | 4,336 | 154 | 476 | 4,966 |
| [v1.8.3](https://github.com/laurent22/joplin/releases/tag/v1.8.3) (p) | 2021-05-04T10:38:16Z | 5,348 | 322 | 953 | 6,623 |
| [v1.8.2](https://github.com/laurent22/joplin/releases/tag/v1.8.2) (p) | 2021-04-25T10:50:51Z | 5,634 | 454 | 1,304 | 7,392 |
| [v1.8.1](https://github.com/laurent22/joplin/releases/tag/v1.8.1) (p) | 2021-03-29T10:46:41Z | 6,638 | 842 | 2,468 | 9,948 |
| [v1.7.11](https://github.com/laurent22/joplin/releases/tag/v1.7.11) | 2021-02-03T12:50:01Z | 121,854 | 43,046 | 64,483 | 229,383 |
| [v1.7.10](https://github.com/laurent22/joplin/releases/tag/v1.7.10) | 2021-01-30T13:25:29Z | 14,647 | 4,892 | 4,539 | 24,078 |
| [v1.7.9](https://github.com/laurent22/joplin/releases/tag/v1.7.9) (p) | 2021-01-28T09:50:21Z | 536 | 150 | 516 | 1,202 |
| [v1.7.6](https://github.com/laurent22/joplin/releases/tag/v1.7.6) (p) | 2021-01-27T10:36:05Z | 351 | 110 | 306 | 767 |
| [v1.7.5](https://github.com/laurent22/joplin/releases/tag/v1.7.5) (p) | 2021-01-26T09:53:05Z | 444 | 221 | 470 | 1,135 |
| [v1.7.4](https://github.com/laurent22/joplin/releases/tag/v1.7.4) (p) | 2021-01-22T17:58:38Z | 734 | 221 | 643 | 1,598 |
| [v1.6.8](https://github.com/laurent22/joplin/releases/tag/v1.6.8) | 2021-01-20T18:11:34Z | 24,257 | 7,735 | 7,640 | 39,632 |
| [v1.6.8](https://github.com/laurent22/joplin/releases/tag/v1.6.8) | 2021-01-20T18:11:34Z | 24,324 | 7,736 | 7,640 | 39,700 |
| [v1.7.3](https://github.com/laurent22/joplin/releases/tag/v1.7.3) (p) | 2021-01-20T11:23:50Z | 388 | 97 | 461 | 946 |
| [v1.6.7](https://github.com/laurent22/joplin/releases/tag/v1.6.7) | 2021-01-11T23:20:33Z | 15,628 | 4,672 | 4,576 | 24,876 |
| [v1.6.7](https://github.com/laurent22/joplin/releases/tag/v1.6.7) | 2021-01-11T23:20:33Z | 15,672 | 4,672 | 4,576 | 24,920 |
| [v1.6.6](https://github.com/laurent22/joplin/releases/tag/v1.6.6) | 2021-01-09T16:15:31Z | 12,872 | 3,445 | 4,825 | 21,142 |
| [v1.6.5](https://github.com/laurent22/joplin/releases/tag/v1.6.5) (p) | 2021-01-09T01:24:32Z | 4,548 | 93 | 326 | 4,967 |
| [v1.6.5](https://github.com/laurent22/joplin/releases/tag/v1.6.5) (p) | 2021-01-09T01:24:32Z | 4,579 | 93 | 326 | 4,998 |
| [v1.6.4](https://github.com/laurent22/joplin/releases/tag/v1.6.4) (p) | 2021-01-07T19:11:32Z | 428 | 95 | 222 | 745 |
| [v1.6.2](https://github.com/laurent22/joplin/releases/tag/v1.6.2) (p) | 2021-01-04T22:34:55Z | 710 | 244 | 608 | 1,562 |
| [v1.5.14](https://github.com/laurent22/joplin/releases/tag/v1.5.14) | 2020-12-30T01:48:46Z | 16,115 | 5,236 | 5,554 | 26,905 |
| [v1.5.14](https://github.com/laurent22/joplin/releases/tag/v1.5.14) | 2020-12-30T01:48:46Z | 16,157 | 5,237 | 5,554 | 26,948 |
| [v1.6.1](https://github.com/laurent22/joplin/releases/tag/v1.6.1) (p) | 2020-12-29T19:37:45Z | 207 | 55 | 188 | 450 |
| [v1.5.13](https://github.com/laurent22/joplin/releases/tag/v1.5.13) | 2020-12-29T18:29:15Z | 793 | 245 | 232 | 1,270 |
| [v1.5.13](https://github.com/laurent22/joplin/releases/tag/v1.5.13) | 2020-12-29T18:29:15Z | 793 | 246 | 232 | 1,271 |
| [v1.5.12](https://github.com/laurent22/joplin/releases/tag/v1.5.12) | 2020-12-28T15:14:08Z | 2,576 | 1,800 | 946 | 5,322 |
| [v1.5.11](https://github.com/laurent22/joplin/releases/tag/v1.5.11) | 2020-12-27T19:54:07Z | 14,428 | 4,665 | 4,321 | 23,414 |
| [v1.5.11](https://github.com/laurent22/joplin/releases/tag/v1.5.11) | 2020-12-27T19:54:07Z | 14,428 | 4,665 | 4,322 | 23,415 |
| [v1.5.10](https://github.com/laurent22/joplin/releases/tag/v1.5.10) (p) | 2020-12-26T12:35:36Z | 329 | 126 | 288 | 743 |
| [v1.5.9](https://github.com/laurent22/joplin/releases/tag/v1.5.9) (p) | 2020-12-23T18:01:08Z | 363 | 388 | 429 | 1,180 |
| [v1.5.8](https://github.com/laurent22/joplin/releases/tag/v1.5.8) (p) | 2020-12-20T09:45:19Z | 599 | 181 | 662 | 1,442 |
| [v1.5.7](https://github.com/laurent22/joplin/releases/tag/v1.5.7) (p) | 2020-12-10T12:58:33Z | 925 | 271 | 1,012 | 2,208 |
| [v1.5.4](https://github.com/laurent22/joplin/releases/tag/v1.5.4) (p) | 2020-12-05T12:07:49Z | 737 | 184 | 654 | 1,575 |
| [v1.4.19](https://github.com/laurent22/joplin/releases/tag/v1.4.19) | 2020-12-01T11:11:16Z | 30,477 | 13,641 | 11,719 | 55,837 |
| [v1.4.19](https://github.com/laurent22/joplin/releases/tag/v1.4.19) | 2020-12-01T11:11:16Z | 30,512 | 13,643 | 11,720 | 55,875 |
| [v1.4.18](https://github.com/laurent22/joplin/releases/tag/v1.4.18) | 2020-11-28T12:21:41Z | 11,766 | 3,913 | 3,166 | 18,845 |
| [v1.4.16](https://github.com/laurent22/joplin/releases/tag/v1.4.16) | 2020-11-27T19:40:16Z | 1,655 | 864 | 624 | 3,143 |
| [v1.4.15](https://github.com/laurent22/joplin/releases/tag/v1.4.15) | 2020-11-27T13:25:43Z | 1,045 | 516 | 299 | 1,860 |
| [v1.4.12](https://github.com/laurent22/joplin/releases/tag/v1.4.12) | 2020-11-23T18:58:07Z | 3,226 | 1,381 | 1,331 | 5,938 |
| [v1.4.11](https://github.com/laurent22/joplin/releases/tag/v1.4.11) (p) | 2020-11-19T23:06:51Z | 5,352 | 195 | 613 | 6,160 |
| [v1.4.10](https://github.com/laurent22/joplin/releases/tag/v1.4.10) (p) | 2020-11-14T09:53:15Z | 701 | 235 | 705 | 1,641 |
| [v1.4.9](https://github.com/laurent22/joplin/releases/tag/v1.4.9) (p) | 2020-11-11T14:23:17Z | 878 | 180 | 422 | 1,480 |
| [v1.4.7](https://github.com/laurent22/joplin/releases/tag/v1.4.7) (p) | 2020-11-07T18:23:29Z | 564 | 211 | 536 | 1,311 |
| [v1.3.18](https://github.com/laurent22/joplin/releases/tag/v1.3.18) | 2020-11-06T12:07:02Z | 36,082 | 11,393 | 10,551 | 58,026 |
| [v1.4.15](https://github.com/laurent22/joplin/releases/tag/v1.4.15) | 2020-11-27T13:25:43Z | 1,047 | 516 | 299 | 1,862 |
| [v1.4.12](https://github.com/laurent22/joplin/releases/tag/v1.4.12) | 2020-11-23T18:58:07Z | 3,226 | 1,382 | 1,332 | 5,940 |
| [v1.4.11](https://github.com/laurent22/joplin/releases/tag/v1.4.11) (p) | 2020-11-19T23:06:51Z | 5,384 | 195 | 613 | 6,192 |
| [v1.4.10](https://github.com/laurent22/joplin/releases/tag/v1.4.10) (p) | 2020-11-14T09:53:15Z | 702 | 235 | 705 | 1,642 |
| [v1.4.9](https://github.com/laurent22/joplin/releases/tag/v1.4.9) (p) | 2020-11-11T14:23:17Z | 878 | 181 | 423 | 1,482 |
| [v1.4.7](https://github.com/laurent22/joplin/releases/tag/v1.4.7) (p) | 2020-11-07T18:23:29Z | 564 | 212 | 536 | 1,312 |
| [v1.3.18](https://github.com/laurent22/joplin/releases/tag/v1.3.18) | 2020-11-06T12:07:02Z | 36,124 | 11,394 | 10,555 | 58,073 |
| [v1.3.17](https://github.com/laurent22/joplin/releases/tag/v1.3.17) (p) | 2020-11-06T11:35:15Z | 91 | 67 | 45 | 203 |
| [v1.4.6](https://github.com/laurent22/joplin/releases/tag/v1.4.6) (p) | 2020-11-05T22:44:12Z | 783 | 131 | 72 | 986 |
| [v1.3.15](https://github.com/laurent22/joplin/releases/tag/v1.3.15) | 2020-11-04T12:22:50Z | 2,839 | 1,349 | 874 | 5,062 |
| [v1.4.6](https://github.com/laurent22/joplin/releases/tag/v1.4.6) (p) | 2020-11-05T22:44:12Z | 787 | 131 | 72 | 990 |
| [v1.3.15](https://github.com/laurent22/joplin/releases/tag/v1.3.15) | 2020-11-04T12:22:50Z | 2,845 | 1,349 | 874 | 5,068 |
| [v1.3.11](https://github.com/laurent22/joplin/releases/tag/v1.3.11) (p) | 2020-10-31T13:22:20Z | 745 | 223 | 501 | 1,469 |
| [v1.3.10](https://github.com/laurent22/joplin/releases/tag/v1.3.10) (p) | 2020-10-29T13:27:14Z | 418 | 155 | 336 | 909 |
| [v1.3.9](https://github.com/laurent22/joplin/releases/tag/v1.3.9) (p) | 2020-10-23T16:04:26Z | 934 | 281 | 654 | 1,869 |
| [v1.3.9](https://github.com/laurent22/joplin/releases/tag/v1.3.9) (p) | 2020-10-23T16:04:26Z | 938 | 281 | 654 | 1,873 |
| [v1.3.8](https://github.com/laurent22/joplin/releases/tag/v1.3.8) (p) | 2020-10-21T18:46:29Z | 564 | 156 | 352 | 1,072 |
| [v1.3.7](https://github.com/laurent22/joplin/releases/tag/v1.3.7) (p) | 2020-10-20T11:35:55Z | 338 | 124 | 364 | 826 |
| [v1.3.5](https://github.com/laurent22/joplin/releases/tag/v1.3.5) (p) | 2020-10-17T14:26:35Z | 516 | 175 | 429 | 1,120 |
| [v1.3.3](https://github.com/laurent22/joplin/releases/tag/v1.3.3) (p) | 2020-10-17T10:56:57Z | 161 | 84 | 54 | 299 |
| [v1.3.2](https://github.com/laurent22/joplin/releases/tag/v1.3.2) (p) | 2020-10-11T20:39:49Z | 716 | 220 | 589 | 1,525 |
| [v1.3.1](https://github.com/laurent22/joplin/releases/tag/v1.3.1) (p) | 2020-10-11T15:10:18Z | 126 | 90 | 65 | 281 |
| [v1.2.6](https://github.com/laurent22/joplin/releases/tag/v1.2.6) | 2020-10-09T13:56:59Z | 50,092 | 17,797 | 14,097 | 81,986 |
| [v1.2.4](https://github.com/laurent22/joplin/releases/tag/v1.2.4) (p) | 2020-09-30T07:34:29Z | 866 | 292 | 823 | 1,981 |
| [v1.2.6](https://github.com/laurent22/joplin/releases/tag/v1.2.6) | 2020-10-09T13:56:59Z | 50,147 | 17,797 | 14,097 | 82,041 |
| [v1.2.4](https://github.com/laurent22/joplin/releases/tag/v1.2.4) (p) | 2020-09-30T07:34:29Z | 866 | 292 | 824 | 1,982 |
| [v1.2.3](https://github.com/laurent22/joplin/releases/tag/v1.2.3) (p) | 2020-09-29T15:13:02Z | 263 | 103 | 105 | 471 |
| [v1.2.2](https://github.com/laurent22/joplin/releases/tag/v1.2.2) (p) | 2020-09-22T20:31:55Z | 1,161 | 246 | 660 | 2,067 |
| [v1.2.2](https://github.com/laurent22/joplin/releases/tag/v1.2.2) (p) | 2020-09-22T20:31:55Z | 1,161 | 247 | 661 | 2,069 |
| [v1.1.4](https://github.com/laurent22/joplin/releases/tag/v1.1.4) | 2020-09-21T11:20:09Z | 28,241 | 13,561 | 7,791 | 49,593 |
| [v1.1.3](https://github.com/laurent22/joplin/releases/tag/v1.1.3) (p) | 2020-09-17T10:30:37Z | 631 | 192 | 486 | 1,309 |
| [v1.1.3](https://github.com/laurent22/joplin/releases/tag/v1.1.3) (p) | 2020-09-17T10:30:37Z | 631 | 193 | 487 | 1,311 |
| [v1.1.2](https://github.com/laurent22/joplin/releases/tag/v1.1.2) (p) | 2020-09-15T12:58:38Z | 425 | 155 | 273 | 853 |
| [v1.1.1](https://github.com/laurent22/joplin/releases/tag/v1.1.1) (p) | 2020-09-11T23:32:47Z | 602 | 235 | 373 | 1,210 |
| [v1.0.245](https://github.com/laurent22/joplin/releases/tag/v1.0.245) | 2020-09-09T12:56:10Z | 23,079 | 10,070 | 5,678 | 38,827 |
| [v1.0.242](https://github.com/laurent22/joplin/releases/tag/v1.0.242) | 2020-09-04T22:00:34Z | 12,912 | 6,467 | 3,049 | 22,428 |
| [v1.0.241](https://github.com/laurent22/joplin/releases/tag/v1.0.241) | 2020-09-04T18:06:00Z | 26,696 | 5,994 | 5,157 | 37,847 |
| [v1.0.239](https://github.com/laurent22/joplin/releases/tag/v1.0.239) (p) | 2020-09-01T21:56:36Z | 1,016 | 270 | 426 | 1,712 |
| [v1.0.237](https://github.com/laurent22/joplin/releases/tag/v1.0.237) (p) | 2020-08-29T15:38:04Z | 639 | 965 | 361 | 1,965 |
| [v1.0.236](https://github.com/laurent22/joplin/releases/tag/v1.0.236) (p) | 2020-08-28T09:16:54Z | 364 | 153 | 127 | 644 |
| [v1.0.235](https://github.com/laurent22/joplin/releases/tag/v1.0.235) (p) | 2020-08-18T22:08:01Z | 2,089 | 530 | 945 | 3,564 |
| [v1.0.234](https://github.com/laurent22/joplin/releases/tag/v1.0.234) (p) | 2020-08-17T23:13:02Z | 722 | 166 | 124 | 1,012 |
| [v1.0.233](https://github.com/laurent22/joplin/releases/tag/v1.0.233) | 2020-08-01T14:51:15Z | 48,711 | 18,253 | 12,392 | 79,356 |
| [v1.0.245](https://github.com/laurent22/joplin/releases/tag/v1.0.245) | 2020-09-09T12:56:10Z | 23,079 | 10,071 | 5,679 | 38,829 |
| [v1.0.242](https://github.com/laurent22/joplin/releases/tag/v1.0.242) | 2020-09-04T22:00:34Z | 12,912 | 6,468 | 3,050 | 22,430 |
| [v1.0.241](https://github.com/laurent22/joplin/releases/tag/v1.0.241) | 2020-09-04T18:06:00Z | 26,699 | 5,995 | 5,161 | 37,855 |
| [v1.0.239](https://github.com/laurent22/joplin/releases/tag/v1.0.239) (p) | 2020-09-01T21:56:36Z | 1,020 | 271 | 427 | 1,718 |
| [v1.0.237](https://github.com/laurent22/joplin/releases/tag/v1.0.237) (p) | 2020-08-29T15:38:04Z | 639 | 966 | 362 | 1,967 |
| [v1.0.236](https://github.com/laurent22/joplin/releases/tag/v1.0.236) (p) | 2020-08-28T09:16:54Z | 364 | 154 | 127 | 645 |
| [v1.0.235](https://github.com/laurent22/joplin/releases/tag/v1.0.235) (p) | 2020-08-18T22:08:01Z | 2,093 | 531 | 945 | 3,569 |
| [v1.0.234](https://github.com/laurent22/joplin/releases/tag/v1.0.234) (p) | 2020-08-17T23:13:02Z | 726 | 167 | 125 | 1,018 |
| [v1.0.233](https://github.com/laurent22/joplin/releases/tag/v1.0.233) | 2020-08-01T14:51:15Z | 48,765 | 18,253 | 12,394 | 79,412 |
| [v1.0.232](https://github.com/laurent22/joplin/releases/tag/v1.0.232) (p) | 2020-07-28T22:34:40Z | 705 | 265 | 202 | 1,172 |
| [v1.0.227](https://github.com/laurent22/joplin/releases/tag/v1.0.227) | 2020-07-07T20:44:54Z | 42,024 | 15,328 | 9,680 | 67,032 |
| [v1.0.226](https://github.com/laurent22/joplin/releases/tag/v1.0.226) (p) | 2020-07-04T10:21:26Z | 4,980 | 2,295 | 711 | 7,986 |
| [v1.0.224](https://github.com/laurent22/joplin/releases/tag/v1.0.224) | 2020-06-20T22:26:08Z | 25,301 | 11,053 | 6,035 | 42,389 |
| [v1.0.227](https://github.com/laurent22/joplin/releases/tag/v1.0.227) | 2020-07-07T20:44:54Z | 42,030 | 15,329 | 9,681 | 67,040 |
| [v1.0.226](https://github.com/laurent22/joplin/releases/tag/v1.0.226) (p) | 2020-07-04T10:21:26Z | 4,981 | 2,296 | 711 | 7,988 |
| [v1.0.224](https://github.com/laurent22/joplin/releases/tag/v1.0.224) | 2020-06-20T22:26:08Z | 25,301 | 11,054 | 6,036 | 42,391 |
| [v1.0.223](https://github.com/laurent22/joplin/releases/tag/v1.0.223) (p) | 2020-06-20T11:51:27Z | 233 | 154 | 102 | 489 |
| [v1.0.221](https://github.com/laurent22/joplin/releases/tag/v1.0.221) (p) | 2020-06-20T01:44:20Z | 903 | 249 | 236 | 1,388 |
| [v1.0.220](https://github.com/laurent22/joplin/releases/tag/v1.0.220) | 2020-06-13T18:26:22Z | 32,840 | 9,975 | 6,452 | 49,267 |
| [v1.0.218](https://github.com/laurent22/joplin/releases/tag/v1.0.218) | 2020-06-07T10:43:34Z | 14,646 | 7,022 | 3,156 | 24,824 |
| [v1.0.220](https://github.com/laurent22/joplin/releases/tag/v1.0.220) | 2020-06-13T18:26:22Z | 32,841 | 9,975 | 6,452 | 49,268 |
| [v1.0.218](https://github.com/laurent22/joplin/releases/tag/v1.0.218) | 2020-06-07T10:43:34Z | 14,646 | 7,023 | 3,156 | 24,825 |
| [v1.0.217](https://github.com/laurent22/joplin/releases/tag/v1.0.217) (p) | 2020-06-06T15:17:27Z | 283 | 141 | 87 | 511 |
| [v1.0.216](https://github.com/laurent22/joplin/releases/tag/v1.0.216) | 2020-05-24T14:21:01Z | 42,470 | 14,365 | 10,230 | 67,065 |
| [v1.0.216](https://github.com/laurent22/joplin/releases/tag/v1.0.216) | 2020-05-24T14:21:01Z | 42,516 | 14,365 | 10,231 | 67,112 |
| [v1.0.214](https://github.com/laurent22/joplin/releases/tag/v1.0.214) (p) | 2020-05-21T17:15:15Z | 7,109 | 3,516 | 791 | 11,416 |
| [v1.0.212](https://github.com/laurent22/joplin/releases/tag/v1.0.212) (p) | 2020-05-21T07:48:39Z | 257 | 114 | 73 | 444 |
| [v1.0.211](https://github.com/laurent22/joplin/releases/tag/v1.0.211) (p) | 2020-05-20T08:59:16Z | 356 | 178 | 115 | 649 |
@@ -296,116 +297,116 @@ updated: 2026-02-01T02:35:07Z
| [v1.0.207](https://github.com/laurent22/joplin/releases/tag/v1.0.207) (p) | 2020-05-10T16:37:35Z | 1,254 | 308 | 1,047 | 2,609 |
| [v1.0.201](https://github.com/laurent22/joplin/releases/tag/v1.0.201) | 2020-04-15T22:55:13Z | 54,431 | 20,112 | 18,229 | 92,772 |
| [v1.0.200](https://github.com/laurent22/joplin/releases/tag/v1.0.200) | 2020-04-12T12:17:46Z | 9,641 | 4,943 | 1,935 | 16,519 |
| [v1.0.199](https://github.com/laurent22/joplin/releases/tag/v1.0.199) | 2020-04-10T18:41:58Z | 19,973 | 5,938 | 3,847 | 29,758 |
| [v1.0.197](https://github.com/laurent22/joplin/releases/tag/v1.0.197) | 2020-03-30T17:21:22Z | 23,529 | 9,877 | 6,725 | 40,131 |
| [v1.0.199](https://github.com/laurent22/joplin/releases/tag/v1.0.199) | 2020-04-10T18:41:58Z | 19,974 | 5,938 | 3,847 | 29,759 |
| [v1.0.197](https://github.com/laurent22/joplin/releases/tag/v1.0.197) | 2020-03-30T17:21:22Z | 23,530 | 9,877 | 6,734 | 40,141 |
| [v1.0.195](https://github.com/laurent22/joplin/releases/tag/v1.0.195) | 2020-03-22T19:56:12Z | 19,185 | 8,010 | 4,541 | 31,736 |
| [v1.0.194](https://github.com/laurent22/joplin/releases/tag/v1.0.194) (p) | 2020-03-14T00:00:32Z | 1,344 | 1,435 | 545 | 3,324 |
| [v1.0.193](https://github.com/laurent22/joplin/releases/tag/v1.0.193) | 2020-03-08T08:58:53Z | 28,852 | 10,986 | 7,466 | 47,304 |
| [v1.0.192](https://github.com/laurent22/joplin/releases/tag/v1.0.192) (p) | 2020-03-06T23:27:52Z | 596 | 168 | 117 | 881 |
| [v1.0.190](https://github.com/laurent22/joplin/releases/tag/v1.0.190) (p) | 2020-03-06T01:22:22Z | 471 | 136 | 111 | 718 |
| [v1.0.189](https://github.com/laurent22/joplin/releases/tag/v1.0.189) (p) | 2020-03-04T17:27:15Z | 464 | 140 | 125 | 729 |
| [v1.0.193](https://github.com/laurent22/joplin/releases/tag/v1.0.193) | 2020-03-08T08:58:53Z | 28,854 | 10,986 | 7,466 | 47,306 |
| [v1.0.192](https://github.com/laurent22/joplin/releases/tag/v1.0.192) (p) | 2020-03-06T23:27:52Z | 600 | 168 | 117 | 885 |
| [v1.0.190](https://github.com/laurent22/joplin/releases/tag/v1.0.190) (p) | 2020-03-06T01:22:22Z | 476 | 136 | 111 | 723 |
| [v1.0.189](https://github.com/laurent22/joplin/releases/tag/v1.0.189) (p) | 2020-03-04T17:27:15Z | 468 | 140 | 125 | 733 |
| [v1.0.187](https://github.com/laurent22/joplin/releases/tag/v1.0.187) (p) | 2020-03-01T12:31:06Z | 985 | 282 | 300 | 1,567 |
| [v1.0.179](https://github.com/laurent22/joplin/releases/tag/v1.0.179) | 2020-01-24T22:42:41Z | 71,852 | 29,156 | 22,619 | 123,627 |
| [v1.0.179](https://github.com/laurent22/joplin/releases/tag/v1.0.179) | 2020-01-24T22:42:41Z | 71,858 | 29,165 | 22,620 | 123,643 |
| [v1.0.178](https://github.com/laurent22/joplin/releases/tag/v1.0.178) | 2020-01-20T19:06:45Z | 17,682 | 6,015 | 2,625 | 26,322 |
| [v1.0.177](https://github.com/laurent22/joplin/releases/tag/v1.0.177) (p) | 2019-12-30T14:40:40Z | 2,015 | 482 | 761 | 3,258 |
| [v1.0.177](https://github.com/laurent22/joplin/releases/tag/v1.0.177) (p) | 2019-12-30T14:40:40Z | 2,015 | 482 | 763 | 3,260 |
| [v1.0.176](https://github.com/laurent22/joplin/releases/tag/v1.0.176) (p) | 2019-12-14T10:36:44Z | 3,179 | 2,582 | 498 | 6,259 |
| [v1.0.175](https://github.com/laurent22/joplin/releases/tag/v1.0.175) | 2019-12-08T11:48:47Z | 73,815 | 17,030 | 16,635 | 107,480 |
| [v1.0.174](https://github.com/laurent22/joplin/releases/tag/v1.0.174) | 2019-11-12T18:20:58Z | 30,786 | 11,813 | 8,259 | 50,858 |
| [v1.0.174](https://github.com/laurent22/joplin/releases/tag/v1.0.174) | 2019-11-12T18:20:58Z | 30,786 | 11,815 | 8,261 | 50,862 |
| [v1.0.173](https://github.com/laurent22/joplin/releases/tag/v1.0.173) | 2019-11-11T08:33:35Z | 5,160 | 2,128 | 776 | 8,064 |
| [v1.0.170](https://github.com/laurent22/joplin/releases/tag/v1.0.170) | 2019-10-13T22:13:04Z | 27,924 | 8,839 | 7,722 | 44,485 |
| [v1.0.169](https://github.com/laurent22/joplin/releases/tag/v1.0.169) | 2019-09-27T18:35:13Z | 17,300 | 5,979 | 3,789 | 27,068 |
| [v1.0.170](https://github.com/laurent22/joplin/releases/tag/v1.0.170) | 2019-10-13T22:13:04Z | 27,924 | 8,839 | 7,723 | 44,486 |
| [v1.0.169](https://github.com/laurent22/joplin/releases/tag/v1.0.169) | 2019-09-27T18:35:13Z | 17,302 | 5,979 | 3,789 | 27,070 |
| [v1.0.168](https://github.com/laurent22/joplin/releases/tag/v1.0.168) | 2019-09-25T21:21:38Z | 5,400 | 2,321 | 750 | 8,471 |
| [v1.0.167](https://github.com/laurent22/joplin/releases/tag/v1.0.167) | 2019-09-10T08:48:37Z | 17,030 | 5,754 | 3,747 | 26,531 |
| [v1.0.166](https://github.com/laurent22/joplin/releases/tag/v1.0.166) | 2019-09-09T17:35:54Z | 2,010 | 607 | 263 | 2,880 |
| [v1.0.167](https://github.com/laurent22/joplin/releases/tag/v1.0.167) | 2019-09-10T08:48:37Z | 17,032 | 5,754 | 3,747 | 26,533 |
| [v1.0.166](https://github.com/laurent22/joplin/releases/tag/v1.0.166) | 2019-09-09T17:35:54Z | 2,010 | 608 | 264 | 2,882 |
| [v1.0.165](https://github.com/laurent22/joplin/releases/tag/v1.0.165) | 2019-08-14T21:46:29Z | 19,129 | 7,028 | 5,495 | 31,652 |
| [v1.0.161](https://github.com/laurent22/joplin/releases/tag/v1.0.161) | 2019-07-13T18:30:00Z | 19,463 | 6,396 | 4,164 | 30,023 |
| [v1.0.160](https://github.com/laurent22/joplin/releases/tag/v1.0.160) | 2019-06-15T00:21:40Z | 30,895 | 7,802 | 8,140 | 46,837 |
| [v1.0.159](https://github.com/laurent22/joplin/releases/tag/v1.0.159) | 2019-06-08T00:00:19Z | 5,299 | 2,225 | 1,324 | 8,848 |
| [v1.0.158](https://github.com/laurent22/joplin/releases/tag/v1.0.158) | 2019-05-27T19:01:18Z | 9,895 | 3,596 | 1,965 | 15,456 |
| [v1.0.161](https://github.com/laurent22/joplin/releases/tag/v1.0.161) | 2019-07-13T18:30:00Z | 19,465 | 6,396 | 4,164 | 30,025 |
| [v1.0.160](https://github.com/laurent22/joplin/releases/tag/v1.0.160) | 2019-06-15T00:21:40Z | 30,898 | 7,802 | 8,140 | 46,840 |
| [v1.0.159](https://github.com/laurent22/joplin/releases/tag/v1.0.159) | 2019-06-08T00:00:19Z | 5,305 | 2,225 | 1,324 | 8,854 |
| [v1.0.158](https://github.com/laurent22/joplin/releases/tag/v1.0.158) | 2019-05-27T19:01:18Z | 9,896 | 3,596 | 1,965 | 15,457 |
| [v1.0.157](https://github.com/laurent22/joplin/releases/tag/v1.0.157) | 2019-05-26T17:55:53Z | 2,254 | 886 | 317 | 3,457 |
| [v1.0.153](https://github.com/laurent22/joplin/releases/tag/v1.0.153) (p) | 2019-05-15T06:27:29Z | 918 | 146 | 134 | 1,198 |
| [v1.0.152](https://github.com/laurent22/joplin/releases/tag/v1.0.152) | 2019-05-13T09:08:07Z | 13,954 | 4,482 | 4,096 | 22,532 |
| [v1.0.152](https://github.com/laurent22/joplin/releases/tag/v1.0.152) | 2019-05-13T09:08:07Z | 13,954 | 4,483 | 4,097 | 22,534 |
| [v1.0.151](https://github.com/laurent22/joplin/releases/tag/v1.0.151) | 2019-05-12T15:14:32Z | 2,007 | 579 | 987 | 3,573 |
| [v1.0.150](https://github.com/laurent22/joplin/releases/tag/v1.0.150) | 2019-05-12T11:27:48Z | 500 | 180 | 99 | 779 |
| [v1.0.148](https://github.com/laurent22/joplin/releases/tag/v1.0.148) (p) | 2019-05-08T19:12:24Z | 191 | 98 | 123 | 412 |
| [v1.0.145](https://github.com/laurent22/joplin/releases/tag/v1.0.145) | 2019-05-03T09:16:53Z | 7,087 | 2,906 | 1,469 | 11,462 |
| [v1.0.143](https://github.com/laurent22/joplin/releases/tag/v1.0.143) | 2019-04-22T10:51:38Z | 11,998 | 3,598 | 2,814 | 18,410 |
| [v1.0.143](https://github.com/laurent22/joplin/releases/tag/v1.0.143) | 2019-04-22T10:51:38Z | 11,998 | 3,599 | 2,814 | 18,411 |
| [v1.0.142](https://github.com/laurent22/joplin/releases/tag/v1.0.142) | 2019-04-02T16:44:51Z | 14,873 | 4,613 | 4,764 | 24,250 |
| [v1.0.140](https://github.com/laurent22/joplin/releases/tag/v1.0.140) | 2019-03-10T20:59:58Z | 13,757 | 4,225 | 3,411 | 21,393 |
| [v1.0.140](https://github.com/laurent22/joplin/releases/tag/v1.0.140) | 2019-03-10T20:59:58Z | 13,761 | 4,225 | 3,411 | 21,397 |
| [v1.0.139](https://github.com/laurent22/joplin/releases/tag/v1.0.139) (p) | 2019-03-09T10:06:48Z | 190 | 107 | 71 | 368 |
| [v1.0.138](https://github.com/laurent22/joplin/releases/tag/v1.0.138) (p) | 2019-03-03T17:23:00Z | 262 | 132 | 110 | 504 |
| [v1.0.138](https://github.com/laurent22/joplin/releases/tag/v1.0.138) (p) | 2019-03-03T17:23:00Z | 266 | 132 | 110 | 508 |
| [v1.0.137](https://github.com/laurent22/joplin/releases/tag/v1.0.137) (p) | 2019-03-03T01:12:51Z | 652 | 99 | 110 | 861 |
| [v1.0.135](https://github.com/laurent22/joplin/releases/tag/v1.0.135) | 2019-02-27T23:36:57Z | 12,709 | 4,005 | 4,113 | 20,827 |
| [v1.0.135](https://github.com/laurent22/joplin/releases/tag/v1.0.135) | 2019-02-27T23:36:57Z | 12,711 | 4,006 | 4,114 | 20,831 |
| [v1.0.134](https://github.com/laurent22/joplin/releases/tag/v1.0.134) | 2019-02-27T10:21:44Z | 1,516 | 615 | 247 | 2,378 |
| [v1.0.132](https://github.com/laurent22/joplin/releases/tag/v1.0.132) | 2019-02-26T23:02:05Z | 1,154 | 497 | 122 | 1,773 |
| [v1.0.127](https://github.com/laurent22/joplin/releases/tag/v1.0.127) | 2019-02-14T23:12:48Z | 10,044 | 3,220 | 2,965 | 16,229 |
| [v1.0.127](https://github.com/laurent22/joplin/releases/tag/v1.0.127) | 2019-02-14T23:12:48Z | 10,047 | 3,220 | 2,965 | 16,232 |
| [v1.0.126](https://github.com/laurent22/joplin/releases/tag/v1.0.126) (p) | 2019-02-09T19:46:16Z | 1,003 | 119 | 140 | 1,262 |
| [v1.0.125](https://github.com/laurent22/joplin/releases/tag/v1.0.125) | 2019-01-26T18:14:33Z | 10,452 | 3,605 | 1,729 | 15,786 |
| [v1.0.125](https://github.com/laurent22/joplin/releases/tag/v1.0.125) | 2019-01-26T18:14:33Z | 10,454 | 3,605 | 1,729 | 15,788 |
| [v1.0.120](https://github.com/laurent22/joplin/releases/tag/v1.0.120) | 2019-01-10T21:42:53Z | 15,710 | 5,260 | 6,553 | 27,523 |
| [v1.0.119](https://github.com/laurent22/joplin/releases/tag/v1.0.119) | 2018-12-18T12:40:22Z | 9,044 | 3,307 | 2,046 | 14,397 |
| [v1.0.119](https://github.com/laurent22/joplin/releases/tag/v1.0.119) | 2018-12-18T12:40:22Z | 9,049 | 3,307 | 2,046 | 14,402 |
| [v1.0.118](https://github.com/laurent22/joplin/releases/tag/v1.0.118) | 2019-01-11T08:34:13Z | 769 | 292 | 118 | 1,179 |
| [v1.0.117](https://github.com/laurent22/joplin/releases/tag/v1.0.117) | 2018-11-24T12:05:24Z | 16,352 | 4,946 | 6,415 | 27,713 |
| [v1.0.116](https://github.com/laurent22/joplin/releases/tag/v1.0.116) | 2018-11-20T19:09:24Z | 4,073 | 1,174 | 746 | 5,993 |
| [v1.0.115](https://github.com/laurent22/joplin/releases/tag/v1.0.115) | 2018-11-16T16:52:02Z | 3,727 | 1,346 | 830 | 5,903 |
| [v1.0.114](https://github.com/laurent22/joplin/releases/tag/v1.0.114) | 2018-10-24T20:14:10Z | 11,475 | 3,547 | 3,860 | 18,882 |
| [v1.0.111](https://github.com/laurent22/joplin/releases/tag/v1.0.111) | 2018-09-30T20:15:09Z | 12,587 | 3,576 | 3,711 | 19,874 |
| [v1.0.111](https://github.com/laurent22/joplin/releases/tag/v1.0.111) | 2018-09-30T20:15:09Z | 12,587 | 3,576 | 3,712 | 19,875 |
| [v1.0.110](https://github.com/laurent22/joplin/releases/tag/v1.0.110) | 2018-09-29T12:29:21Z | 1,018 | 453 | 145 | 1,616 |
| [v1.0.109](https://github.com/laurent22/joplin/releases/tag/v1.0.109) | 2018-09-27T18:01:41Z | 2,152 | 747 | 360 | 3,259 |
| [v1.0.108](https://github.com/laurent22/joplin/releases/tag/v1.0.108) (p) | 2018-09-29T18:49:29Z | 68 | 63 | 39 | 170 |
| [v1.0.107](https://github.com/laurent22/joplin/releases/tag/v1.0.107) | 2018-09-16T19:51:07Z | 7,213 | 2,180 | 1,740 | 11,133 |
| [v1.0.107](https://github.com/laurent22/joplin/releases/tag/v1.0.107) | 2018-09-16T19:51:07Z | 7,213 | 2,181 | 1,741 | 11,135 |
| [v1.0.106](https://github.com/laurent22/joplin/releases/tag/v1.0.106) | 2018-09-08T15:23:40Z | 4,605 | 1,499 | 343 | 6,447 |
| [v1.0.105](https://github.com/laurent22/joplin/releases/tag/v1.0.105) | 2018-09-05T11:29:36Z | 4,706 | 1,638 | 1,487 | 7,831 |
| [v1.0.104](https://github.com/laurent22/joplin/releases/tag/v1.0.104) | 2018-06-28T20:25:36Z | 15,214 | 4,749 | 7,423 | 27,386 |
| [v1.0.103](https://github.com/laurent22/joplin/releases/tag/v1.0.103) | 2018-06-21T19:38:13Z | 2,134 | 930 | 707 | 3,771 |
| [v1.0.101](https://github.com/laurent22/joplin/releases/tag/v1.0.101) | 2018-06-17T18:35:11Z | 1,379 | 652 | 438 | 2,469 |
| [v1.0.100](https://github.com/laurent22/joplin/releases/tag/v1.0.100) | 2018-06-14T17:41:43Z | 965 | 481 | 276 | 1,722 |
| [v1.0.99](https://github.com/laurent22/joplin/releases/tag/v1.0.99) | 2018-06-10T13:18:23Z | 1,336 | 646 | 411 | 2,393 |
| [v1.0.104](https://github.com/laurent22/joplin/releases/tag/v1.0.104) | 2018-06-28T20:25:36Z | 15,214 | 4,749 | 7,424 | 27,387 |
| [v1.0.103](https://github.com/laurent22/joplin/releases/tag/v1.0.103) | 2018-06-21T19:38:13Z | 2,134 | 931 | 708 | 3,773 |
| [v1.0.101](https://github.com/laurent22/joplin/releases/tag/v1.0.101) | 2018-06-17T18:35:11Z | 1,379 | 653 | 438 | 2,470 |
| [v1.0.100](https://github.com/laurent22/joplin/releases/tag/v1.0.100) | 2018-06-14T17:41:43Z | 965 | 482 | 277 | 1,724 |
| [v1.0.99](https://github.com/laurent22/joplin/releases/tag/v1.0.99) | 2018-06-10T13:18:23Z | 1,336 | 647 | 412 | 2,395 |
| [v1.0.97](https://github.com/laurent22/joplin/releases/tag/v1.0.97) | 2018-06-09T19:23:34Z | 362 | 197 | 89 | 648 |
| [v1.0.96](https://github.com/laurent22/joplin/releases/tag/v1.0.96) | 2018-05-26T16:36:39Z | 2,806 | 1,271 | 1,756 | 5,833 |
| [v1.0.96](https://github.com/laurent22/joplin/releases/tag/v1.0.96) | 2018-05-26T16:36:39Z | 2,808 | 1,272 | 1,758 | 5,838 |
| [v1.0.95](https://github.com/laurent22/joplin/releases/tag/v1.0.95) | 2018-05-25T13:04:30Z | 473 | 262 | 168 | 903 |
| [v1.0.94](https://github.com/laurent22/joplin/releases/tag/v1.0.94) | 2018-05-21T20:52:59Z | 1,221 | 633 | 451 | 2,305 |
| [v1.0.93](https://github.com/laurent22/joplin/releases/tag/v1.0.93) | 2018-05-14T11:36:01Z | 1,886 | 1,391 | 808 | 4,085 |
| [v1.0.91](https://github.com/laurent22/joplin/releases/tag/v1.0.91) | 2018-05-10T14:48:04Z | 875 | 598 | 368 | 1,841 |
| [v1.0.94](https://github.com/laurent22/joplin/releases/tag/v1.0.94) | 2018-05-21T20:52:59Z | 1,221 | 635 | 452 | 2,308 |
| [v1.0.93](https://github.com/laurent22/joplin/releases/tag/v1.0.93) | 2018-05-14T11:36:01Z | 1,886 | 1,398 | 809 | 4,093 |
| [v1.0.91](https://github.com/laurent22/joplin/releases/tag/v1.0.91) | 2018-05-10T14:48:04Z | 875 | 599 | 369 | 1,843 |
| [v1.0.89](https://github.com/laurent22/joplin/releases/tag/v1.0.89) | 2018-05-09T13:05:05Z | 552 | 291 | 161 | 1,004 |
| [v1.0.85](https://github.com/laurent22/joplin/releases/tag/v1.0.85) | 2018-05-01T21:08:24Z | 1,702 | 997 | 681 | 3,380 |
| [v1.0.83](https://github.com/laurent22/joplin/releases/tag/v1.0.83) | 2018-04-04T19:43:58Z | 5,649 | 2,581 | 2,708 | 10,938 |
| [v1.0.82](https://github.com/laurent22/joplin/releases/tag/v1.0.82) | 2018-03-31T19:16:31Z | 754 | 454 | 171 | 1,379 |
| [v1.0.81](https://github.com/laurent22/joplin/releases/tag/v1.0.81) | 2018-03-28T08:13:58Z | 1,042 | 641 | 831 | 2,514 |
| [v1.0.79](https://github.com/laurent22/joplin/releases/tag/v1.0.79) | 2018-03-23T18:00:11Z | 974 | 584 | 433 | 1,991 |
| [v1.0.78](https://github.com/laurent22/joplin/releases/tag/v1.0.78) | 2018-03-17T15:27:18Z | 1,357 | 947 | 920 | 3,224 |
| [v1.0.85](https://github.com/laurent22/joplin/releases/tag/v1.0.85) | 2018-05-01T21:08:24Z | 1,702 | 998 | 683 | 3,383 |
| [v1.0.83](https://github.com/laurent22/joplin/releases/tag/v1.0.83) | 2018-04-04T19:43:58Z | 5,649 | 2,582 | 2,708 | 10,939 |
| [v1.0.82](https://github.com/laurent22/joplin/releases/tag/v1.0.82) | 2018-03-31T19:16:31Z | 754 | 454 | 173 | 1,381 |
| [v1.0.81](https://github.com/laurent22/joplin/releases/tag/v1.0.81) | 2018-03-28T08:13:58Z | 1,042 | 642 | 832 | 2,516 |
| [v1.0.79](https://github.com/laurent22/joplin/releases/tag/v1.0.79) | 2018-03-23T18:00:11Z | 974 | 585 | 434 | 1,993 |
| [v1.0.78](https://github.com/laurent22/joplin/releases/tag/v1.0.78) | 2018-03-17T15:27:18Z | 1,357 | 948 | 921 | 3,226 |
| [v1.0.77](https://github.com/laurent22/joplin/releases/tag/v1.0.77) | 2018-03-16T15:12:35Z | 206 | 148 | 92 | 446 |
| [v1.0.72](https://github.com/laurent22/joplin/releases/tag/v1.0.72) | 2018-03-14T09:44:35Z | 456 | 300 | 102 | 858 |
| [v1.0.70](https://github.com/laurent22/joplin/releases/tag/v1.0.70) | 2018-02-28T20:04:30Z | 2,009 | 1,099 | 1,301 | 4,409 |
| [v1.0.67](https://github.com/laurent22/joplin/releases/tag/v1.0.67) | 2018-02-19T22:51:08Z | 1,950 | 649 | 0 | 2,599 |
| [v1.0.72](https://github.com/laurent22/joplin/releases/tag/v1.0.72) | 2018-03-14T09:44:35Z | 456 | 301 | 104 | 861 |
| [v1.0.70](https://github.com/laurent22/joplin/releases/tag/v1.0.70) | 2018-02-28T20:04:30Z | 2,009 | 1,101 | 1,304 | 4,414 |
| [v1.0.67](https://github.com/laurent22/joplin/releases/tag/v1.0.67) | 2018-02-19T22:51:08Z | 1,950 | 650 | 0 | 2,600 |
| [v1.0.66](https://github.com/laurent22/joplin/releases/tag/v1.0.66) | 2018-02-18T23:09:09Z | 454 | 178 | 111 | 743 |
| [v1.0.65](https://github.com/laurent22/joplin/releases/tag/v1.0.65) | 2018-02-17T20:02:25Z | 352 | 176 | 158 | 686 |
| [v1.0.64](https://github.com/laurent22/joplin/releases/tag/v1.0.64) | 2018-02-16T00:58:20Z | 1,201 | 587 | 1,151 | 2,939 |
| [v1.0.65](https://github.com/laurent22/joplin/releases/tag/v1.0.65) | 2018-02-17T20:02:25Z | 352 | 177 | 158 | 687 |
| [v1.0.64](https://github.com/laurent22/joplin/releases/tag/v1.0.64) | 2018-02-16T00:58:20Z | 1,201 | 588 | 1,151 | 2,940 |
| [v1.0.63](https://github.com/laurent22/joplin/releases/tag/v1.0.63) | 2018-02-14T19:40:36Z | 416 | 204 | 119 | 739 |
| [v1.0.62](https://github.com/laurent22/joplin/releases/tag/v1.0.62) | 2018-02-12T20:19:58Z | 717 | 351 | 403 | 1,471 |
| [v0.10.61](https://github.com/laurent22/joplin/releases/tag/v0.10.61) | 2018-02-08T18:27:39Z | 1,130 | 684 | 990 | 2,804 |
| [v0.10.60](https://github.com/laurent22/joplin/releases/tag/v0.10.60) | 2018-02-06T13:09:56Z | 775 | 569 | 579 | 1,923 |
| [v0.10.54](https://github.com/laurent22/joplin/releases/tag/v0.10.54) | 2018-01-31T20:21:30Z | 1,966 | 1,508 | 350 | 3,824 |
| [v1.0.62](https://github.com/laurent22/joplin/releases/tag/v1.0.62) | 2018-02-12T20:19:58Z | 717 | 351 | 404 | 1,472 |
| [v0.10.61](https://github.com/laurent22/joplin/releases/tag/v0.10.61) | 2018-02-08T18:27:39Z | 1,130 | 685 | 990 | 2,805 |
| [v0.10.60](https://github.com/laurent22/joplin/releases/tag/v0.10.60) | 2018-02-06T13:09:56Z | 775 | 569 | 580 | 1,924 |
| [v0.10.54](https://github.com/laurent22/joplin/releases/tag/v0.10.54) | 2018-01-31T20:21:30Z | 1,966 | 1,509 | 350 | 3,825 |
| [v0.10.52](https://github.com/laurent22/joplin/releases/tag/v0.10.52) | 2018-01-31T19:25:18Z | 167 | 682 | 45 | 894 |
| [v0.10.51](https://github.com/laurent22/joplin/releases/tag/v0.10.51) | 2018-01-28T18:47:02Z | 1,436 | 1,648 | 355 | 3,439 |
| [v0.10.48](https://github.com/laurent22/joplin/releases/tag/v0.10.48) | 2018-01-23T11:19:51Z | 2,115 | 1,800 | 59 | 3,974 |
| [v0.10.48](https://github.com/laurent22/joplin/releases/tag/v0.10.48) | 2018-01-23T11:19:51Z | 2,115 | 1,800 | 60 | 3,975 |
| [v0.10.47](https://github.com/laurent22/joplin/releases/tag/v0.10.47) | 2018-01-16T17:27:17Z | 1,338 | 1,321 | 94 | 2,753 |
| [v0.10.43](https://github.com/laurent22/joplin/releases/tag/v0.10.43) | 2018-01-08T10:12:10Z | 3,496 | 2,411 | 1,248 | 7,155 |
| [v0.10.41](https://github.com/laurent22/joplin/releases/tag/v0.10.41) | 2018-01-05T20:38:12Z | 1,249 | 1,605 | 271 | 3,125 |
| [v0.10.40](https://github.com/laurent22/joplin/releases/tag/v0.10.40) | 2018-01-02T23:16:57Z | 1,643 | 1,836 | 366 | 3,845 |
| [v0.10.39](https://github.com/laurent22/joplin/releases/tag/v0.10.39) | 2017-12-11T21:19:44Z | 6,004 | 4,451 | 3,337 | 13,792 |
| [v0.10.38](https://github.com/laurent22/joplin/releases/tag/v0.10.38) | 2017-12-08T10:12:06Z | 1,094 | 1,279 | 332 | 2,705 |
| [v0.10.41](https://github.com/laurent22/joplin/releases/tag/v0.10.41) | 2018-01-05T20:38:12Z | 1,249 | 1,606 | 272 | 3,127 |
| [v0.10.40](https://github.com/laurent22/joplin/releases/tag/v0.10.40) | 2018-01-02T23:16:57Z | 1,643 | 1,836 | 367 | 3,846 |
| [v0.10.39](https://github.com/laurent22/joplin/releases/tag/v0.10.39) | 2017-12-11T21:19:44Z | 6,004 | 4,452 | 3,337 | 13,793 |
| [v0.10.38](https://github.com/laurent22/joplin/releases/tag/v0.10.38) | 2017-12-08T10:12:06Z | 1,095 | 1,279 | 332 | 2,706 |
| [v0.10.37](https://github.com/laurent22/joplin/releases/tag/v0.10.37) | 2017-12-07T19:38:05Z | 294 | 897 | 113 | 1,304 |
| [v0.10.36](https://github.com/laurent22/joplin/releases/tag/v0.10.36) | 2017-12-05T09:34:40Z | 1,057 | 1,409 | 468 | 2,934 |
| [v0.10.35](https://github.com/laurent22/joplin/releases/tag/v0.10.35) | 2017-12-02T15:56:08Z | 1,615 | 1,599 | 772 | 3,986 |
| [v0.10.34](https://github.com/laurent22/joplin/releases/tag/v0.10.34) | 2017-12-02T14:50:28Z | 120 | 723 | 87 | 930 |
| [v0.10.33](https://github.com/laurent22/joplin/releases/tag/v0.10.33) | 2017-12-02T13:20:39Z | 100 | 718 | 54 | 872 |
| [v0.10.31](https://github.com/laurent22/joplin/releases/tag/v0.10.31) | 2017-12-01T09:56:44Z | 922 | 1,505 | 442 | 2,869 |
| [v0.10.30](https://github.com/laurent22/joplin/releases/tag/v0.10.30) | 2017-11-30T20:28:16Z | 849 | 1,434 | 454 | 2,737 |
| [v0.10.30](https://github.com/laurent22/joplin/releases/tag/v0.10.30) | 2017-11-30T20:28:16Z | 850 | 1,435 | 455 | 2,740 |
| [v0.10.28](https://github.com/laurent22/joplin/releases/tag/v0.10.28) | 2017-11-30T01:07:46Z | 1,500 | 1,768 | 910 | 4,178 |
| [v0.10.26](https://github.com/laurent22/joplin/releases/tag/v0.10.26) | 2017-11-29T16:02:17Z | 311 | 763 | 294 | 1,368 |
| [v0.10.25](https://github.com/laurent22/joplin/releases/tag/v0.10.25) | 2017-11-24T14:27:49Z | 265 | 758 | 6,820 | 7,843 |
| [v0.10.23](https://github.com/laurent22/joplin/releases/tag/v0.10.23) | 2017-11-21T19:38:41Z | 254 | 720 | 70 | 1,044 |
| [v0.10.23](https://github.com/laurent22/joplin/releases/tag/v0.10.23) | 2017-11-21T19:38:41Z | 254 | 721 | 71 | 1,046 |
| [v0.10.22](https://github.com/laurent22/joplin/releases/tag/v0.10.22) | 2017-11-20T21:45:57Z | 198 | 707 | 52 | 957 |
| [v0.10.21](https://github.com/laurent22/joplin/releases/tag/v0.10.21) | 2017-11-18T00:53:15Z | 174 | 699 | 47 | 920 |
| [v0.10.20](https://github.com/laurent22/joplin/releases/tag/v0.10.20) | 2017-11-17T17:18:25Z | 167 | 712 | 60 | 939 |
| [v0.10.19](https://github.com/laurent22/joplin/releases/tag/v0.10.19) | 2017-11-20T18:59:48Z | 194 | 716 | 59 | 969 |
| [v0.10.19](https://github.com/laurent22/joplin/releases/tag/v0.10.19) | 2017-11-20T18:59:48Z | 195 | 716 | 59 | 970 |

View File

@@ -10671,7 +10671,7 @@ __metadata:
react-native-file-viewer: "npm:2.1.5"
react-native-get-random-values: "npm:1.11.0"
react-native-image-picker: "npm:8.2.1"
react-native-localize: "npm:3.5.4"
react-native-localize: "npm:3.6.0"
react-native-modal-datetime-picker: "npm:18.0.0"
react-native-nitro-modules: "npm:0.33.2"
react-native-paper: "npm:5.14.5"
@@ -11148,7 +11148,7 @@ __metadata:
query-string: "npm:7.1.3"
rate-limiter-flexible: "npm:7.4.0"
raw-body: "npm:3.0.2"
samlify: "npm:2.10.1"
samlify: "npm:2.10.2"
short-uuid: "npm:5.2.0"
source-map-support: "npm:0.5.21"
sqlite3: "npm:5.1.6"
@@ -11182,7 +11182,7 @@ __metadata:
dayjs: "npm:1.11.19"
execa: "npm:4.1.0"
fs-extra: "npm:11.3.2"
gettext-extractor: "npm:4.0.1"
gettext-extractor: "npm:4.0.3"
gettext-parser: "npm:7.0.1"
glob: "npm:11.0.3"
gulp: "npm:4.0.2"
@@ -30552,17 +30552,17 @@ __metadata:
languageName: node
linkType: hard
"gettext-extractor@npm:4.0.1":
version: 4.0.1
resolution: "gettext-extractor@npm:4.0.1"
"gettext-extractor@npm:4.0.3":
version: 4.0.3
resolution: "gettext-extractor@npm:4.0.3"
dependencies:
"@types/parse5": "npm:^5"
css-selector-parser: "npm:^1.3"
glob: "npm:11.0.1"
glob: "npm:11.1.0"
parse5: "npm:5 - 6"
pofile: "npm:1.0.x"
typescript: "npm:^5.4.0"
checksum: 10/768a63bb07336d4364381feba14d4463dc5fc9c81e8c74746f0d6e0745663f6549b343a02f54139f9e8f91a953f5b477823c6e6ae3005c369f72306c62e9c13c
checksum: 10/ae8848b7b31e921510b750906df0afe819abfc1da955a19bae68f27ca2fc951e43b028b9f8ccdf4910ee822dedc6c3b03b5b8faf731ad7eceab0ef511732d30f
languageName: node
linkType: hard
@@ -30770,22 +30770,6 @@ __metadata:
languageName: node
linkType: hard
"glob@npm:11.0.1":
version: 11.0.1
resolution: "glob@npm:11.0.1"
dependencies:
foreground-child: "npm:^3.1.0"
jackspeak: "npm:^4.0.1"
minimatch: "npm:^10.0.0"
minipass: "npm:^7.1.2"
package-json-from-dist: "npm:^1.0.0"
path-scurry: "npm:^2.0.0"
bin:
glob: dist/esm/bin.mjs
checksum: 10/57b12a05cc25f1c38f3b24cf6ea7a8bacef11e782c4b9a8c5b0bef3e6c5bcb8c4548cb31eb4115592e0490a024c1bde7359c470565608dd061d3b21179740457
languageName: node
linkType: hard
"glob@npm:11.0.3":
version: 11.0.3
resolution: "glob@npm:11.0.3"
@@ -30802,6 +30786,22 @@ __metadata:
languageName: node
linkType: hard
"glob@npm:11.1.0":
version: 11.1.0
resolution: "glob@npm:11.1.0"
dependencies:
foreground-child: "npm:^3.3.1"
jackspeak: "npm:^4.1.1"
minimatch: "npm:^10.1.1"
minipass: "npm:^7.1.2"
package-json-from-dist: "npm:^1.0.0"
path-scurry: "npm:^2.0.0"
bin:
glob: dist/esm/bin.mjs
checksum: 10/da4501819633daff8822c007bb3f93d5c4d2cbc7b15a8e886660f4497dd251a1fb4f53a85fba1e760b31704eff7164aeb2c7a82db10f9f2c362d12c02fe52cf3
languageName: node
linkType: hard
"glob@npm:7.0.6":
version: 7.0.6
resolution: "glob@npm:7.0.6"
@@ -34489,7 +34489,7 @@ __metadata:
languageName: node
linkType: hard
"jackspeak@npm:^4.0.1, jackspeak@npm:^4.1.1":
"jackspeak@npm:^4.1.1":
version: 4.1.1
resolution: "jackspeak@npm:4.1.1"
dependencies:
@@ -38645,7 +38645,7 @@ __metadata:
languageName: node
linkType: hard
"minimatch@npm:^10.0.0, minimatch@npm:^10.0.1, minimatch@npm:^10.1.1":
"minimatch@npm:^10.0.1, minimatch@npm:^10.1.1":
version: 10.1.1
resolution: "minimatch@npm:10.1.1"
dependencies:
@@ -44258,11 +44258,11 @@ __metadata:
languageName: node
linkType: hard
"react-native-localize@npm:3.5.4":
version: 3.5.4
resolution: "react-native-localize@npm:3.5.4"
"react-native-localize@npm:3.6.0":
version: 3.6.0
resolution: "react-native-localize@npm:3.6.0"
peerDependencies:
"@expo/config-plugins": ^9.0.0 || ^10.0.0
"@expo/config-plugins": "*"
react: "*"
react-native: "*"
react-native-macos: "*"
@@ -44271,7 +44271,7 @@ __metadata:
optional: true
react-native-macos:
optional: true
checksum: 10/afcd00e12561d147ad50b5d2f240a7b7853c5bd8226386e0afab61c7acd7b8a2b40651552272dbb293f5ed68800e0e3e48bd939d98888be81a0f9687c70cdb6b
checksum: 10/1a443a77f3431ad638c577c1fc1b2f0b6234c61fd66dcf1ca095303b23dea47fb8bd0d40b18abc2917b874c9df75fe638c40bec6745fce995ad01ccef69d250e
languageName: node
linkType: hard
@@ -46582,9 +46582,9 @@ __metadata:
languageName: node
linkType: hard
"samlify@npm:2.10.1":
version: 2.10.1
resolution: "samlify@npm:2.10.1"
"samlify@npm:2.10.2":
version: 2.10.2
resolution: "samlify@npm:2.10.2"
dependencies:
"@authenio/xml-encryption": "npm:^2.0.2"
"@xmldom/xmldom": "npm:^0.8.6"
@@ -46597,7 +46597,7 @@ __metadata:
xml-crypto: "npm:^6.1.2"
xml-escape: "npm:^1.1.0"
xpath: "npm:^0.0.32"
checksum: 10/f44259ec1f03faacedbd9b3098b07893dd69204492d1f4c63dca0bd8b979ed9ea76e3911f7e953baa4e11787acdb9f33e5449e21b0199f7ff627f449a9ce3897
checksum: 10/2f46e2b172fd43c3c63ad18e9a715e6cbfaf4a781ef8f6b2f7f42c3a3998ae7e07fc41a3d24a5f54f88e716535b3a8e0c2953a3788fef0430762b29e8f8b9ffe
languageName: node
linkType: hard