1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-02-01 07:49:31 +02:00

Compare commits

..

37 Commits

Author SHA1 Message Date
Laurent Cozic
142d5e83c7 update 2026-01-03 09:18:43 +00:00
renovate[bot]
0538bf0720 Update dependency @rollup/plugin-node-resolve to v16.0.2 (#14023)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-02 20:29:47 +00:00
renovate[bot]
54018c3a94 Update dependency @types/serviceworker to v0.0.157 (#14020)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-02 09:35:32 +00:00
renovate[bot]
0cb120c321 Update dependency @types/serviceworker to v0.0.156 (#14010)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-01 09:52:01 +00:00
Joplin Bot
0dab436420 Doc: Auto-update documentation
Auto-updated using release-website.sh
2026-01-01 02:03:48 +00:00
renovate[bot]
77331ca471 Update dependency @types/react to v18.3.25 (#13999)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-29 11:45:37 +00:00
krevad
d467205b91 All: Translation: Update sv.po (#13997) 2025-12-28 23:47:32 -05:00
renovate[bot]
7a2f686228 Update dependency @types/nodemailer to v6.4.20 (#13998)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-28 23:26:56 +00:00
Joplin Bot
fa37b87c98 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-12-28 02:03:41 +00:00
Laurent Cozic
0eed352684 Android 3.5.6 2025-12-27 20:38:40 +00:00
Laurent Cozic
6ab281d299 Revert "All: Apache Tomcat WebDAV compatibility for sync (#13614)"
Trying to fix #13992

This reverts commit 5be124b54a.
2025-12-27 20:26:36 +00:00
Laurent Cozic
5b94e0d470 Chore: CI: Disable mobile tests on macOS (#13987) 2025-12-27 01:28:15 +00:00
Joplin Bot
5372eeb64a Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-12-26 12:52:51 +00:00
Laurent Cozic
f6baf036dc Android 3.5.5 2025-12-26 10:53:47 +00:00
Henry Heino
610f00029f Mobile: Update js-draw to v1.33.0 (#13990) 2025-12-26 10:19:30 +00:00
renovate[bot]
10be1a0240 Update dependency @types/serviceworker to v0.0.154 (#13991)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-26 09:41:24 +00:00
Laurent Cozic
99a9be535c Update renovate.json5 2025-12-25 18:47:55 +00:00
renovate[bot]
614a95abb8 Update dependency react-native-webview to v13.16.0 (#13982)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-24 12:16:03 +00:00
Henry Heino
7cbaae3847 Mobile: Editor: Fix search/replace UI is partially offscreen on small-screen devices (#13978) 2025-12-24 10:11:23 +00:00
Henry Heino
9e2a6d22ea Chore: Mobile: Allow disabling features known to be incompatible with small screens at build time (#13980) 2025-12-24 10:10:56 +00:00
Henry Heino
f576e116a8 Mobile: Feature flags: Fix "voice typing" feature flag (#13981) 2025-12-24 10:10:12 +00:00
Joplin Bot
b0e912157b Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-12-24 01:52:27 +00:00
Laurent Cozic
c5598242f9 Android 3.5.4 2025-12-23 21:06:18 +00:00
Laurent Cozic
57980ae916 Lock files and prebuild assets 2025-12-23 19:44:54 +00:00
renovate[bot]
9d1720b6e1 Update dependency sass to v1.93.2 (#13972)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 19:41:31 +00:00
Henry Heino
c4e0ed18eb Android: Attempt to fix application hang when opening the camera (#13974) 2025-12-23 19:41:13 +00:00
Henry Heino
150f6c9a3f Android: Fix react-native-vector-icons error when opening a note (#13975) 2025-12-23 19:40:44 +00:00
Henry Heino
6f3781f27a Mobile: Toolbar editor: Fix toolbar editor dismiss button is rendered outside the dialog on small screens (#13976) 2025-12-23 19:40:38 +00:00
Henry Heino
37c3d24650 Chore: Android: Allow disabling the voice typing feature at build time (#13977) 2025-12-23 19:40:29 +00:00
renovate[bot]
bcb3f69d15 Update dependency expo to v53.0.23 (#13968)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 13:00:10 +00:00
renovate[bot]
70ffb29af4 Update dependency @playwright/test to v1.55.1 (#13970)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 12:59:49 +00:00
Henry Heino
5f61bee712 Mobile: Resolves #520: Viewer, Rich Text Editor: Save/restore the cursor and scroll position when switching notes (#13962) 2025-12-23 11:51:15 +00:00
Henry Heino
496d007f74 Mobile: Rich Text Editor: Fix indent/de-indent buttons do nothing when not in a list (#13961) 2025-12-23 11:50:26 +00:00
renovate[bot]
5a9b389504 Update dependency sass to v1.93.1 (#13969)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-22 04:56:42 +00:00
Henry Heino
107290177e Web: Note viewer: Fix assets from development plugins don't load (#13954) 2025-12-21 10:16:28 +00:00
renovate[bot]
5055c9af3e Update dependency @react-native-documents/picker to v10.1.7 (#13964)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-20 15:30:20 +00:00
renovate[bot]
2ed6650136 Update bitnamilegacy/postgresql Docker tag to v17.6.0 (#13966)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-20 15:30:07 +00:00
67 changed files with 1330 additions and 850 deletions

View File

@@ -959,6 +959,7 @@ packages/app-mobile/utils/fs-driver/testUtil/verifyDirectoryMatches.js
packages/app-mobile/utils/getPackageInfo.js
packages/app-mobile/utils/getVersionInfoText.js
packages/app-mobile/utils/hooks/useBackHandler.js
packages/app-mobile/utils/hooks/useDebounced.js
packages/app-mobile/utils/hooks/useIsScreenReaderEnabled.js
packages/app-mobile/utils/hooks/useKeyboardState.js
packages/app-mobile/utils/hooks/useOnLongPressProps.js
@@ -1114,6 +1115,7 @@ packages/editor/CodeMirror/vendor/announceSearchMatch.js
packages/editor/ProseMirror/commands/commands.test.js
packages/editor/ProseMirror/commands/commands.js
packages/editor/ProseMirror/commands/focusEditor.js
packages/editor/ProseMirror/commands/selectDocumentEnd.js
packages/editor/ProseMirror/createEditor.js
packages/editor/ProseMirror/index.js
packages/editor/ProseMirror/plugins/detailsPlugin.test.js
@@ -1146,6 +1148,7 @@ packages/editor/ProseMirror/types.js
packages/editor/ProseMirror/utils/SelectableNodeView.js
packages/editor/ProseMirror/utils/UndoStackSynchronizer.js
packages/editor/ProseMirror/utils/canReplaceSelectionWith.js
packages/editor/ProseMirror/utils/clampPointToDocument.js
packages/editor/ProseMirror/utils/computeSelectionFormatting.js
packages/editor/ProseMirror/utils/dom/createButton.js
packages/editor/ProseMirror/utils/dom/createTextArea.js

4
.gitignore vendored
View File

@@ -1,6 +1,7 @@
_mydocs
_releases
_vieux/
.claude
!/var/cache
!/var/logs
!/var/sessions
@@ -931,6 +932,7 @@ packages/app-mobile/utils/fs-driver/testUtil/verifyDirectoryMatches.js
packages/app-mobile/utils/getPackageInfo.js
packages/app-mobile/utils/getVersionInfoText.js
packages/app-mobile/utils/hooks/useBackHandler.js
packages/app-mobile/utils/hooks/useDebounced.js
packages/app-mobile/utils/hooks/useIsScreenReaderEnabled.js
packages/app-mobile/utils/hooks/useKeyboardState.js
packages/app-mobile/utils/hooks/useOnLongPressProps.js
@@ -1086,6 +1088,7 @@ packages/editor/CodeMirror/vendor/announceSearchMatch.js
packages/editor/ProseMirror/commands/commands.test.js
packages/editor/ProseMirror/commands/commands.js
packages/editor/ProseMirror/commands/focusEditor.js
packages/editor/ProseMirror/commands/selectDocumentEnd.js
packages/editor/ProseMirror/createEditor.js
packages/editor/ProseMirror/index.js
packages/editor/ProseMirror/plugins/detailsPlugin.test.js
@@ -1118,6 +1121,7 @@ packages/editor/ProseMirror/types.js
packages/editor/ProseMirror/utils/SelectableNodeView.js
packages/editor/ProseMirror/utils/UndoStackSynchronizer.js
packages/editor/ProseMirror/utils/canReplaceSelectionWith.js
packages/editor/ProseMirror/utils/clampPointToDocument.js
packages/editor/ProseMirror/utils/computeSelectionFormatting.js
packages/editor/ProseMirror/utils/dom/createButton.js
packages/editor/ProseMirror/utils/dom/createTextArea.js

View File

@@ -19,7 +19,7 @@
services:
postgresql-master:
image: 'bitnamilegacy/postgresql:17.5.0'
image: 'bitnamilegacy/postgresql:17.6.0'
ports:
- '5432:5432'
environment:
@@ -36,7 +36,7 @@ services:
- POSTGRESQL_EXTRA_FLAGS=-c work_mem=100000 -c log_statement=all
postgresql-slave:
image: 'bitnamilegacy/postgresql:17.5.0'
image: 'bitnamilegacy/postgresql:17.6.0'
ports:
- '5433:5432'
depends_on:

View File

@@ -26,7 +26,16 @@
module.exports = {
testMatch: [
'**/tests/**/*.js',
'**/tests/HtmlToHtml.js',
'**/tests/HtmlToMd.js',
'**/tests/MarkupToHtml.js',
'**/tests/MdToHtml.js',
'**/tests/feature_NoteHistory.js',
'**/tests/feature_NoteList.js',
'**/tests/feature_ShowAllNotes.js',
'**/tests/feature_TagList.js',
'**/tests/services/**/*.js',
'**/*.test.js',
],

View File

@@ -7,7 +7,7 @@
"scripts": {
"test": "jest --verbose=false --config=jest.config.js --bail --forceExit",
"test-one": "jest --verbose=false --config=jest.config.js --bail --forceExit",
"test-ci": "jest --config=jest.config.js --forceExit",
"test-ci": "jest --config=jest.config.js --forceExit --testPathIgnorePatterns=cli-integration-tests.test",
"build": "gulp build",
"start": "gulp build -L && node \"build/main.js\" --stack-trace-enabled --log-level debug --env dev",
"start-no-build": "node \"build/main.js\" --stack-trace-enabled --log-level debug --env dev",

View File

@@ -145,13 +145,13 @@
"@joplin/renderer": "~3.5",
"@joplin/tools": "~3.5",
"@joplin/utils": "~3.5",
"@playwright/test": "1.55.0",
"@playwright/test": "1.55.1",
"@sentry/electron": "4.24.0",
"@testing-library/react-hooks": "8.0.1",
"@types/jest": "29.5.14",
"@types/mustache": "4.2.6",
"@types/node": "18.19.130",
"@types/react": "18.3.23",
"@types/react": "18.3.25",
"@types/react-dom": "18.3.7",
"@types/react-redux": "7.1.33",
"@types/styled-components": "5.1.32",

View File

@@ -89,8 +89,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097783
versionName "3.5.3"
versionCode 2097786
versionName "3.5.6"
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}

View File

@@ -5,7 +5,6 @@ import { ForwardedRef, forwardRef, useCallback, useEffect, useImperativeHandle,
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
import { CameraRef, Props } from './types';
import { _ } from '@joplin/lib/locale';
import { Platform } from 'react-native';
import Logger from '@joplin/utils/Logger';
const logger = Logger.create('Camera/expo');
@@ -66,7 +65,9 @@ const Camera = (props: Props, ref: ForwardedRef<CameraRef>) => {
// iOS issue workaround: Since upgrading to Expo SDK 52, closing and reopening the camera on iOS
// never emits onCameraReady. As a workaround, call .resumePreview and wait for it to resolve,
// rather than relying on the CameraView's onCameraReady prop.
if (Platform.OS === 'ios' && camera) {
//
// Update 12/23/2025: This also happens on certain Android devices.
if (camera) {
// Work around an issue on iOS where the onCameraReady callback is never called.
// Instead, wait for the preview to start using resumePreview:
await camera.resumePreview();

View File

@@ -85,7 +85,7 @@ const useStyles = ({ themeId, style, cameraRatio }: UseStyleProps) => {
}, [themeId, style, outputPositioning]);
};
const androidRatios = ['1:1', '4:3', '16:9'];
const androidRatios = ['4:3', '16:9'];
const iOSRatios: string[] = [];
const useAvailableRatios = (): string[] => {
return Platform.OS === 'android' ? androidRatios : iOSRatios;

View File

@@ -67,6 +67,10 @@ const useStyles = (themeId: number, containerStyle: ViewStyle, size: DialogVaria
alignSelf: 'center',
},
heading: {
// Without flexShrink/flexGrow, the heading can push the close button
// outside of the dialog.
flexShrink: 1,
flexGrow: 1,
},
modalBackground: {
justifyContent: 'center',

View File

@@ -27,7 +27,7 @@ interface WrapperProps {
noteBody: string;
highlightedKeywords?: string[];
noteResources?: Record<string, ResourceInfo>;
onScroll?: (percent: number)=> void;
onScroll?: ()=> void;
onMarkForDownload?: OnMarkForDownloadCallback;
}
@@ -52,7 +52,7 @@ const WrappedNoteViewer: React.FC<WrapperProps> = (
highlightedKeywords={highlightedKeywords}
noteResources={noteResources}
paddingBottom={0}
initialScroll={0}
initialScrollPercent={0}
noteHash={''}
onMarkForDownload={onMarkForDownload}
onScroll={onScroll}

View File

@@ -15,6 +15,7 @@ import CommandService from '@joplin/lib/services/CommandService';
import { AppState } from '../../utils/types';
import { connect } from 'react-redux';
import useWebViewSetup from '../../contentScripts/rendererBundle/useWebViewSetup';
import { OnScrollCallback } from '../../contentScripts/rendererBundle/types';
interface Props {
themeId: number;
@@ -25,12 +26,12 @@ interface Props {
highlightedKeywords: string[];
noteResources: Record<string, ResourceInfo>;
paddingBottom: number;
initialScroll: number|null;
initialScrollPercent: number|null;
noteHash: string;
onCheckboxChange?: HandleMessageCallback;
onRequestEditResource?: HandleMessageCallback;
onMarkForDownload?: OnMarkForDownloadCallback;
onScroll: (scrollTop: number)=> void;
onScroll: OnScrollCallback;
onLoadEnd?: ()=> void;
pluginStates: PluginStates;
}
@@ -46,9 +47,7 @@ const onJoplinLinkClick = async (message: string) => {
function NoteBodyViewer(props: Props) {
const webviewRef = useRef<WebViewControl>(null);
const onScroll = useCallback(async (scrollTop: number) => {
props.onScroll(scrollTop);
}, [props.onScroll]);
const onScroll = props.onScroll;
const onResourceLongPress = useOnResourceLongPress(
{
@@ -82,7 +81,7 @@ function NoteBodyViewer(props: Props) {
highlightedKeywords: props.highlightedKeywords,
noteResources: props.noteResources,
noteHash: props.noteHash,
initialScroll: props.initialScroll,
initialScrollPercent: props.initialScrollPercent,
paddingBottom: props.paddingBottom,
});

View File

@@ -24,7 +24,7 @@ interface Props {
highlightedKeywords: string[];
noteResources: Record<string, ResourceInfo>;
noteHash: string;
initialScroll: number|undefined;
initialScrollPercent: number|undefined;
paddingBottom: number;
}
@@ -136,7 +136,7 @@ const useRerenderHandler = (props: Props) => {
// If the hash changed, we don't set initial scroll -- we want to scroll to the hash
// instead.
initialScroll: (previousHash && hashChanged) ? undefined : props.initialScroll,
initialScrollPercent: (previousHash && hashChanged) ? undefined : props.initialScrollPercent,
noteHash: props.noteHash,
};

View File

@@ -1,4 +1,4 @@
import { OnMessageEvent } from '../ExtendedWebView/types';
export type OnScrollCallback = (scrollTop: number)=> void;
export { OnScrollCallback } from '../../contentScripts/rendererBundle/types';
export type OnWebViewMessageHandler = (event: OnMessageEvent)=> void;

View File

@@ -28,13 +28,16 @@ const defaultEditorProps = {
globalSearch: '',
noteId: '',
noteHash: '',
initialScroll: 0,
style: {},
toolbarEnabled: true,
readOnly: false,
onChange: ()=>{},
onSelectionChange: ()=>{},
onUndoRedoDepthChange: ()=>{},
onScroll: ()=>{},
onAttach: async ()=>{},
onSearchVisibleChange: ()=>{},
noteResources: {},
plugins: {},
mode: EditorType.Markdown,

View File

@@ -13,7 +13,7 @@ import { editorFont } from '../global-style';
import { EditorControl as EditorBodyControl, ContentScriptData } from '@joplin/editor/types';
import { EditorControl, EditorSettings, EditorType } from './types';
import { _ } from '@joplin/lib/locale';
import { ChangeEvent, EditorEvent, EditorEventType, SelectionRangeChangeEvent, UndoRedoDepthChangeEvent } from '@joplin/editor/events';
import { ChangeEvent, EditorEvent, EditorEventType, EditorScrolledEvent, SelectionRangeChangeEvent, UndoRedoDepthChangeEvent } from '@joplin/editor/events';
import { EditorCommandType, EditorKeymap, EditorLanguageType, SearchState } from '@joplin/editor/types';
import SelectionFormatting, { defaultSelectionFormatting } from '@joplin/editor/SelectionFormatting';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
@@ -34,18 +34,23 @@ import { MarkupLanguage } from '@joplin/renderer';
import WarningBanner from './WarningBanner';
import useIsScreenReaderEnabled from '../../utils/hooks/useIsScreenReaderEnabled';
import Logger from '@joplin/utils/Logger';
import { Second } from '@joplin/utils/time';
import useDebounced from '../../utils/hooks/useDebounced';
const logger = Logger.create('NoteEditor');
type ChangeEventHandler = (event: ChangeEvent)=> void;
type UndoRedoDepthChangeHandler = (event: UndoRedoDepthChangeEvent)=> void;
type SelectionChangeEventHandler = (event: SelectionRangeChangeEvent)=> void;
type OnAttachCallback = (filePath?: string)=> Promise<void>;
type OnChange = (event: ChangeEvent)=> void;
type OnSearchVisibleChange = (visible: boolean)=> void;
type OnScroll = (event: EditorScrolledEvent)=> void;
type OnUndoRedoDepthChange = (event: UndoRedoDepthChangeEvent)=> void;
type OnSelectionChange = (event: SelectionRangeChangeEvent)=> void;
type OnAttach = (filePath?: string)=> Promise<void>;
interface Props {
ref: Ref<EditorControl>;
themeId: number;
initialText: string;
initialScroll: number;
mode: EditorType;
markupLanguage: MarkupLanguage;
noteId: string;
@@ -58,10 +63,12 @@ interface Props {
plugins: PluginStates;
noteResources: ResourceInfos;
onChange: ChangeEventHandler;
onSelectionChange: SelectionChangeEventHandler;
onUndoRedoDepthChange: UndoRedoDepthChangeHandler;
onAttach: OnAttachCallback;
onScroll: OnScroll;
onChange: OnChange;
onSearchVisibleChange: OnSearchVisibleChange;
onSelectionChange: OnSelectionChange;
onUndoRedoDepthChange: OnUndoRedoDepthChange;
onAttach: OnAttach;
}
function fontFamilyFromSettings() {
@@ -257,6 +264,19 @@ const useHighlightActiveLine = () => {
return canHighlight && Setting.value('editor.highlightActiveLine');
};
const useHasSpaceForToolbar = () => {
const [hasSpaceForToolbar, setHasSpaceForToolbar] = useState(true);
const onContainerLayout = useCallback((event: LayoutChangeEvent) => {
const containerHeight = event.nativeEvent.layout.height;
setHasSpaceForToolbar(containerHeight >= 140);
}, []);
const debouncedHasSpaceForToolbar = useDebounced(hasSpaceForToolbar, Second / 4);
return { hasSpaceForToolbar: debouncedHasSpaceForToolbar, onContainerLayout };
};
function NoteEditor(props: Props) {
const webviewRef = useRef<WebViewControl>(null);
@@ -292,6 +312,7 @@ function NoteEditor(props: Props) {
const [searchState, setSearchState] = useState(defaultSearchState);
const editorControlRef = useRef<EditorControl|null>(null);
const lastSearchVisibleRef = useRef<boolean|undefined>(undefined);
const onEditorEvent = (event: EditorEvent) => {
let exhaustivenessCheck: never;
switch (event.kind) {
@@ -322,21 +343,29 @@ function NoteEditor(props: Props) {
// If the change to the search was done by this editor, it was already applied to the
// search state. Skipping the update in this case also helps avoid overwriting the
// search state with an older value.
const showSearch = event.searchState.dialogVisible ?? lastSearchVisibleRef.current;
if (hasExternalChange) {
setSearchState(event.searchState);
if (event.searchState.dialogVisible) {
if (showSearch) {
editorControl.searchControl.showSearch();
} else {
editorControl.searchControl.hideSearch();
}
}
if (showSearch !== lastSearchVisibleRef.current) {
props.onSearchVisibleChange(showSearch);
lastSearchVisibleRef.current = showSearch;
}
break;
}
case EditorEventType.Remove:
case EditorEventType.Scroll:
// Not handled
break;
case EditorEventType.Scroll:
props.onScroll(event);
break;
default:
exhaustivenessCheck = event;
return exhaustivenessCheck;
@@ -382,18 +411,6 @@ function NoteEditor(props: Props) {
return editorControl;
});
const [hasSpaceForToolbar, setHasSpaceForToolbar] = useState(true);
const toolbarEnabled = props.toolbarEnabled && hasSpaceForToolbar;
const onContainerLayout = useCallback((event: LayoutChangeEvent) => {
const containerHeight = event.nativeEvent.layout.height;
if (containerHeight < 140) {
setHasSpaceForToolbar(false);
} else {
setHasSpaceForToolbar(true);
}
}, []);
const onAttach = useCallback(async (type: string, base64: string) => {
const tempFilePath = join(Setting.value('tempDir'), `paste.${uuid.createNano()}.${toFileExtension(type)}`);
@@ -411,7 +428,11 @@ function NoteEditor(props: Props) {
searchVisible: searchState.dialogVisible,
}), [selectionState, searchState.dialogVisible]);
const { hasSpaceForToolbar, onContainerLayout } = useHasSpaceForToolbar();
const toolbarEnabled = props.toolbarEnabled && hasSpaceForToolbar;
const toolbar = <EditorToolbar editorState={toolbarEditorState} />;
const EditorComponent = props.mode === EditorType.Markdown ? MarkdownEditor : RichTextEditor;
return (
@@ -442,6 +463,7 @@ function NoteEditor(props: Props) {
noteHash={props.noteHash}
initialText={props.initialText}
initialSelection={props.initialSelection}
initialScroll={props.initialScroll}
editorSettings={editorSettings}
globalSearch={props.globalSearch}
onEditorEvent={onEditorEvent}

View File

@@ -96,6 +96,8 @@ const RichTextEditor: React.FC<EditorProps> = props => {
themeId: props.themeId,
pluginStates: props.plugins,
noteResources: props.noteResources,
initialSelection: props.initialSelection,
initialScroll: props.initialScroll,
onPostMessage: onPostMessage,
onAttachFile: props.onAttach,
});

View File

@@ -12,6 +12,7 @@ const defaultWrapperProps: EditorProps = {
noteHash: '',
noteId: '',
initialText: '',
initialScroll: 0,
editorSettings: defaultEditorSettings,
initialSelection: { start: 0, end: 0 },
globalSearch: '',

View File

@@ -62,6 +62,7 @@ export interface EditorProps {
noteHash: string;
initialText: string;
initialSelection: SelectionRange;
initialScroll: number;
editorSettings: EditorSettings;
globalSearch: string;
plugins: PluginStates;

View File

@@ -7,7 +7,7 @@ import NoteBodyViewer from '../../NoteBodyViewer/NoteBodyViewer';
import checkPermissions from '../../../utils/checkPermissions';
import NoteEditor from '../../NoteEditor/NoteEditor';
import * as React from 'react';
import { Keyboard, View, TextInput, StyleSheet, Linking, Share, NativeSyntheticEvent } from 'react-native';
import { Keyboard, View, TextInput, StyleSheet, Linking, Share, NativeSyntheticEvent, useWindowDimensions } from 'react-native';
import { Platform, PermissionsAndroid } from 'react-native';
import { connect } from 'react-redux';
import Note from '@joplin/lib/models/Note';
@@ -76,6 +76,11 @@ import { EditorType } from '../../NoteEditor/types';
import { IconButton } from 'react-native-paper';
import { writeTextToCacheFile } from '../../../utils/ShareUtils';
import shareFile from '../../../utils/shareFile';
import NotePositionService from '@joplin/lib/services/NotePositionService';
import useKeyboardState from '../../../utils/hooks/useKeyboardState';
import VoiceTyping from '../../../services/voiceTyping/VoiceTyping';
import useDebounced from '../../../utils/hooks/useDebounced';
import { Second } from '@joplin/utils/time';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const emptyArray: any[] = [];
@@ -120,12 +125,14 @@ interface Props extends BaseProps {
interface ComponentProps extends Props {
dialogs: DialogControl;
visibleEditorPluginIds: string[];
lowVerticalSpace: boolean;
}
interface State {
note: NoteEntity;
mode: NoteViewerMode;
readOnly: boolean;
searchVisible: boolean;
folder: FolderEntity|null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
lastSavedNote: any;
@@ -154,6 +161,8 @@ interface State {
multiline: boolean;
}
type ScrollEventSlice = { fraction: number };
class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> implements BaseNoteScreenComponent<State> {
// This isn't in this.state because we don't want changing scroll to trigger
// a re-render.
@@ -211,6 +220,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
showCamera: false,
showImageEditor: false,
showAudioRecorder: false,
searchVisible: false,
imageEditorResource: null,
noteResources: {},
imageEditorResourceFilepath: null,
@@ -226,6 +236,13 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
multiline: false,
};
const initialCursorLocation = NotePositionService.instance().getCursorPosition(props.noteId, defaultWindowId).markdown;
if (initialCursorLocation) {
this.selection = { start: initialCursorLocation, end: initialCursorLocation };
}
const initialScroll = NotePositionService.instance().getScrollPercent(props.noteId, defaultWindowId);
this.lastBodyScroll = initialScroll;
this.titleTextFieldRef = React.createRef();
this.saveActionQueues_ = {};
@@ -379,6 +396,9 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
return this.props.useEditorBeta;
}
private onSearchVisibleChange_ = (visible: boolean) => {
this.setState({ searchVisible: visible });
};
private onUndoRedoDepthChange(event: UndoRedoDepthChangeEvent) {
if (this.useEditorBeta()) {
@@ -770,8 +790,12 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
this.selection = event.nativeEvent.selection;
};
private onMarkdownEditorSelectionChange = (event: SelectionRangeChangeEvent) => {
private onEditorSelectionChange = (event: SelectionRangeChangeEvent) => {
this.selection = { start: event.from, end: event.to };
NotePositionService.instance().updateCursorPosition(
this.props.noteId, defaultWindowId, { markdown: event.from },
);
};
public makeSaveAction(state: State) {
@@ -1291,8 +1315,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
});
}
const voiceTypingSupported = Platform.OS === 'android';
if (voiceTypingSupported) {
if (VoiceTyping.supported()) {
output.push({
title: _('Voice typing...'),
onPress: () => {
@@ -1487,10 +1510,16 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
return this.folderPickerOptions_;
}
private onBodyViewerScroll = (scrollTop: number) => {
this.lastBodyScroll = scrollTop;
private onBodyViewerScroll = (event: ScrollEventSlice) => {
this.lastBodyScroll = event.fraction;
NotePositionService.instance().updateScrollPosition(
this.props.noteId, defaultWindowId, event.fraction,
);
};
private onMarkdownEditorScroll = () => {};
public onBodyViewerCheckboxChange(newBody: string) {
void this.saveOneProperty('body', newBody);
}
@@ -1580,6 +1609,14 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
// Currently keyword highlighting is supported only when FTS is available.
const keywords = this.props.searchQuery && !!this.props.ftsEnabled ? this.props.highlightedWords : emptyArray;
const increaseSpaceForEditor = this.props.lowVerticalSpace
&& this.state.mode === 'edit'
// For now, only dismiss other UI when search is visible. This provides a way to re-show the hidden UI (by dismissing search).
&& this.state.searchVisible
// Tapping on the title input when search is visible should edit the title, even if showing the keyboard decreases the
// available space.
&& !this.titleTextFieldRef.current?.isFocused();
let bodyComponent = null;
if (editorView) {
@@ -1604,7 +1641,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
onMarkForDownload={this.onMarkForDownload}
onRequestEditResource={this.onEditResource}
onScroll={this.onBodyViewerScroll}
initialScroll={this.lastBodyScroll}
initialScrollPercent={this.lastBodyScroll}
/>
);
} else {
@@ -1649,7 +1686,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
bodyComponent = <NoteEditor
ref={this.editorRef}
toolbarEnabled={this.props.toolbarEnabled}
toolbarEnabled={this.props.toolbarEnabled && !increaseSpaceForEditor}
themeId={this.props.themeId}
noteId={this.props.noteId}
noteHash={this.props.noteHash}
@@ -1658,8 +1695,9 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
markupLanguage={this.state.note.markup_language}
globalSearch={this.props.searchQuery}
onChange={this.onMarkdownEditorTextChange}
onSelectionChange={this.onMarkdownEditorSelectionChange}
onSelectionChange={this.onEditorSelectionChange}
onUndoRedoDepthChange={this.onUndoRedoDepthChange}
onSearchVisibleChange={this.onSearchVisibleChange_}
onAttach={this.onAttach}
noteResources={this.state.noteResources}
readOnly={this.state.readOnly}
@@ -1671,6 +1709,14 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
paddingLeft: 0,
paddingRight: 0,
}}
// For now, only save/restore the scroll location for the Rich Text editor since that editor's
// scroll should roughly match the viewer. In the future, it may make sense to refactor this to
// use mapsToLine (similar to what's done on desktop) to sync the Markdown editor scroll, but this
// will require refactoring.
initialScroll={this.props.editorType === EditorType.RichText ? this.lastBodyScroll : undefined}
onScroll={this.props.editorType === EditorType.RichText ? this.onBodyViewerScroll : this.onMarkdownEditorScroll}
mode={this.props.editorType}
/>;
}
@@ -1762,25 +1808,27 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
const { editorPlugin: activeEditorPlugin } = getActivePluginEditorView(this.props.plugins, this.props.windowId);
const header = <ScreenHeader
folderPickerOptions={this.folderPickerOptions()}
menuOptions={this.menuOptions()}
showSaveButton={showSaveButton}
saveButtonDisabled={saveButtonDisabled}
onSaveButtonPress={this.saveNoteButton_press}
showSideMenuButton={false}
showSearchButton={false}
showUndoButton={(this.state.undoRedoButtonState.canUndo || this.state.undoRedoButtonState.canRedo) && this.state.mode === 'edit'}
showRedoButton={this.state.undoRedoButtonState.canRedo && this.state.mode === 'edit'}
showPluginEditorButton={!!activeEditorPlugin}
undoButtonDisabled={!this.state.undoRedoButtonState.canUndo && this.state.undoRedoButtonState.canRedo}
onUndoButtonPress={this.screenHeader_undoButtonPress}
onRedoButtonPress={this.screenHeader_redoButtonPress}
title={getDisplayParentTitle(this.state.note, this.state.folder)}
/>;
return (
<View style={this.rootStyle(this.props.themeId).root}>
<ScreenHeader
folderPickerOptions={this.folderPickerOptions()}
menuOptions={this.menuOptions()}
showSaveButton={showSaveButton}
saveButtonDisabled={saveButtonDisabled}
onSaveButtonPress={this.saveNoteButton_press}
showSideMenuButton={false}
showSearchButton={false}
showUndoButton={(this.state.undoRedoButtonState.canUndo || this.state.undoRedoButtonState.canRedo) && this.state.mode === 'edit'}
showRedoButton={this.state.undoRedoButtonState.canRedo && this.state.mode === 'edit'}
showPluginEditorButton={!!activeEditorPlugin}
undoButtonDisabled={!this.state.undoRedoButtonState.canUndo && this.state.undoRedoButtonState.canRedo}
onUndoButtonPress={this.screenHeader_undoButtonPress}
onRedoButtonPress={this.screenHeader_redoButtonPress}
title={getDisplayParentTitle(this.state.note, this.state.folder)}
/>
{titleComp}
{!increaseSpaceForEditor && header}
{!increaseSpaceForEditor && titleComp}
{bodyComponent}
{renderVoiceTypingDialogs()}
{renderActionButton()}
@@ -1798,6 +1846,17 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
}
}
const useHasLowAvailableSpace = () => {
const windowDimensions = useWindowDimensions();
const keyboardState = useKeyboardState();
const verticalSpaceAvailable = windowDimensions.height - keyboardState.dockedKeyboardHeight;
const lowVerticalScreenSpace = verticalSpaceAvailable < 270;
// Debounce state updates to avoid multiple re-renders when the keyboard is hidden, then quickly
// re-shown (e.g. when moving focus between text inputs).
return useDebounced(lowVerticalScreenSpace, Second / 10);
};
// We added this change to reset the component state when the props.noteId is changed.
// NoteScreenComponent original implementation assumed that noteId would never change,
// which can cause some bugs where previously set state to another note would interfere
@@ -1805,9 +1864,16 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
const NoteScreenWrapper = (props: Props) => {
const dialogs = useContext(DialogContext);
const visibleEditorPluginIds = useVisiblePluginEditorViewIds(props.plugins, props.windowId);
const lowVerticalSpace = useHasLowAvailableSpace();
return (
<NoteScreenComponent key={props.noteId} dialogs={dialogs} visibleEditorPluginIds={visibleEditorPluginIds} {...props} />
<NoteScreenComponent
key={props.noteId}
dialogs={dialogs}
visibleEditorPluginIds={visibleEditorPluginIds}
lowVerticalSpace={lowVerticalSpace}
{...props}
/>
);
};

View File

@@ -22,6 +22,7 @@ import { themeStyle } from '../global-style';
import getHelpMessage from '@joplin/lib/components/shared/NoteRevisionViewer/getHelpMessage';
import { DialogContext } from '../DialogManager';
import useDeleteHistoryClick from '@joplin/lib/components/shared/NoteRevisionViewer/useDeleteHistoryClick';
import { OnScrollCallback } from '../NoteBodyViewer/types';
interface Props {
themeId: number;
@@ -153,6 +154,10 @@ const NoteRevisionViewer: React.FC<Props> = props => {
return result;
}, [revisions]);
const onScroll: OnScrollCallback = useCallback((event) => {
setInitialScroll(event.fraction);
}, []);
const onOptionSelected = useCallback((value: string) => {
setCurrentRevisionId(value);
}, []);
@@ -280,8 +285,8 @@ const NoteRevisionViewer: React.FC<Props> = props => {
noteResources={resources}
highlightedKeywords={emptyStringList}
paddingBottom={0}
initialScroll={initialScroll}
onScroll={setInitialScroll}
initialScrollPercent={initialScroll}
onScroll={onScroll}
noteHash={''}
/>
</View>;

View File

@@ -4,7 +4,6 @@ import { Text, Button } from 'react-native-paper';
import { _, languageName } from '@joplin/lib/locale';
import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect';
import VoiceTyping, { OnTextCallback, VoiceTypingSession } from '../../services/voiceTyping/VoiceTyping';
import whisper from '../../services/voiceTyping/whisper';
import { RecorderState } from './types';
import RecordingControls from './RecordingControls';
import { PrimaryButton } from '../buttons';
@@ -38,7 +37,7 @@ const useVoiceTyping = ({ locale, onSetPreview, onText }: UseVoiceTypingProps) =
voiceTypingRef.current = voiceTyping;
const builder = useMemo(() => {
return new VoiceTyping(locale, [whisper]);
return new VoiceTyping(locale);
}, [locale]);
const [redownloadCounter, setRedownloadCounter] = useState(0);

View File

@@ -80,6 +80,21 @@ const useCss = (editorTheme: Theme) => {
.toolbar-edge-toolbar:not(.one-row) .toolwidget-tag--exit .toolbar-icon {
display: none;
}
${Setting.value('buildFlag.ui.disableSmallScreenIncompatibleFeatures') ? `
/* As of December 2025, the help overlay is difficult to use on small screens
(slow to load, help text overlapping content in some cases). */
.js-draw .toolbar-help-overlay-button {
display: none;
}
/* As of December 2025, the pipette button is difficult to use on small screens:
It may not be clear that it's necessary to dismiss the tool menu in order to
pick a color from the screen. */
.js-draw .color-input-container > button.pipetteButton.pipetteButton {
display: none;
}
` : ''}
`;
}, [editorTheme]);
};

View File

@@ -10,7 +10,7 @@ const defaultRendererSettings: RenderSettings = {
resources: {},
codeTheme: 'atom-one-light.css',
noteHash: '',
initialScroll: 0,
initialScrollPercent: 0,
readAssetBlob: async (_path: string) => new Blob(),
removeUnusedPluginAssets: true,

View File

@@ -20,7 +20,7 @@ export interface RenderSettings {
resources: ResourceInfos;
codeTheme: string;
noteHash: string;
initialScroll: number;
initialScrollPercent: number;
// If [null], plugin assets are not added to the document
pluginAssetContainerSelector: string|null;
removeUnusedPluginAssets: boolean;

View File

@@ -64,7 +64,10 @@ export const initialize = (options: RendererWebViewOptions) => {
const onMainContentScroll = () => {
const newScrollTop = document.scrollingElement.scrollTop;
if (lastScrollTop !== newScrollTop) {
messenger.remoteApi.onScroll(newScrollTop);
const scrollHeight = document.scrollingElement.scrollHeight;
messenger.remoteApi.onScroll({
fraction: newScrollTop / (scrollHeight || 1),
});
}
};

View File

@@ -19,13 +19,13 @@ const afterFullPageRender = (
}
const hash = renderSettings.noteHash;
const initialScroll = renderSettings.initialScroll;
const initialScrollPercent = renderSettings.initialScrollPercent;
// Don't scroll to a hash if we're given initial scroll (initial scroll
// overrides scrolling to a hash).
if ((initialScroll ?? null) !== null) {
if ((initialScrollPercent ?? null) !== null) {
const scrollingElement = document.scrollingElement ?? document.documentElement;
scrollingElement.scrollTop = initialScroll;
scrollingElement.scrollTop = initialScrollPercent * scrollingElement.scrollHeight;
} else if (hash) {
// Gives it a bit of time before scrolling to the anchor
// so that images are loaded.

View File

@@ -30,20 +30,24 @@ export interface ExtraContentScriptSource {
pluginId: string;
}
export interface ScrollEvent {
fraction: number; // e.g. 0.5 when scrolled 50% of the way through the document
}
export type OnScrollCallback = (scrollTop: ScrollEvent)=> void;
export interface RendererProcessApi {
renderer: Renderer;
jumpToHash: (hash: string)=> void;
}
export interface MainProcessApi {
onScroll(scrollTop: number): void;
onScroll: OnScrollCallback;
onPostMessage(message: string): void;
onPostPluginMessage(contentScriptId: string, message: unknown): Promise<unknown>;
fsDriver: RendererFsDriver;
}
export type OnScrollCallback = (scrollTop: number)=> void;
export interface MarkupRecord {
language: MarkupLanguage;
markup: string;
@@ -62,7 +66,7 @@ export interface RenderOptions {
removeUnusedPluginAssets: boolean;
noteHash: string;
initialScroll: number;
initialScrollPercent: number;
// Forwarded renderer settings
splitted?: boolean;

View File

@@ -12,12 +12,12 @@ import RNToWebViewMessenger from '../../utils/ipc/RNToWebViewMessenger';
import useEditPopup from './utils/useEditPopup';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import { RenderSettings } from './contentScript/Renderer';
import resolvePathWithinDir from '@joplin/lib/utils/resolvePathWithinDir';
import Resource from '@joplin/lib/models/Resource';
import { ResourceInfos } from '@joplin/renderer/types';
import useContentScripts from './utils/useContentScripts';
import uuid from '@joplin/lib/uuid';
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
import resolvePathWithinDir from '@joplin/lib/utils/resolvePathWithinDir';
const logger = Logger.create('renderer/useWebViewSetup');
@@ -91,8 +91,8 @@ const useMessenger = (props: UseMessengerProps) => {
const messenger = useMemo(() => {
const fsDriver = shim.fsDriver();
const localApi = {
onScroll: (fraction: number) => onScrollRef.current?.(fraction),
const localApi: MainProcessApi = {
onScroll: (event) => onScrollRef.current?.(event),
onPostMessage: (message: string) => onPostMessageRef.current?.(message),
onPostPluginMessage,
fsDriver: {
@@ -212,20 +212,32 @@ const useWebViewSetup = (props: Props): Result => {
settingsChanged = true;
}
},
readAssetBlob: (assetPath: string): Promise<Blob> => {
// Built-in assets are in resourceDir, external plugin assets are in cacheDir.
const assetsDirs = [Setting.value('resourceDir'), Setting.value('cacheDir')];
// Handles plugin asset loading on web (where the WebView can't load assets directly).
readAssetBlob: async (assetPath: string): Promise<Blob> => {
if (assetPath.startsWith('pluginAssets/')) { // Built-in plugin asset
assetPath = assetPath.replace(/^pluginAssets\//, '');
let resolvedPath = null;
for (const assetDir of assetsDirs) {
resolvedPath ??= resolvePathWithinDir(assetDir, assetPath);
if (resolvedPath) break;
}
const fullPath = shim.fsDriver().resolveRelativePathWithinDir(
Setting.value('pluginAssetDir'), assetPath,
);
return shim.fsDriver().fileAtPath(fullPath);
} else { // Asset from an installed/development plugin
// User-installed plugins are stored in cacheDir
const allowedBasePaths = [Setting.value('cacheDir')];
// Development plugins are stored in other locations. Add them separately:
if (Setting.value('plugins.devPluginPaths')) {
allowedBasePaths.push(...Setting.value('plugins.devPluginPaths').split(','));
}
if (!resolvedPath) {
throw new Error(`Failed to load asset at ${assetPath} -- not in any of the allowed asset directories: ${assetsDirs.join(',')}.`);
for (const basePath of allowedBasePaths) {
const resolved = resolvePathWithinDir(basePath, assetPath);
if (resolved) {
return shim.fsDriver().fileAtPath(resolved);
}
}
throw new Error(`Unable to resolve plugin asset: ${assetPath}`);
}
return shim.fsDriver().fileAtPath(resolvedPath);
},
removeUnusedPluginAssets: options.removeUnusedPluginAssets,
globalSettings: {

View File

@@ -1,42 +1,17 @@
import { _ } from '@joplin/lib/locale';
import Setting from '@joplin/lib/models/Setting';
import { themeStyle } from '@joplin/lib/theme';
import { Theme } from '@joplin/lib/themes/type';
import { useMemo } from 'react';
import { extname } from 'path';
import shim from '@joplin/lib/shim';
import { Platform } from 'react-native';
import { Ionicons as Icon } from '@react-native-vector-icons/ionicons';
export const editPopupClass = 'joplin-editPopup';
const getEditIconSrc = (theme: Theme) => {
// Use an inline edit icon on web -- getImageSourceSync isn't supported there.
if (Platform.OS === 'web') {
const svgData = `
<svg viewBox="-103 60 180 180" width="30" height="30" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<path d="m 100,19 c -11.7,0 -21.1,9.5 -21.2,21.2 0,0 42.3,0 42.3,0 0,-11.7 -9.5,-21.2 -21.2,-21.2 z M 79,43 v 143 l 21.3,26.4 21,-26.5 V 42.8 Z" style="transform: rotate(45deg)" fill=${JSON.stringify(theme.color2)}/>
</svg>
`.replace(/[ \t\n]+/, ' ');
return `data:image/svg+xml;utf8,${encodeURIComponent(svgData)}`;
}
const iconUri = Icon.getImageSourceSync('pencil', 20, theme.color2).uri;
// Copy to a location that can be read within a WebView
// (necessary on iOS)
const destPath = `${Setting.value('resourceDir')}/edit-icon${extname(iconUri)}`;
// Copy in the background -- the edit icon popover script doesn't need the
// icon immediately.
void (async () => {
// Can be '' in a testing environment.
if (iconUri) {
await shim.fsDriver().copy(iconUri, destPath);
}
})();
return destPath;
const svgData = `
<svg viewBox="-103 60 180 180" width="30" height="30" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<path d="m 100,19 c -11.7,0 -21.1,9.5 -21.2,21.2 0,0 42.3,0 42.3,0 0,-11.7 -9.5,-21.2 -21.2,-21.2 z M 79,43 v 143 l 21.3,26.4 21,-26.5 V 42.8 Z" style="transform: rotate(45deg)" fill=${JSON.stringify(theme.color2)}/>
</svg>
`.replace(/[ \t\n]+/, ' ');
return `data:image/svg+xml;utf8,${encodeURIComponent(svgData)}`;
};
// Creates JavaScript/CSS that can be used to create an "Edit" button.

View File

@@ -29,6 +29,8 @@ export const initialize = async (
settings,
initialText,
initialNoteId,
initialSelection,
initialScroll,
parentElementClassName,
initialSearch,
}: EditorProps,
@@ -41,6 +43,14 @@ export const initialize = async (
throw new Error('Parent node is not an element.');
}
document.addEventListener('scrollend', () => {
const fraction = document.scrollingElement.scrollTop / (document.scrollingElement.scrollHeight || 1);
void messenger.remoteApi.onEditorEvent({
kind: EditorEventType.Scroll,
fraction,
});
});
const assetContainer = document.createElement('div');
assetContainer.id = 'joplin-container-pluginAssetsContainer';
document.body.appendChild(assetContainer);
@@ -100,6 +110,13 @@ export const initialize = async (
});
editor.setSearchState(initialSearch, 'initialSearch');
if (initialSelection) {
editor.select(initialSelection.start, initialSelection.end);
}
if (initialScroll) {
editor.setScrollPercent(initialScroll);
}
messenger.setLocalInterface({
editor,
});

View File

@@ -3,9 +3,13 @@ import { EditorControl, EditorSettings, OnLocalize, SearchState } from '@joplin/
import { MarkupRecord, RendererControl } from '../rendererBundle/types';
import { RenderResult } from '@joplin/renderer/types';
type SelectionRange = { start: number; end: number };
export interface EditorProps {
initialText: string;
initialSearch: SearchState;
initialSelection: SelectionRange;
initialScroll: number;
initialNoteId: string;
parentElementClassName: string;
settings: EditorSettings;

View File

@@ -14,6 +14,7 @@ import { RendererControl, RenderOptions } from '../rendererBundle/types';
import { ResourceInfos } from '@joplin/renderer/types';
import { _ } from '@joplin/lib/locale';
import { defaultSearchState } from '../../components/NoteEditor/SearchPanel';
import { SelectionRange } from '../markdownEditorBundle/types';
const logger = Logger.create('useWebViewSetup');
@@ -21,6 +22,8 @@ interface Props {
initialText: string;
noteId: string;
settings: EditorSettings;
initialSelection: SelectionRange|null;
initialScroll: number|null;
parentElementClassName: string;
globalSearch: string;
themeId: number;
@@ -53,7 +56,7 @@ const useMessenger = (props: UseMessengerProps) => {
noteViewerFontSize: `${baseTheme.fontSize}${baseTheme.fontSizeUnits ?? 'px'}`,
},
noteHash: '',
initialScroll: 0,
initialScrollPercent: 0,
pluginAssetContainerSelector: null,
removeUnusedPluginAssets: true,
};
@@ -119,6 +122,8 @@ const useSource = (props: UseSourceProps) => {
...defaultSearchState,
searchText: propsRef.current.globalSearch,
},
initialScroll: propsRef.current.initialScroll,
initialSelection: propsRef.current.initialSelection,
settings: propsRef.current.settings,
};

View File

@@ -6,7 +6,7 @@ PODS:
- ReactCommon/turbomodule/core
- EXConstants (17.1.7):
- ExpoModulesCore
- Expo (53.0.20):
- Expo (53.0.23):
- DoubleConversion
- ExpoModulesCore
- glog
@@ -1408,7 +1408,7 @@ PODS:
- ReactCommon/turbomodule/core
- react-native-alarm-notification (3.5.0):
- React
- react-native-document-picker (10.1.5):
- react-native-document-picker (10.1.7):
- DoubleConversion
- glog
- hermes-engine
@@ -1550,7 +1550,7 @@ PODS:
- react-native-vector-icons-material-icons (12.4.0)
- react-native-version-info (1.1.1):
- React-Core
- react-native-webview (13.15.0):
- react-native-webview (13.16.0):
- DoubleConversion
- glog
- hermes-engine
@@ -1902,7 +1902,7 @@ PODS:
- React-Core
- RNCPushNotificationIOS (1.11.0):
- React-Core
- RNDateTimePicker (8.4.4):
- RNDateTimePicker (8.4.5):
- React-Core
- RNDeviceInfo (14.0.4):
- React-Core
@@ -2309,7 +2309,7 @@ SPEC CHECKSUMS:
DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb
EXAV: ae28256069c4cdde93d185c007d8f68d92902c2e
EXConstants: 98bcf0f22b820f9b28f9fee55ff2daededadd2f8
Expo: b527631da3b11e085809e877b845f9e6cdd68f9c
Expo: c8f323f74218c45c46e27eed40d8a53ba50667c3
ExpoAsset: ef06e880126c375f580d4923fdd1cdf4ee6ee7d6
ExpoCamera: e1879906d41184e84b57d7643119f8509414e318
ExpoFileSystem: 7f92f7be2f5c5ed40a7c9efc8fa30821181d9d63
@@ -2356,7 +2356,7 @@ SPEC CHECKSUMS:
React-Mapbuffer: c3f4b608e4a59dd2f6a416ef4d47a14400194468
React-microtasksnativemodule: 054f34e9b82f02bd40f09cebd4083828b5b2beb6
react-native-alarm-notification: a4326a743df72a94d361a4c3a21515556f650341
react-native-document-picker: d7580f6e287bbf2c31c071d6b3f252ae1c6586f1
react-native-document-picker: b6419b766863408dacbdf5e97b2f3a694c611150
react-native-geolocation: ec15ffebc53790314885eb9e5f2132132fbc2600
react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba
react-native-image-picker: 7babe45e727db306b3f00d08c72eda3586d6e9c1
@@ -2373,7 +2373,7 @@ SPEC CHECKSUMS:
react-native-vector-icons-material-design-icons: 76cd460b3540b80527b4a80fb7f867f7deedb498
react-native-vector-icons-material-icons: d67e485a05560416ff6b5977d5fa7e0eb6af6870
react-native-version-info: f0b04e16111c4016749235ff6d9a757039189141
react-native-webview: 0dceb35a9d050f5fa55f7fe2d8c4d1903651eb7d
react-native-webview: 8ad427a520a3b94d2006a62bb7756be726116af5
React-NativeModulesApple: 2c4377e139522c3d73f5df582e4f051a838ff25e
React-oscompat: ef5df1c734f19b8003e149317d041b8ce1f7d29c
React-perflogger: 9a151e0b4c933c9205fd648c246506a83f31395d
@@ -2408,7 +2408,7 @@ SPEC CHECKSUMS:
rn-fetch-blob: 25612b6d6f6e980c6f17ed98ba2f58f5696a51ca
RNCClipboard: f6679d470d0da2bce2a37b0af7b9e0bf369ecda5
RNCPushNotificationIOS: 6c4ca3388c7434e4a662b92e4dfeeee858e6f440
RNDateTimePicker: 7d93eacf4bdf56350e4b7efd5cfc47639185e10c
RNDateTimePicker: 8c12d12e8660697c2e176d2f98775764431c141f
RNDeviceInfo: d863506092aef7e7af3a1c350c913d867d795047
RNExitApp: 4432b9b7cc5ccec9f91c94e507849891282befd4
RNFileViewer: 4b5d83358214347e4ab2d4ca8d5c1c90d869e251

View File

@@ -114,10 +114,7 @@ jest.doMock('@expo/vector-icons/MaterialCommunityIcons', () => {
const mockIconLibrary = (libraryName, exportName) => {
jest.doMock(libraryName, () => {
const MockIconComponent = class extends require('react-native').View {
// Used by the renderer
static getImageSourceSync = () => ({ uri: '' });
};
const MockIconComponent = require('react-native').View;
return {
default: MockIconComponent,
[exportName]: MockIconComponent,

View File

@@ -16,7 +16,7 @@
"clean": "node tools/clean.js",
"buildInjectedJs": "gulp buildInjectedJs",
"test": "jest",
"test-ci": "yarn test",
"test-ci": "node tools/runTestsConditionally.js",
"watchInjectedJs": "gulp watchInjectedJs",
"postinstall": "jetify"
},
@@ -28,13 +28,13 @@
"@joplin/react-native-saf-x": "~3.5",
"@joplin/renderer": "~3.5",
"@joplin/utils": "~3.5",
"@js-draw/material-icons": "1.32.0",
"@js-draw/material-icons": "1.33.0",
"@react-native-clipboard/clipboard": "1.16.3",
"@react-native-community/datetimepicker": "8.4.5",
"@react-native-community/geolocation": "3.4.0",
"@react-native-community/netinfo": "11.4.1",
"@react-native-community/push-notification-ios": "1.11.0",
"@react-native-documents/picker": "10.1.6",
"@react-native-documents/picker": "10.1.7",
"@react-native-vector-icons/fontawesome5": "12.3.0",
"@react-native-vector-icons/get-image": "12.3.0",
"@react-native-vector-icons/ionicons": "12.3.0",
@@ -47,11 +47,11 @@
"crypto-browserify": "3.12.1",
"deprecated-react-native-prop-types": "5.0.0",
"events": "3.3.0",
"expo": "53.0.20",
"expo": "53.0.23",
"expo-av": "15.1.7",
"expo-camera": "16.1.11",
"expo-local-authentication": "16.0.5",
"js-draw": "1.32.0",
"js-draw": "1.33.0",
"lodash": "4.17.21",
"md5": "2.3.0",
"path-browserify": "1.0.1",
@@ -80,7 +80,7 @@
"react-native-svg": "15.13.0",
"react-native-url-polyfill": "2.0.0",
"react-native-version-info": "1.1.1",
"react-native-webview": "13.15.0",
"react-native-webview": "13.16.0",
"react-native-zip-archive": "7.0.2",
"react-redux": "8.1.3",
"redux": "4.2.1",
@@ -114,7 +114,7 @@
"@types/node": "18.19.130",
"@types/react": "19.0.14",
"@types/react-redux": "7.1.33",
"@types/serviceworker": "0.0.153",
"@types/serviceworker": "0.0.157",
"@types/tar-stream": "3.1.4",
"babel-jest": "29.7.0",
"babel-loader": "9.1.3",

View File

@@ -1,5 +1,5 @@
module.exports = {
hash:"eff3d7e44d37c3c7f09b80c7a51d078b", files: {
hash:"daebd8498ff273c64cf5905d4356e66a", files: {
'abc/abc_render.js': { data: require('./abc/abc_render.js.base64.js'), mime: 'application/javascript', encoding: 'base64' },
'abc/abcjs-basic-min.js': { data: require('./abc/abcjs-basic-min.js.base64.js'), mime: 'application/javascript', encoding: 'base64' },
'highlight.js/atom-one-dark-reasonable.css': { data: require('./highlight.js/atom-one-dark-reasonable.css.base64.js'), mime: 'text/css', encoding: 'base64' },

View File

@@ -1 +1 @@
module.exports = {"hash":"eff3d7e44d37c3c7f09b80c7a51d078b","files":["abc/abc_render.js","abc/abcjs-basic-min.js","highlight.js/atom-one-dark-reasonable.css","highlight.js/atom-one-light.css","katex/fonts/KaTeX_AMS-Regular.woff2","katex/fonts/KaTeX_Caligraphic-Bold.woff2","katex/fonts/KaTeX_Caligraphic-Regular.woff2","katex/fonts/KaTeX_Fraktur-Bold.woff2","katex/fonts/KaTeX_Fraktur-Regular.woff2","katex/fonts/KaTeX_Main-Bold.woff2","katex/fonts/KaTeX_Main-BoldItalic.woff2","katex/fonts/KaTeX_Main-Italic.woff2","katex/fonts/KaTeX_Main-Regular.woff2","katex/fonts/KaTeX_Math-BoldItalic.woff2","katex/fonts/KaTeX_Math-Italic.woff2","katex/fonts/KaTeX_SansSerif-Bold.woff2","katex/fonts/KaTeX_SansSerif-Italic.woff2","katex/fonts/KaTeX_SansSerif-Regular.woff2","katex/fonts/KaTeX_Script-Regular.woff2","katex/fonts/KaTeX_Size1-Regular.woff2","katex/fonts/KaTeX_Size2-Regular.woff2","katex/fonts/KaTeX_Size3-Regular.woff2","katex/fonts/KaTeX_Size4-Regular.woff2","katex/fonts/KaTeX_Typewriter-Regular.woff2","katex/katex.css","mermaid/mermaid.min.js","mermaid/mermaid_render.js"]}
module.exports = {"hash":"daebd8498ff273c64cf5905d4356e66a","files":["abc/abc_render.js","abc/abcjs-basic-min.js","highlight.js/atom-one-dark-reasonable.css","highlight.js/atom-one-light.css","katex/fonts/KaTeX_AMS-Regular.woff2","katex/fonts/KaTeX_Caligraphic-Bold.woff2","katex/fonts/KaTeX_Caligraphic-Regular.woff2","katex/fonts/KaTeX_Fraktur-Bold.woff2","katex/fonts/KaTeX_Fraktur-Regular.woff2","katex/fonts/KaTeX_Main-Bold.woff2","katex/fonts/KaTeX_Main-BoldItalic.woff2","katex/fonts/KaTeX_Main-Italic.woff2","katex/fonts/KaTeX_Main-Regular.woff2","katex/fonts/KaTeX_Math-BoldItalic.woff2","katex/fonts/KaTeX_Math-Italic.woff2","katex/fonts/KaTeX_SansSerif-Bold.woff2","katex/fonts/KaTeX_SansSerif-Italic.woff2","katex/fonts/KaTeX_SansSerif-Regular.woff2","katex/fonts/KaTeX_Script-Regular.woff2","katex/fonts/KaTeX_Size1-Regular.woff2","katex/fonts/KaTeX_Size2-Regular.woff2","katex/fonts/KaTeX_Size3-Regular.woff2","katex/fonts/KaTeX_Size4-Regular.woff2","katex/fonts/KaTeX_Typewriter-Regular.woff2","katex/katex.css","mermaid/mermaid.min.js","mermaid/mermaid_render.js"]}

File diff suppressed because one or more lines are too long

View File

@@ -39,15 +39,17 @@ export interface VoiceTypingProvider {
export default class VoiceTyping {
private provider: VoiceTypingProvider|null = null;
public constructor(
private locale: string,
allProviders: VoiceTypingProvider[],
) {
this.provider = allProviders.find(p => p.supported()) ?? null;
public constructor(private locale: string) {
this.provider = VoiceTyping.providers_.find(p => p.supported()) ?? null;
}
public supported() {
return this.provider !== null;
private static providers_: VoiceTypingProvider[] = [];
public static initialize(providers: VoiceTypingProvider[]) {
this.providers_ = providers;
}
public static supported() {
return this.providers_.some(p => p.supported());
}
private getModelPath() {

View File

@@ -213,7 +213,7 @@ const modelLocalFilepath = () => {
};
const whisper: VoiceTypingProvider = {
supported: () => !!SpeechToTextModule,
supported: () => !!SpeechToTextModule && Setting.value('buildFlag.voiceTypingEnabled'),
modelLocalFilepath: modelLocalFilepath,
getDownloadUrl: (locale) => {
const lang = languageCodeOnly(locale).toLowerCase();

View File

@@ -0,0 +1,13 @@
const { execSync } = require('child_process');
if (process.env.RUNNER_OS === 'macOS') {
console.info('Skipping app-mobile tests on macOS');
process.exit(0);
}
try {
execSync('yarn test', { stdio: 'inherit' });
} catch (error) {
console.error(error);
process.exit(1);
}

View File

@@ -90,6 +90,8 @@ import PerformanceLogger from '@joplin/lib/PerformanceLogger';
import { Profile } from '@joplin/lib/services/profileConfig/types';
import shim from '@joplin/lib/shim';
import { Platform } from 'react-native';
import VoiceTyping from '../services/voiceTyping/VoiceTyping';
import whisper from '../services/voiceTyping/whisper';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -362,6 +364,9 @@ const buildStartupTasks = (
addTask('buildStartupTasks/migrate PPK', async () => {
await migratePpk();
});
addTask('buildStartupTasks/set up voice typing', async () => {
VoiceTyping.initialize([whisper]);
});
addTask('buildStartupTasks/load folders', async () => {
await refreshFolders(dispatch, '');

View File

@@ -0,0 +1,14 @@
import useQueuedAsyncEffect from '@joplin/lib/hooks/useQueuedAsyncEffect';
import { useState } from 'react';
const useDebounced = <T> (value: T, interval: number) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useQueuedAsyncEffect(() => {
setDebouncedValue(value);
}, [value], { interval });
return debouncedValue;
};
export default useDebounced;

View File

@@ -2,11 +2,16 @@ import { EditorView } from 'prosemirror-view';
import { EditorCommandType } from '../../types';
import commands from './commands';
import createTestEditor from '../testing/createTestEditor';
import selectDocumentEnd from './selectDocumentEnd';
const selectAll = (editor: EditorView) => {
commands[EditorCommandType.SelectAll](editor.state, editor.dispatch, editor);
};
const moveCursorToEnd = (editor: EditorView) => {
selectDocumentEnd(editor.state, editor.dispatch, editor);
};
describe('ProseMirror/commands', () => {
test('textBold should toggle bold formatting', () => {
const editor = createTestEditor({ html: '<p>Test</p>' });
@@ -93,4 +98,50 @@ describe('ProseMirror/commands', () => {
}],
});
});
test.each([
{
label: 'should indent the selected paragraph',
before: '<p>Test</p>',
select: selectAll,
expectedDoc: [
{ type: 'paragraph', content: [{ type: 'text', text: ' Test' }] },
],
},
{
label: 'should not throw an error if the selection is at the end of the document (after the last block)',
before: '<p>Test</p><p>Test 2</p>',
select: moveCursorToEnd,
expectedDoc: [
{ type: 'paragraph', content: [{ type: 'text', text: 'Test' }] },
{ type: 'paragraph', content: [{ type: 'text', text: 'Test 2' }] },
{ type: 'paragraph', content: [{ type: 'text', text: ' ' }] },
],
},
])('indentMore should add spaces to the beginning of the selected lines ($label)', ({ before, select, expectedDoc }) => {
const editor = createTestEditor({ html: before });
select(editor);
commands[EditorCommandType.IndentMore](editor.state, editor.dispatch, editor);
expect(editor.state.doc.toJSON()).toMatchObject({
content: expectedDoc,
});
});
test('indentLess should remove spaces from the beginning of the line', () => {
const editor = createTestEditor({ html: '<p> test</p>' });
selectAll(editor);
commands[EditorCommandType.IndentLess](editor.state, editor.dispatch, editor);
expect(editor.state.doc.toJSON()).toMatchObject({
content: [{
content: [
{ type: 'text', text: 'test' },
],
type: 'paragraph',
}],
});
});
});

View File

@@ -4,7 +4,7 @@ import { redo, undo } from 'prosemirror-history';
import { autoJoin, selectAll, setBlockType, toggleMark } from 'prosemirror-commands';
import schema from '../schema';
import { liftListItem, sinkListItem, wrapRangeInList } from 'prosemirror-schema-list';
import { NodeType } from 'prosemirror-model';
import { NodeType, Slice } from 'prosemirror-model';
import { getSearchVisible, setSearchVisible } from '../plugins/searchPlugin';
import { findNext, findPrev, replaceAll, replaceNext } from 'prosemirror-search';
import { getEditorApi } from '../plugins/joplinEditorApiPlugin';
@@ -76,6 +76,43 @@ const toggleCode: Command = (state, dispatch, view) => {
return toggleMark(schema.marks.code)(state, dispatch, view) || setBlockType(schema.nodes.paragraph)(state, dispatch, view);
};
const getSelectedBlock = (state: EditorState) => {
const blockRange = state.selection.$from.blockRange(state.selection.$to);
// blockRange can be null in an empty document, or when the selection is after the last
// block (e.g. the very end of the document). Handle this:
const contentStart = blockRange ? blockRange.start + 1 : state.selection.from;
return { blockRange, contentStart };
};
const addTextAtLineStart = (text: string): Command => (state, dispatch) => {
const { contentStart } = getSelectedBlock(state);
let transaction = state.tr;
transaction = transaction.insertText(text, contentStart);
if (dispatch) dispatch(transaction);
return true;
};
const removeTextAtLineStart = (pattern: RegExp): Command => (state, dispatch) => {
const { contentStart, blockRange } = getSelectedBlock(state);
const text = state.doc.textBetween(contentStart, blockRange.end);
const match = text.match(pattern);
if (!match || match.index !== 0) return false;
const contentEnd = contentStart + match[0].length;
// Verify that the indexes are correct. This also helps verify that there aren't any
// non-text nodes (e.g checkboxes) included in the range:
const actualText = state.doc.textBetween(contentStart, contentEnd);
if (actualText) {
const transaction = state.tr.replaceRange(contentStart, contentEnd, Slice.empty);
if (dispatch) dispatch(transaction);
return true;
}
return false;
};
const listItemTypes = [schema.nodes.list_item, schema.nodes.task_list_item];
const commands: Record<EditorCommandType, ExtendedCommand|null> = {
@@ -193,10 +230,12 @@ const commands: Record<EditorCommandType, ExtendedCommand|null> = {
[EditorCommandType.DeleteToLineEnd]: null,
[EditorCommandType.DeleteToLineStart]: null,
[EditorCommandType.IndentMore]: (state, dispatch, view) => {
return listItemTypes.some(type => sinkListItem(type)(state, dispatch, view));
return listItemTypes.some(type => sinkListItem(type)(state, dispatch, view))
|| addTextAtLineStart(' ')(state, dispatch, view);
},
[EditorCommandType.IndentLess]: (state, dispatch, view) => {
return listItemTypes.some(type => liftListItem(type)(state, dispatch, view));
return removeTextAtLineStart(/\s{1,4}/)(state, dispatch, view)
|| listItemTypes.some(type => liftListItem(type)(state, dispatch, view));
},
[EditorCommandType.IndentAuto]: null,
[EditorCommandType.InsertNewlineAndIndent]: null,

View File

@@ -0,0 +1,22 @@
import { Command, TextSelection } from 'prosemirror-state';
const selectDocumentEnd: Command = (state, dispatch) => {
// nodeSize is defined to be the length of the node content plus two (one for the start
// and one for the end token). However, the main document has no start/end tokens, so subtract two.
const position = state.doc.nodeSize - 2;
const endAlreadySelected = position === state.selection.from && state.selection.to === state.selection.from;
if (endAlreadySelected) {
return false;
}
const transaction = state.tr.setSelection(TextSelection.create(state.doc, position));
if (dispatch) {
dispatch(transaction);
}
return true;
};
export default selectDocumentEnd;

View File

@@ -28,6 +28,7 @@ import { RenderResult } from '../../renderer/types';
import postprocessEditorOutput from './utils/postprocessEditorOutput';
import detailsPlugin from './plugins/detailsPlugin';
import tablePlugin from './plugins/tablePlugin';
import clampPointToDocument from './utils/clampPointToDocument';
interface ProseMirrorControl extends EditorControl {
getSettings(): EditorSettings;
@@ -134,6 +135,14 @@ const createEditor = async (
formatting: selectionFormatting,
});
}
props.onEvent({
kind: EditorEventType.SelectionRangeChange,
anchor: newState.selection.anchor,
head: newState.selection.head,
from: newState.selection.from,
to: newState.selection.to,
});
};
const view = new EditorView(parentElement, {
@@ -187,10 +196,14 @@ const createEditor = async (
redo: () => {
void editorControl.execCommand(EditorCommandType.Redo);
},
select: function(anchor: number, head: number): void {
select: (anchor: number, head: number) => {
const transaction = view.state.tr;
transaction.setSelection(
TextSelection.create(transaction.doc, anchor, head),
TextSelection.create(
transaction.doc,
clampPointToDocument(view.state, anchor),
clampPointToDocument(view.state, head),
),
);
view.dispatch(transaction);
},

View File

@@ -3,11 +3,22 @@ import { SearchState } from '../../types';
import { Plugin, EditorState, Command, Transaction } from 'prosemirror-state';
import { EditorEvent, EditorEventType } from '../../events';
type VisibleMetaState = {
visible: boolean;
changeSource: string;
};
const getVisibleMeta = (tr: Transaction): VisibleMetaState|undefined => (
tr.getMeta(visiblePlugin)
);
const setVisibleMeta = (tr: Transaction, metaState: VisibleMetaState): Transaction => (
tr.setMeta(visiblePlugin, metaState)
);
const visiblePlugin = new Plugin({
state: {
init: () => false,
apply: (tr, value) => {
const visibleMeta = tr.getMeta(visiblePlugin);
const visibleMeta = getVisibleMeta(tr);
if (visibleMeta) {
return visibleMeta.visible;
}
@@ -24,7 +35,7 @@ export const setSearchVisible = (visible: boolean): Command => (state, dispatch)
return false;
}
if (dispatch) {
dispatch(state.tr.setMeta(visiblePlugin, { visible }));
dispatch(setVisibleMeta(state.tr, { visible, changeSource: 'setSearchVisible' }));
}
return true;
};
@@ -58,7 +69,7 @@ const searchExtension = (onEditorEvent: (event: EditorEvent)=> void) => {
onEditorEvent({
kind: EditorEventType.UpdateSearchDialog,
searchState: currentState,
changeSources: [transaction.getMeta(visiblePlugin)?.changeSource ?? 'unknown'],
changeSources: [getVisibleMeta(transaction)?.changeSource ?? 'unknown'],
});
}
};
@@ -81,16 +92,13 @@ const searchExtension = (onEditorEvent: (event: EditorEvent)=> void) => {
],
updateState: (editorState: EditorState, searchState: SearchState, changeSource: string) => {
let transaction = editorState.tr;
setSearchVisible(searchState.dialogVisible)(editorState, (newTransaction) => {
transaction = newTransaction;
});
transaction = setSearchState(transaction, new SearchQuery({
search: searchState.searchText,
caseSensitive: searchState.caseSensitive,
regexp: searchState.useRegex,
replace: searchState.replaceText,
}));
transaction.setMeta(visiblePlugin, { changeSource });
setVisibleMeta(transaction, { changeSource, visible: searchState.dialogVisible });
lastState = { ...searchState };
return transaction;

View File

@@ -0,0 +1,19 @@
import { EditorState } from 'prosemirror-state';
const documentMaximumIndex = (state: EditorState) => {
// nodeSize is documented to be the size of a node's content plus two (one for the
// start marker and one for the end marker). The main document doesn't have start or
// end markers, so subtract these to get the document maximum index:
return state.doc.nodeSize - 2;
};
const clampPointToDocument = (state: EditorState, point: number) => {
if (point < 0) return 0;
const maximumIndex = documentMaximumIndex(state);
if (point > maximumIndex) return maximumIndex;
return point;
};
export default clampPointToDocument;

View File

@@ -19,7 +19,7 @@
"@testing-library/react-hooks": "8.0.1",
"@types/jest": "29.5.14",
"@types/node": "18.19.130",
"@types/react": "18.3.23",
"@types/react": "18.3.25",
"@types/react-redux": "7.1.33",
"@types/styled-components": "5.1.32",
"jest": "29.7.0",

View File

@@ -369,7 +369,7 @@ class WebDavApi {
// The "solution", an ugly one, is to send a purposely invalid string as eTag, which will bypass the If-None-Match check - Seafile
// finds out that no resource has this ID and simply sends the requested data.
// Also add a random value to make sure the eTag is unique for each call.
if (['GET', 'HEAD'].indexOf(method) < 0) headers['If-None-Match'] = `"JoplinIgnore-${Math.floor(Math.random() * 100000)}"`;
if (['GET', 'HEAD'].indexOf(method) < 0) headers['If-None-Match'] = `JoplinIgnore-${Math.floor(Math.random() * 100000)}`;
if (!headers['User-Agent']) headers['User-Agent'] = 'Joplin/1.0';
const fetchOptions = {};

View File

@@ -16,9 +16,10 @@ const customCssFilePath = (Setting: typeof SettingType, filename: string): strin
return `${Setting.value('rootProfileDir')}/${filename}`;
};
const showVoiceTypingSettings = () => (
type VoiceTypingSettingSlice = Record<'buildFlag.voiceTypingEnabled', boolean>;
const showVoiceTypingSettings = (settings: VoiceTypingSettingSlice) => (
// For now, iOS and web don't support voice typing.
shim.mobilePlatform() === 'android'
shim.mobilePlatform() === 'android' && !!settings['buildFlag.voiceTypingEnabled']
);
export enum CameraDirection {
@@ -1907,6 +1908,25 @@ const builtInMetadata = (Setting: typeof SettingType) => {
isGlobal: true,
},
// As of December 2025, the voice typing feature doesn't work well on low-resource devices.
// There have been requests to allow disabling the voice typing feature at build time. This
// feature flag allows doing so, by changing the default `value` from `true` to `false`:
'buildFlag.voiceTypingEnabled': {
value: true,
type: SettingItemType.Bool,
public: false,
appTypes: [AppType.Mobile],
label: () => 'Voice typing: Enable the voice typing feature',
},
'buildFlag.ui.disableSmallScreenIncompatibleFeatures': {
value: false,
type: SettingItemType.Bool,
public: false,
appTypes: [AppType.Mobile],
label: () => 'UI: Disable features known to be incompatible with small screens',
},
'survey.webClientEval2025.progress': {
value: SurveyProgress.NotStarted,
type: SettingItemType.Int,
@@ -1971,7 +1991,7 @@ const builtInMetadata = (Setting: typeof SettingType) => {
appTypes: [AppType.Mobile],
label: () => _('Voice typing: Glossary'),
description: () => _('A comma-separated list of words. May be used for uncommon words, to help voice typing spell them correctly.'),
show: () => showVoiceTypingSettings(),
show: showVoiceTypingSettings,
section: 'note',
},

View File

@@ -27,7 +27,7 @@
"@types/mustache": "4.2.6",
"@types/node": "18.19.130",
"@types/node-rsa": "1.1.4",
"@types/react": "18.3.23",
"@types/react": "18.3.25",
"@types/uuid": "10.0.0",
"jest": "29.7.0",
"jest-expect-message": "1.1.3",

View File

@@ -27,7 +27,7 @@
"devDependencies": {
"@types/jest": "29.5.14",
"@types/pdfjs-dist": "2.10.378",
"@types/react": "18.3.23",
"@types/react": "18.3.25",
"@types/react-dom": "18.3.7",
"@types/styled-components": "5.1.32",
"babel-jest": "29.7.0",

File diff suppressed because one or more lines are too long

View File

@@ -74,7 +74,7 @@
"@types/mustache": "4.2.6",
"@types/node": "18.19.130",
"@types/node-os-utils": "1.3.4",
"@types/nodemailer": "6.4.19",
"@types/nodemailer": "6.4.20",
"@types/yargs": "17.0.33",
"@types/zxcvbn": "4.4.5",
"gulp": "4.0.2",

View File

@@ -225,3 +225,4 @@ mdpi
xhdpi
xxhdpi
xxxhdpi
scrollend

View File

@@ -7,6 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Joplin-CLI 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: Isak Bergdahl\n"
"Language-Team: \n"
"Language: sv\n"
@@ -14,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.7\n"
"X-Generator: Poedit 3.8\n"
#: packages/app-mobile/components/screens/ConfigScreen/ConfigScreen.tsx:621
msgid "- Camera: to allow taking a picture and attaching it to a note."
@@ -301,7 +303,7 @@ msgstr ""
#: packages/editor/ProseMirror/plugins/imagePlugin.ts:148
msgid "A brief description of the image:"
msgstr ""
msgstr "En kort beskrivning av bilden:"
#: packages/lib/models/settings/builtInMetadata.ts:1970
msgid ""
@@ -418,7 +420,7 @@ msgstr "Lägg till brödtext"
#: packages/editor/ProseMirror/plugins/tablePlugin.ts:24
msgid "Add column"
msgstr ""
msgstr "Lägg till kolumn"
#: packages/app-mobile/components/buttons/FloatingActionButton.tsx:66
#: packages/app-mobile/components/ComboBox.tsx:103
@@ -435,9 +437,8 @@ msgid "Add recipient:"
msgstr "Lägg till mottagare:"
#: packages/editor/ProseMirror/plugins/tablePlugin.ts:19
#, fuzzy
msgid "Add row"
msgstr "Lägg till ny"
msgstr "Lägg till rad"
#: packages/app-mobile/components/TagEditor.tsx:281
msgid "Add tags:"
@@ -579,6 +580,9 @@ msgid ""
"offline or cannot connect to the server.\n"
"Error: %s"
msgstr ""
"Ett fel uppstod vid sändningen av svaret. Detta kan inträffa om appen är "
"offline eller inte kan ansluta till servern.\n"
"Fel: %s"
#: packages/app-desktop/ElectronAppWrapper.ts:189
msgid "An error occurred: %s"
@@ -1195,9 +1199,8 @@ msgstr "Fäll ihop anteckningsböcker"
#: packages/app-mobile/components/screens/Note/Note.tsx:1712
#: packages/app-mobile/components/screens/NoteRevisionViewer.tsx:220
#, fuzzy
msgid "Collapse title"
msgstr "Fäll in %s"
msgstr "Fäll ihop titel"
#: packages/app-desktop/gui/Sidebar/listItemComponents/ExpandIcon.tsx:23
msgid "Collapsed"
@@ -1210,9 +1213,9 @@ msgstr "Kommande larm"
#: packages/lib/models/settings/builtInMetadata.ts:1591
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
"or path to individual cert files. For example: /my/cert_dir, /other/"
"custom.pem. Note that if you make changes to the TLS settings, you must save "
"your changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
"Kommaseparerad lista över sökvägar till kataloger för att läsa certifikaten "
"från eller sökvägen till enskilda cert-filer. Till exempel: /my/cert_dir,/"
@@ -1738,9 +1741,8 @@ msgid "Delete attachment \"%s\"?"
msgstr "Ta bort bilagan \"%s\"?"
#: packages/editor/ProseMirror/plugins/tablePlugin.ts:34
#, fuzzy
msgid "Delete column"
msgstr "Ta bort rad"
msgstr "Ta bort kolumn"
#: packages/server/src/services/TaskService.ts:27
msgid "Delete expired sessions"
@@ -1781,9 +1783,8 @@ msgid "Delete profile \"%s\""
msgstr "Ta bort profilen \"%s\""
#: packages/editor/ProseMirror/plugins/tablePlugin.ts:29
#, fuzzy
msgid "Delete row"
msgstr "Ta bort anteckning"
msgstr "Ta bort rad"
#: packages/app-mobile/components/ScreenHeader/index.tsx:486
msgid "Delete selected notes"
@@ -1958,9 +1959,9 @@ msgid ""
"to-dos, while `-tnt` would display notes and to-dos."
msgstr ""
"Visar endast objekt av den specifika typen/typerna. Kan vara `n` för "
"anteckningar, `t` för att-göra eller `nt` för anteckningar och att-göra (t."
"ex. `-tt` skulle bara visa att-göra, medan `-tnt` skulle visa anteckningar "
"och att-göra."
"anteckningar, `t` för att-göra eller `nt` för anteckningar och att-göra "
"(t.ex. `-tt` skulle bara visa att-göra, medan `-tnt` skulle visa "
"anteckningar och att-göra."
#: packages/app-cli/app/command-status.js:13
msgid "Displays summary about the notes and notebooks."
@@ -2017,7 +2018,7 @@ msgstr ""
#: packages/app-mobile/components/FeedbackBanner.tsx:167
msgid "Do you find the Joplin web app useful?"
msgstr ""
msgstr "Tycker du att Joplin-webbappen är användbar?"
#: packages/lib/models/settings/builtInMetadata.ts:1980
msgid "Document scanner: Title template"
@@ -2579,9 +2580,8 @@ msgstr "Expandera alla anteckningsböcker"
#: packages/app-mobile/components/screens/Note/Note.tsx:1712
#: packages/app-mobile/components/screens/NoteRevisionViewer.tsx:220
#, fuzzy
msgid "Expand title"
msgstr "Expanderad"
msgstr "Expandera rubrik"
#: packages/app-desktop/gui/Sidebar/listItemComponents/ExpandIcon.tsx:21
msgid "Expanded"
@@ -2691,7 +2691,7 @@ msgstr "Funktionsflaggor"
#: packages/app-mobile/components/FeedbackBanner.tsx:180
msgid "Feedback"
msgstr ""
msgstr "Återkoppling"
#: packages/lib/Synchronizer.ts:207
msgid "Fetched items: %d/%d."
@@ -3377,9 +3377,8 @@ msgid "Joplin Server"
msgstr "Joplin Server"
#: packages/lib/SyncTargetJoplinServerSAML.ts:67
#, fuzzy
msgid "Joplin Server (SAML)"
msgstr "Joplin-serverwebbadress"
msgstr "Joplin Server (SAML)"
#: packages/lib/utils/joplinCloud/index.ts:454
msgid "Joplin Server Business"
@@ -3412,6 +3411,8 @@ msgid ""
"Joplin supports saving the location at which notes are saved or created. Do "
"you want to enable it? This can be changed at any time in settings."
msgstr ""
"Joplin stöder sparande av platsen där anteckningar sparas eller skapas. Vill "
"du aktivera det? Detta kan ändras när som helst i inställningarna."
#: packages/app-desktop/gui/ClipperConfigScreen.tsx:132
msgid ""
@@ -3465,7 +3466,7 @@ msgstr "Nycklar som behöver uppgradering"
#: packages/editor/ProseMirror/plugins/imagePlugin.ts:265
msgid "Label"
msgstr ""
msgstr "Etikett"
#: packages/lib/models/settings/builtInMetadata.ts:1443
msgid "Landscape"
@@ -3683,8 +3684,8 @@ msgid ""
"Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, "
"`status`, `decrypt-file`, and `target-status`."
msgstr ""
"Hanterar E2EE-konfigurationen. Kommandona är `enable`,` disable`, `decrypt`,"
"` status`, `decrypt-file` och `target-status`."
"Hanterar E2EE-konfigurationen. Kommandona är `enable`,` disable`, "
"`decrypt`,` status`, `decrypt-file` och `target-status`."
#: packages/lib/models/settings/builtInMetadata.ts:471
msgid "Manual"
@@ -3708,9 +3709,8 @@ msgid "Markdown editor"
msgstr "Markdown-redigerare"
#: packages/lib/models/settings/builtInMetadata.ts:1515
#, fuzzy
msgid "Markdown editor: Highlight active line"
msgstr "Markdown-redigerare: Rendera bilder"
msgstr "Markdown-redigerare: Markera aktiv rad"
#: packages/lib/models/settings/builtInMetadata.ts:1504
msgid "Markdown editor: Render images"
@@ -4105,7 +4105,7 @@ msgstr "Inte nu"
#: packages/app-mobile/components/FeedbackBanner.tsx:188
msgid "Not useful"
msgstr ""
msgstr "Inte användbart"
#: packages/app-desktop/gui/NoteListControls/NoteListControls.tsx:110
#: packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.tsx:71
@@ -4182,9 +4182,8 @@ msgid "Note list style"
msgstr "Anteckningsliststil"
#: packages/app-cli/app/command-unpublish.ts:41
#, fuzzy
msgid "Note not published: %s"
msgstr "Innehåll tillhandahållet av: %s"
msgstr "Anteckning inte publicerad: %s"
#: packages/app-mobile/components/screens/DocumentScanner/DocumentScanner.tsx:140
msgid "Note preview"
@@ -4450,7 +4449,7 @@ msgstr "Numrerad lista"
#: packages/app-mobile/components/SyncWizard/SyncWizard.tsx:130
msgid "Other"
msgstr ""
msgstr "Annat"
#: packages/app-desktop/gui/MenuBar.tsx:525
msgid "Other applications..."
@@ -4841,9 +4840,8 @@ msgid "Publish Note"
msgstr "Publicera anteckning"
#: packages/app-cli/app/command-publish.ts:47
#, fuzzy
msgid "Publish note \"%s\" (in notebook \"%s\")?"
msgstr "Flytta %d anteckning till anteckningsboken \"%s\"?"
msgstr "Publicera anteckningen \"%s\" (i anteckningsboken \"%s\")?"
#: packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareNoteDialog.ts:6
msgid "Publish note..."
@@ -4864,14 +4862,13 @@ msgid "Publish/unpublish"
msgstr "Publicera/avpublicera"
#: packages/app-cli/app/command-publish.ts:60
#, fuzzy
msgid "Published at URL: %s"
msgstr "Publicera anteckningar"
msgstr "Publicerad på URL: %s"
#: packages/app-cli/app/command-publish.ts:27
#: packages/app-cli/app/command-unpublish.ts:24
msgid "Publishes a note to Joplin Server or Joplin Cloud"
msgstr ""
msgstr "Publicerar en anteckning till Joplin Server eller Joplin Cloud"
#: packages/app-mobile/components/CameraView/ScannedBarcodes.tsx:79
msgid "QR Code"
@@ -4938,9 +4935,8 @@ msgid "Recipients:"
msgstr "Mottagare:"
#: packages/app-mobile/components/screens/DocumentScanner/NotePreview.tsx:162
#, fuzzy
msgid "Recognise text:"
msgstr "betonad text"
msgstr "Identifiera text:"
#: packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts:142
msgid "Recognize handwritten image"
@@ -5291,9 +5287,8 @@ msgid "Save geo-location with notes"
msgstr "Spara geografisk plats med anteckningar"
#: packages/app-mobile/components/screens/Note/Note.tsx:555
#, fuzzy
msgid "Save geolocation?"
msgstr "Spara geografisk plats med anteckningar"
msgstr "Spara geografisk plats?"
#: packages/app-mobile/components/screens/Notes/NewNoteButton.tsx:81
msgid "Scan notebook"
@@ -5401,7 +5396,7 @@ msgstr "Välj anteckningsbok"
#: packages/app-mobile/components/SyncWizard/SyncWizard.tsx:131
msgid "Select one of the other supported sync targets."
msgstr ""
msgstr "Välj ett av de andra synkroniseringsmålen som stöds."
#: packages/app-mobile/components/screens/folder.js:109
msgid "Select parent notebook"
@@ -5935,7 +5930,7 @@ msgstr ""
#: packages/app-mobile/components/SyncWizard/SyncWizard.tsx:111
msgid "Sync"
msgstr ""
msgstr "Synkronisera"
#: packages/lib/utils/joplinCloud/index.ts:170
msgid "Sync as many devices as you want"
@@ -6058,9 +6053,8 @@ msgid "Take photo"
msgstr "Ta ett foto"
#: packages/app-mobile/components/FeedbackBanner.tsx:164
#, fuzzy
msgid "Take survey"
msgstr "Rita bild"
msgstr "Fyll i enkät"
#: packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/TaskButton.tsx:73
msgid "Task \"%s\" failed with error: %s"
@@ -6092,6 +6086,8 @@ msgid ""
"Thank you for the feedback!\n"
"Do you have time to complete a short survey?"
msgstr ""
"Tack för din återkoppling!\n"
"Har du tid att fylla i en kort enkät?"
#: packages/lib/services/profileConfig/index.ts:106
msgid ""
@@ -6877,9 +6873,8 @@ msgid "Unable to share log data. Reason: %s"
msgstr "Det går inte att dela loggdata. Orsak: %s"
#: packages/app-mobile/components/screens/Note/Note.tsx:1101
#, fuzzy
msgid "Unable to share note data. Reason: %s"
msgstr "Det går inte att dela loggdata. Orsak: %s"
msgstr "Det gick inte att dela anteckningsdata. Orsak: %s"
#: packages/app-mobile/components/Checkbox.tsx:51
msgid "Unchecked"
@@ -7140,7 +7135,7 @@ msgstr ""
#: packages/app-mobile/components/FeedbackBanner.tsx:195
msgid "Useful"
msgstr ""
msgstr "Användbar"
#: packages/server/src/services/MustacheService.ts:125
msgid "User deletions"
@@ -7321,6 +7316,9 @@ msgid ""
"higher-quality on-server transcription service. Requires sync with a copy of "
"the desktop app."
msgstr ""
"När detta är aktiverat begärs att bilderna i anteckningen transkriberas med "
"en serverbaserad transkriberingstjänst av högre kvalitet. Kräver "
"synkronisering med en kopia från skrivbordsappen."
#: packages/lib/models/settings/builtInMetadata.ts:576
msgid ""

View File

@@ -61,7 +61,7 @@
"jest": "29.7.0",
"js-yaml": "4.1.1",
"rss": "1.2.2",
"sass": "1.93.0",
"sass": "1.93.2",
"sqlite3": "5.1.6",
"style-to-js": "1.1.17",
"ts-node": "10.9.2",

View File

@@ -188,6 +188,9 @@
"android-v3.5.1": true,
"ios-v13.5.1": true,
"v3.5.9": true,
"android-v3.5.3": true
"android-v3.5.3": true,
"android-v3.5.4": true,
"android-v3.5.5": true,
"android-v3.5.6": true
}
}

View File

@@ -15,7 +15,7 @@
},
"devDependencies": {
"@rollup/plugin-commonjs": "28.0.6",
"@rollup/plugin-node-resolve": "16.0.1",
"@rollup/plugin-node-resolve": "16.0.2",
"@rollup/plugin-replace": "6.0.2",
"browserify": "14.5.0",
"rollup": "4.2.0",

View File

@@ -1,5 +1,29 @@
# Joplin Android Changelog
## [android-v3.5.6](https://github.com/laurent22/joplin/releases/tag/android-v3.5.6) (Pre-release) - 2025-12-27T20:34:44Z
- Revert "All: Apache Tomcat WebDAV compatibility for sync (#13614)"
## [android-v3.5.5](https://github.com/laurent22/joplin/releases/tag/android-v3.5.5) (Pre-release) - 2025-12-26T10:53:20Z
- Improved: Update js-draw to v1.33.0 (#13990 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Updated packages react-native-webview (v13.16.0)
- Fixed: Editor: Fix search/replace UI is partially off-screen on small-screen devices (#13978 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Feature flags: Fix "voice typing" feature flag (#13981 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
## [android-v3.5.4](https://github.com/laurent22/joplin/releases/tag/android-v3.5.4) (Pre-release) - 2025-12-23T20:00:18Z
- Improved: Accessibility: Dark mode: Improve contrast of conflicts notebook title, error messages in "Logs" (#13925 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Attempt to fix application hang when opening the camera (#13974 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Rich Text Editor: Set the default math/code block content to the selection (#13952 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Updated packages @react-native-community/datetimepicker (v8.4.5), @react-native-documents/picker (v10.1.7), esbuild (v0.25.10), expo (v53.0.23), fs-extra (v11.3.2), mermaid (v11.10.1), sass (v1.93.2), sharp (v0.34.4)
- Improved: Viewer, Rich Text Editor: Save/restore the cursor and scroll position when switching notes (#13962) (#520 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix `react-native-vector-icons` error when opening a note (#13975 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix some icons are invisible: Upgrade `react-native-vector-icons` to v12 (#13905) (#13854 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Rich Text Editor: Fix checklists saved with extra space (#13951) (#13081 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Rich Text Editor: Fix indent/de-indent buttons do nothing when not in a list (#13961 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Toolbar editor: Fix toolbar editor dismiss button is rendered outside the dialog on small screens (#13976 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
## [android-v3.5.3](https://github.com/laurent22/joplin/releases/tag/android-v3.5.3) (Pre-release) - 2025-12-14T13:46:02Z
- New: Add a link to the list of open-source licenses (5caec16)

View File

@@ -1,231 +1,231 @@
---
updated: 2025-12-01T02:03:38Z
updated: 2026-01-01T02:02:32Z
---
# Joplin statistics
| Name | Value |
| ----- | ----- |
| Total Windows downloads | 7,382,195 |
| Total macOs downloads | 2,066,495 |
| Total Linux downloads | 1,673,738 |
| Total Windows downloads | 7,465,188 |
| Total macOs downloads | 2,073,797 |
| Total Linux downloads | 1,698,890 |
| Windows % | 66% |
| macOS % | 19% |
| macOS % | 18% |
| Linux % | 15% |
(p) Indicates pre-releases
| Version | Date | Windows | macOS | Linux | Total |
| ----- | ----- | ----- | ----- | ----- | ----- |
| [v3.5.9](https://github.com/laurent22/joplin/releases/tag/v3.5.9) (p) | 2025-11-30T19:11:11Z | 108 | 11 | 27 | 146 |
| [v3.5.7](https://github.com/laurent22/joplin/releases/tag/v3.5.7) (p) | 2025-11-22T08:35:36Z | 1,754 | 295 | 431 | 2,480 |
| [v3.5.6](https://github.com/laurent22/joplin/releases/tag/v3.5.6) (p) | 2025-10-29T14:48:46Z | 4,208 | 643 | 1,418 | 6,269 |
| [v3.5.5](https://github.com/laurent22/joplin/releases/tag/v3.5.5) (p) | 2025-10-18T10:31:09Z | 2,409 | 363 | 572 | 3,344 |
| [v3.5.4](https://github.com/laurent22/joplin/releases/tag/v3.5.4) (p) | 2025-10-10T17:19:58Z | 2,484 | 253 | 324 | 3,061 |
| [v3.4.12](https://github.com/laurent22/joplin/releases/tag/v3.4.12) | 2025-09-09T21:35:47Z | 275,109 | 25,465 | 67,955 | 368,529 |
| [v3.4.10](https://github.com/laurent22/joplin/releases/tag/v3.4.10) | 2025-09-01T12:25:22Z | 76,956 | 7,793 | 10,330 | 95,079 |
| [v3.4.7](https://github.com/laurent22/joplin/releases/tag/v3.4.7) (p) | 2025-08-23T10:49:54Z | 7,966 | 383 | 593 | 8,942 |
| [v3.4.6](https://github.com/laurent22/joplin/releases/tag/v3.4.6) (p) | 2025-08-20T20:30:35Z | 42,588 | 5,163 | 2,635 | 50,386 |
| [v3.4.5](https://github.com/laurent22/joplin/releases/tag/v3.4.5) (p) | 2025-08-10T12:49:30Z | 1,784 | 396 | 446 | 2,626 |
| [v3.4.4](https://github.com/laurent22/joplin/releases/tag/v3.4.4) (p) | 2025-08-13T16:46:39Z | 112 | 60 | 41 | 213 |
| [v3.4.3](https://github.com/laurent22/joplin/releases/tag/v3.4.3) (p) | 2025-07-25T19:49:44Z | 2,004 | 505 | 567 | 3,076 |
| [v3.4.2](https://github.com/laurent22/joplin/releases/tag/v3.4.2) (p) | 2025-07-24T10:43:55Z | 644 | 161 | 129 | 934 |
| [v3.3.13](https://github.com/laurent22/joplin/releases/tag/v3.3.13) | 2025-06-09T20:13:30Z | 264,706 | 27,152 | 60,416 | 352,274 |
| [v3.4.1](https://github.com/laurent22/joplin/releases/tag/v3.4.1) (p) | 2025-05-20T09:59:39Z | 3,966 | 1,163 | 1,332 | 6,461 |
| [v3.3.12](https://github.com/laurent22/joplin/releases/tag/v3.3.12) | 2025-05-04T18:12:23Z | 163,291 | 21,584 | 28,234 | 213,109 |
| [v3.3.10](https://github.com/laurent22/joplin/releases/tag/v3.3.10) | 2025-05-02T19:46:15Z | 30,287 | 5,163 | 1,844 | 37,294 |
| [v3.3.9](https://github.com/laurent22/joplin/releases/tag/v3.3.9) | 2025-05-01T21:02:12Z | 29,117 | 6,155 | 1,203 | 36,475 |
| [v3.3.7](https://github.com/laurent22/joplin/releases/tag/v3.3.7) (p) | 2025-04-29T13:47:19Z | 770 | 0 | 181 | 951 |
| [v3.3.6](https://github.com/laurent22/joplin/releases/tag/v3.3.6) (p) | 2025-04-24T12:27:20Z | 1,071 | 303 | 261 | 1,635 |
| [v3.3.5](https://github.com/laurent22/joplin/releases/tag/v3.3.5) (p) | 2025-04-17T13:40:31Z | 1,401 | 319 | 316 | 2,036 |
| [v3.5.9](https://github.com/laurent22/joplin/releases/tag/v3.5.9) (p) | 2025-11-30T19:11:11Z | 4,213 | 765 | 1,266 | 6,244 |
| [v3.5.7](https://github.com/laurent22/joplin/releases/tag/v3.5.7) (p) | 2025-11-22T08:35:36Z | 1,816 | 317 | 1,078 | 3,211 |
| [v3.5.6](https://github.com/laurent22/joplin/releases/tag/v3.5.6) (p) | 2025-10-29T14:48:46Z | 4,224 | 645 | 1,451 | 6,320 |
| [v3.5.5](https://github.com/laurent22/joplin/releases/tag/v3.5.5) (p) | 2025-10-18T10:31:09Z | 2,426 | 367 | 582 | 3,375 |
| [v3.5.4](https://github.com/laurent22/joplin/releases/tag/v3.5.4) (p) | 2025-10-10T17:19:58Z | 2,492 | 258 | 331 | 3,081 |
| [v3.4.12](https://github.com/laurent22/joplin/releases/tag/v3.4.12) | 2025-09-09T21:35:47Z | 339,960 | 31,383 | 90,374 | 461,717 |
| [v3.4.10](https://github.com/laurent22/joplin/releases/tag/v3.4.10) | 2025-09-01T12:25:22Z | 77,250 | 7,829 | 10,369 | 95,448 |
| [v3.4.7](https://github.com/laurent22/joplin/releases/tag/v3.4.7) (p) | 2025-08-23T10:49:54Z | 8,025 | 385 | 600 | 9,010 |
| [v3.4.6](https://github.com/laurent22/joplin/releases/tag/v3.4.6) (p) | 2025-08-20T20:30:35Z | 42,759 | 5,173 | 2,643 | 50,575 |
| [v3.4.5](https://github.com/laurent22/joplin/releases/tag/v3.4.5) (p) | 2025-08-10T12:49:30Z | 1,786 | 399 | 449 | 2,634 |
| [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,004 | 505 | 569 | 3,078 |
| [v3.4.2](https://github.com/laurent22/joplin/releases/tag/v3.4.2) (p) | 2025-07-24T10:43:55Z | 644 | 161 | 130 | 935 |
| [v3.3.13](https://github.com/laurent22/joplin/releases/tag/v3.3.13) | 2025-06-09T20:13:30Z | 265,307 | 27,199 | 60,703 | 353,209 |
| [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 | 164,423 | 21,586 | 28,239 | 214,248 |
| [v3.3.10](https://github.com/laurent22/joplin/releases/tag/v3.3.10) | 2025-05-02T19:46:15Z | 31,260 | 5,164 | 1,846 | 38,270 |
| [v3.3.9](https://github.com/laurent22/joplin/releases/tag/v3.3.9) | 2025-05-01T21:02:12Z | 30,100 | 6,155 | 1,203 | 37,458 |
| [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.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 | 989 | 4,550 |
| [v3.2.13](https://github.com/laurent22/joplin/releases/tag/v3.2.13) | 2025-02-28T14:38:21Z | 224,241 | 32,996 | 44,662 | 301,899 |
| [v3.3.3](https://github.com/laurent22/joplin/releases/tag/v3.3.3) (p) | 2025-03-16T11:52:33Z | 2,717 | 844 | 990 | 4,551 |
| [v3.2.13](https://github.com/laurent22/joplin/releases/tag/v3.2.13) | 2025-02-28T14:38:21Z | 224,510 | 33,017 | 44,677 | 302,204 |
| [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 | 870 | 215 | 186 | 1,271 |
| [v3.2.12](https://github.com/laurent22/joplin/releases/tag/v3.2.12) | 2025-01-23T23:52:04Z | 153,409 | 25,356 | 27,991 | 206,756 |
| [v3.2.11](https://github.com/laurent22/joplin/releases/tag/v3.2.11) | 2025-01-13T17:48:21Z | 67,340 | 14,878 | 6,899 | 89,117 |
| [v3.2.10](https://github.com/laurent22/joplin/releases/tag/v3.2.10) (p) | 2025-01-10T10:17:28Z | 2,849 | 195 | 203 | 3,247 |
| [v3.2.9](https://github.com/laurent22/joplin/releases/tag/v3.2.9) (p) | 2025-01-09T22:58:42Z | 393 | 120 | 70 | 583 |
| [v3.2.7](https://github.com/laurent22/joplin/releases/tag/v3.2.7) (p) | 2025-01-06T16:35:41Z | 909 | 185 | 905 | 1,999 |
| [v3.2.6](https://github.com/laurent22/joplin/releases/tag/v3.2.6) (p) | 2024-12-23T21:54:40Z | 1,782 | 364 | 494 | 2,640 |
| [v3.2.12](https://github.com/laurent22/joplin/releases/tag/v3.2.12) | 2025-01-23T23:52:04Z | 153,563 | 25,384 | 28,029 | 206,976 |
| [v3.2.11](https://github.com/laurent22/joplin/releases/tag/v3.2.11) | 2025-01-13T17:48:21Z | 67,519 | 14,881 | 6,902 | 89,302 |
| [v3.2.10](https://github.com/laurent22/joplin/releases/tag/v3.2.10) (p) | 2025-01-10T10:17:28Z | 2,926 | 195 | 205 | 3,326 |
| [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 | 906 | 2,000 |
| [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.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 | 252 | 1,476 |
| [v3.2.3](https://github.com/laurent22/joplin/releases/tag/v3.2.3) (p) | 2024-11-18T00:09:05Z | 2,735 | 585 | 909 | 4,229 |
| [v3.2.1](https://github.com/laurent22/joplin/releases/tag/v3.2.1) (p) | 2024-11-10T16:16:27Z | 1,284 | 267 | 364 | 1,915 |
| [v3.1.24](https://github.com/laurent22/joplin/releases/tag/v3.1.24) | 2024-11-09T15:08:29Z | 209,733 | 33,551 | 43,891 | 287,175 |
| [v3.1.23](https://github.com/laurent22/joplin/releases/tag/v3.1.23) | 2024-11-07T10:56:45Z | 28,399 | 6,765 | 1,546 | 36,710 |
| [v3.1.22](https://github.com/laurent22/joplin/releases/tag/v3.1.22) | 2024-11-05T08:59:32Z | 30,284 | 8,732 | 1,240 | 40,256 |
| [v3.1.20](https://github.com/laurent22/joplin/releases/tag/v3.1.20) | 2024-10-22T12:21:32Z | 96,576 | 19,412 | 13,947 | 129,935 |
| [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,735 | 585 | 912 | 4,232 |
| [v3.2.1](https://github.com/laurent22/joplin/releases/tag/v3.2.1) (p) | 2024-11-10T16:16:27Z | 1,285 | 267 | 365 | 1,917 |
| [v3.1.24](https://github.com/laurent22/joplin/releases/tag/v3.1.24) | 2024-11-09T15:08:29Z | 209,928 | 33,564 | 43,916 | 287,408 |
| [v3.1.23](https://github.com/laurent22/joplin/releases/tag/v3.1.23) | 2024-11-07T10:56:45Z | 28,517 | 6,766 | 1,561 | 36,844 |
| [v3.1.22](https://github.com/laurent22/joplin/releases/tag/v3.1.22) | 2024-11-05T08:59:32Z | 30,426 | 8,732 | 1,240 | 40,398 |
| [v3.1.20](https://github.com/laurent22/joplin/releases/tag/v3.1.20) | 2024-10-22T12:21:32Z | 96,787 | 19,412 | 13,949 | 130,148 |
| [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 | 511 | 1,982 |
| [v3.1.8](https://github.com/laurent22/joplin/releases/tag/v3.1.8) (p) | 2024-09-08T20:32:44Z | 1,256 | 288 | 350 | 1,894 |
| [v3.1.15](https://github.com/laurent22/joplin/releases/tag/v3.1.15) (p) | 2024-09-17T09:15:10Z | 1,216 | 255 | 512 | 1,983 |
| [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,002 | 37,860 | 45,189 | 288,051 |
| [v3.1.3](https://github.com/laurent22/joplin/releases/tag/v3.1.3) (p) | 2024-08-17T13:08:21Z | 1,270 | 315 | 502 | 2,087 |
| [v3.1.2](https://github.com/laurent22/joplin/releases/tag/v3.1.2) (p) | 2024-08-16T09:00:59Z | 481 | 145 | 105 | 731 |
| [v3.1.1](https://github.com/laurent22/joplin/releases/tag/v3.1.1) (p) | 2024-08-10T11:36:02Z | 1,116 | 229 | 281 | 1,626 |
| [v2.14.23](https://github.com/laurent22/joplin/releases/tag/v2.14.23) | 2024-08-07T11:15:25Z | 10,874 | 2,757 | 653 | 14,284 |
| [v3.0.14](https://github.com/laurent22/joplin/releases/tag/v3.0.14) | 2024-07-28T13:55:50Z | 90,951 | 18,596 | 18,852 | 128,399 |
| [v3.0.12](https://github.com/laurent22/joplin/releases/tag/v3.0.12) | 2024-07-02T17:11:14Z | 45,927 | 12,879 | 7,378 | 66,184 |
| [v3.0.15](https://github.com/laurent22/joplin/releases/tag/v3.0.15) | 2024-08-21T09:19:58Z | 205,225 | 37,882 | 45,218 | 288,325 |
| [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 | 483 | 147 | 106 | 736 |
| [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,878 | 2,781 | 654 | 14,313 |
| [v3.0.14](https://github.com/laurent22/joplin/releases/tag/v3.0.14) | 2024-07-28T13:55:50Z | 91,107 | 18,598 | 18,873 | 128,578 |
| [v3.0.12](https://github.com/laurent22/joplin/releases/tag/v3.0.12) | 2024-07-02T17:11:14Z | 46,087 | 12,892 | 7,395 | 66,374 |
| [v3.0.11](https://github.com/laurent22/joplin/releases/tag/v3.0.11) (p) | 2024-06-29T10:20:02Z | 875 | 180 | 283 | 1,338 |
| [v3.0.10](https://github.com/laurent22/joplin/releases/tag/v3.0.10) (p) | 2024-06-19T15:24:07Z | 1,651 | 307 | 575 | 2,533 |
| [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.9](https://github.com/laurent22/joplin/releases/tag/v3.0.9) (p) | 2024-06-12T19:07:50Z | 1,303 | 276 | 394 | 1,973 |
| [v3.0.8](https://github.com/laurent22/joplin/releases/tag/v3.0.8) (p) | 2024-05-22T14:20:45Z | 2,696 | 0 | 953 | 3,649 |
| [v2.14.22](https://github.com/laurent22/joplin/releases/tag/v2.14.22) | 2024-05-22T19:19:02Z | 144,319 | 30,908 | 25,547 | 200,774 |
| [v3.0.8](https://github.com/laurent22/joplin/releases/tag/v3.0.8) (p) | 2024-05-22T14:20:45Z | 2,696 | 0 | 954 | 3,650 |
| [v2.14.22](https://github.com/laurent22/joplin/releases/tag/v2.14.22) | 2024-05-22T19:19:02Z | 144,451 | 30,928 | 25,554 | 200,933 |
| [v3.0.6](https://github.com/laurent22/joplin/releases/tag/v3.0.6) (p) | 2024-04-27T13:16:04Z | 3,040 | 704 | 878 | 4,622 |
| [v3.0.3](https://github.com/laurent22/joplin/releases/tag/v3.0.3) (p) | 2024-04-18T15:41:38Z | 1,518 | 337 | 347 | 2,202 |
| [v3.0.2](https://github.com/laurent22/joplin/releases/tag/v3.0.2) (p) | 2024-03-21T18:18:49Z | 2,908 | 760 | 1,125 | 4,793 |
| [v2.14.20](https://github.com/laurent22/joplin/releases/tag/v2.14.20) | 2024-03-18T17:05:17Z | 194,535 | 39,596 | 38,355 | 272,486 |
| [v2.14.19](https://github.com/laurent22/joplin/releases/tag/v2.14.19) | 2024-03-08T10:45:16Z | 65,913 | 18,494 | 8,576 | 92,983 |
| [v2.14.17](https://github.com/laurent22/joplin/releases/tag/v2.14.17) | 2024-03-01T18:10:26Z | 64,425 | 18,659 | 7,643 | 90,727 |
| [v3.0.2](https://github.com/laurent22/joplin/releases/tag/v3.0.2) (p) | 2024-03-21T18:18:49Z | 2,909 | 761 | 1,125 | 4,795 |
| [v2.14.20](https://github.com/laurent22/joplin/releases/tag/v2.14.20) | 2024-03-18T17:05:17Z | 194,689 | 39,624 | 38,386 | 272,699 |
| [v2.14.19](https://github.com/laurent22/joplin/releases/tag/v2.14.19) | 2024-03-08T10:45:16Z | 65,998 | 18,495 | 8,580 | 93,073 |
| [v2.14.17](https://github.com/laurent22/joplin/releases/tag/v2.14.17) | 2024-03-01T18:10:26Z | 64,480 | 18,659 | 7,644 | 90,783 |
| [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 |
| [v2.14.14](https://github.com/laurent22/joplin/releases/tag/v2.14.14) (p) | 2024-02-10T16:03:08Z | 1,305 | 259 | 398 | 1,962 |
| [v2.14.13](https://github.com/laurent22/joplin/releases/tag/v2.14.13) (p) | 2024-02-09T16:31:54Z | 455 | 135 | 104 | 694 |
| [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,013 | 233 | 266 | 1,512 |
| [v2.14.11](https://github.com/laurent22/joplin/releases/tag/v2.14.11) (p) | 2024-01-26T11:53:05Z | 1,306 | 274 | 491 | 2,071 |
| [v2.14.11](https://github.com/laurent22/joplin/releases/tag/v2.14.11) (p) | 2024-01-26T11:53:05Z | 1,307 | 275 | 492 | 2,074 |
| [v2.14.10](https://github.com/laurent22/joplin/releases/tag/v2.14.10) (p) | 2024-01-18T22:45:04Z | 2,130 | 286 | 389 | 2,805 |
| [v2.13.15](https://github.com/laurent22/joplin/releases/tag/v2.13.15) | 2024-01-15T13:01:19Z | 150,994 | 34,188 | 29,289 | 214,471 |
| [v2.13.14](https://github.com/laurent22/joplin/releases/tag/v2.13.14) | 2024-01-13T19:11:04Z | 20,780 | 7,846 | 2,383 | 31,009 |
| [v2.13.15](https://github.com/laurent22/joplin/releases/tag/v2.13.15) | 2024-01-15T13:01:19Z | 151,102 | 34,188 | 29,294 | 214,584 |
| [v2.13.14](https://github.com/laurent22/joplin/releases/tag/v2.13.14) | 2024-01-13T19:11:04Z | 20,829 | 7,846 | 2,383 | 31,058 |
| [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 | 790 | 253 | 194 | 1,237 |
| [v2.14.7](https://github.com/laurent22/joplin/releases/tag/v2.14.7) (p) | 2024-01-08T11:51:49Z | 653 | 144 | 191 | 988 |
| [v2.14.8](https://github.com/laurent22/joplin/releases/tag/v2.14.8) (p) | 2024-01-09T22:57:07Z | 791 | 253 | 194 | 1,238 |
| [v2.14.7](https://github.com/laurent22/joplin/releases/tag/v2.14.7) (p) | 2024-01-08T11:51:49Z | 653 | 144 | 193 | 990 |
| [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 | 54,974 | 15,954 | 6,368 | 77,296 |
| [v2.13.12](https://github.com/laurent22/joplin/releases/tag/v2.13.12) | 2023-12-31T16:08:02Z | 46,142 | 14,316 | 5,108 | 65,566 |
| [v2.13.11](https://github.com/laurent22/joplin/releases/tag/v2.13.11) | 2023-12-24T12:58:53Z | 46,932 | 13,282 | 6,017 | 66,231 |
| [v2.13.10](https://github.com/laurent22/joplin/releases/tag/v2.13.10) | 2023-12-22T10:11:08Z | 20,330 | 8,727 | 1,561 | 30,618 |
| [v2.13.9](https://github.com/laurent22/joplin/releases/tag/v2.13.9) | 2023-12-09T17:18:58Z | 69,064 | 21,942 | 8,643 | 99,649 |
| [v2.13.8](https://github.com/laurent22/joplin/releases/tag/v2.13.8) | 2023-12-03T12:07:08Z | 51,552 | 17,922 | 5,236 | 74,710 |
| [v2.13.13](https://github.com/laurent22/joplin/releases/tag/v2.13.13) | 2024-01-06T13:33:11Z | 55,042 | 15,954 | 6,368 | 77,364 |
| [v2.13.12](https://github.com/laurent22/joplin/releases/tag/v2.13.12) | 2023-12-31T16:08:02Z | 46,194 | 14,316 | 5,109 | 65,619 |
| [v2.13.11](https://github.com/laurent22/joplin/releases/tag/v2.13.11) | 2023-12-24T12:58:53Z | 46,983 | 13,282 | 6,019 | 66,284 |
| [v2.13.10](https://github.com/laurent22/joplin/releases/tag/v2.13.10) | 2023-12-22T10:11:08Z | 20,385 | 8,747 | 1,581 | 30,713 |
| [v2.13.9](https://github.com/laurent22/joplin/releases/tag/v2.13.9) | 2023-12-09T17:18:58Z | 69,164 | 21,942 | 8,648 | 99,754 |
| [v2.13.8](https://github.com/laurent22/joplin/releases/tag/v2.13.8) | 2023-12-03T12:07:08Z | 51,606 | 17,922 | 5,236 | 74,764 |
| [v2.13.6](https://github.com/laurent22/joplin/releases/tag/v2.13.6) (p) | 2023-11-17T19:24:03Z | 2,192 | 463 | 596 | 3,251 |
| [v2.13.5](https://github.com/laurent22/joplin/releases/tag/v2.13.5) (p) | 2023-11-09T20:24:09Z | 1,503 | 356 | 458 | 2,317 |
| [v2.13.4](https://github.com/laurent22/joplin/releases/tag/v2.13.4) (p) | 2023-10-31T00:01:00Z | 1,581 | 388 | 504 | 2,473 |
| [v2.13.5](https://github.com/laurent22/joplin/releases/tag/v2.13.5) (p) | 2023-11-09T20:24:09Z | 1,504 | 356 | 458 | 2,318 |
| [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.3](https://github.com/laurent22/joplin/releases/tag/v2.13.3) (p) | 2023-10-24T09:25:33Z | 1,315 | 285 | 304 | 1,904 |
| [v2.12.19](https://github.com/laurent22/joplin/releases/tag/v2.12.19) | 2023-10-21T09:39:18Z | 168,128 | 43,672 | 27,895 | 239,695 |
| [v2.13.2](https://github.com/laurent22/joplin/releases/tag/v2.13.2) (p) | 2023-10-06T17:00:07Z | 2,050 | 505 | 706 | 3,261 |
| [v2.12.18](https://github.com/laurent22/joplin/releases/tag/v2.12.18) | 2023-09-22T14:37:24Z | 110,230 | 36,530 | 18,726 | 165,486 |
| [v2.12.17](https://github.com/laurent22/joplin/releases/tag/v2.12.17) | 2023-09-14T21:54:52Z | 48,168 | 21,042 | 6,651 | 75,861 |
| [v2.12.19](https://github.com/laurent22/joplin/releases/tag/v2.12.19) | 2023-10-21T09:39:18Z | 168,213 | 43,676 | 27,903 | 239,792 |
| [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,324 | 36,533 | 18,731 | 165,588 |
| [v2.12.17](https://github.com/laurent22/joplin/releases/tag/v2.12.17) | 2023-09-14T21:54:52Z | 48,176 | 21,044 | 6,654 | 75,874 |
| [v2.13.1](https://github.com/laurent22/joplin/releases/tag/v2.13.1) (p) | 2023-09-13T09:31:50Z | 1,396 | 430 | 690 | 2,516 |
| [v2.12.16](https://github.com/laurent22/joplin/releases/tag/v2.12.16) | 2023-09-11T22:33:37Z | 28,826 | 14,676 | 2,461 | 45,963 |
| [v2.12.15](https://github.com/laurent22/joplin/releases/tag/v2.12.15) | 2023-08-27T11:35:39Z | 65,621 | 28,157 | 8,478 | 102,256 |
| [v2.12.12](https://github.com/laurent22/joplin/releases/tag/v2.12.12) (p) | 2023-08-19T22:44:56Z | 3,343 | 399 | 431 | 4,173 |
| [v2.12.10](https://github.com/laurent22/joplin/releases/tag/v2.12.10) (p) | 2023-07-30T18:25:58Z | 7,981 | 3,821 | 918 | 12,720 |
| [v2.12.9](https://github.com/laurent22/joplin/releases/tag/v2.12.9) (p) | 2023-07-25T16:06:08Z | 2,791 | 375 | 324 | 3,490 |
| [v2.12.7](https://github.com/laurent22/joplin/releases/tag/v2.12.7) (p) | 2023-07-13T12:55:31Z | 2,204 | 669 | 595 | 3,468 |
| [v2.12.5](https://github.com/laurent22/joplin/releases/tag/v2.12.5) (p) | 2023-07-12T15:03:46Z | 2,109 | 172 | 164 | 2,445 |
| [v2.12.4](https://github.com/laurent22/joplin/releases/tag/v2.12.4) (p) | 2023-07-07T22:36:53Z | 2,341 | 1,669 | 225 | 4,235 |
| [v2.12.3](https://github.com/laurent22/joplin/releases/tag/v2.12.3) (p) | 2023-07-07T10:16:55Z | 423 | 210 | 102 | 735 |
| [v2.11.11](https://github.com/laurent22/joplin/releases/tag/v2.11.11) | 2023-06-23T15:16:37Z | 190,601 | 67,256 | 38,922 | 296,779 |
| [v2.11.9](https://github.com/laurent22/joplin/releases/tag/v2.11.9) (p) | 2023-06-06T16:23:27Z | 2,318 | 581 | 752 | 3,651 |
| [v2.11.6](https://github.com/laurent22/joplin/releases/tag/v2.11.6) (p) | 2023-05-31T20:13:08Z | 1,181 | 441 | 350 | 1,972 |
| [v2.11.5](https://github.com/laurent22/joplin/releases/tag/v2.11.5) (p) | 2023-05-28T00:41:40Z | 1,043 | 314 | 289 | 1,646 |
| [v2.10.19](https://github.com/laurent22/joplin/releases/tag/v2.10.19) | 2023-05-17T12:25:41Z | 125,668 | 48,351 | 22,497 | 196,516 |
| [v2.11.4](https://github.com/laurent22/joplin/releases/tag/v2.11.4) (p) | 2023-05-16T10:02:21Z | 1,094 | 475 | 422 | 1,991 |
| [v2.11.3](https://github.com/laurent22/joplin/releases/tag/v2.11.3) (p) | 2023-05-16T09:09:57Z | 155 | 47 | 45 | 247 |
| [v2.10.18](https://github.com/laurent22/joplin/releases/tag/v2.10.18) | 2023-05-09T13:27:43Z | 57,364 | 24,267 | 6,814 | 88,445 |
| [v2.10.17](https://github.com/laurent22/joplin/releases/tag/v2.10.17) | 2023-05-08T17:27:28Z | 19,753 | 11,516 | 898 | 32,167 |
| [v2.10.16](https://github.com/laurent22/joplin/releases/tag/v2.10.16) | 2023-04-27T09:27:45Z | 10,323 | 4,263 | 789 | 15,375 |
| [v2.10.15](https://github.com/laurent22/joplin/releases/tag/v2.10.15) (p) | 2023-04-26T22:02:16Z | 388 | 147 | 64 | 599 |
| [v2.10.13](https://github.com/laurent22/joplin/releases/tag/v2.10.13) (p) | 2023-04-03T16:53:46Z | 5,107 | 831 | 1,081 | 7,019 |
| [v2.10.12](https://github.com/laurent22/joplin/releases/tag/v2.10.12) (p) | 2023-03-23T12:17:13Z | 4,077 | 521 | 607 | 5,205 |
| [v2.10.11](https://github.com/laurent22/joplin/releases/tag/v2.10.11) (p) | 2023-03-17T10:54:02Z | 3,445 | 386 | 407 | 4,238 |
| [v2.10.10](https://github.com/laurent22/joplin/releases/tag/v2.10.10) (p) | 2023-03-13T23:16:37Z | 2,943 | 286 | 257 | 3,486 |
| [v2.10.9](https://github.com/laurent22/joplin/releases/tag/v2.10.9) (p) | 2023-03-12T16:16:45Z | 2,483 | 216 | 299 | 2,998 |
| [v2.10.8](https://github.com/laurent22/joplin/releases/tag/v2.10.8) (p) | 2023-02-26T12:53:55Z | 4,963 | 574 | 874 | 6,411 |
| [v2.10.7](https://github.com/laurent22/joplin/releases/tag/v2.10.7) (p) | 2023-02-24T10:56:20Z | 2,640 | 193 | 282 | 3,115 |
| [v2.10.6](https://github.com/laurent22/joplin/releases/tag/v2.10.6) (p) | 2023-02-20T14:00:05Z | 3,418 | 344 | 292 | 4,054 |
| [v2.10.5](https://github.com/laurent22/joplin/releases/tag/v2.10.5) | 2023-01-16T15:00:53Z | 377 | 107 | 327 | 811 |
| [v2.10.4](https://github.com/laurent22/joplin/releases/tag/v2.10.4) (p) | 2023-01-05T13:09:20Z | 8,641 | 1,306 | 1,815 | 11,762 |
| [v2.10.3](https://github.com/laurent22/joplin/releases/tag/v2.10.3) (p) | 2022-12-31T15:53:23Z | 3,204 | 316 | 420 | 3,940 |
| [v2.10.2](https://github.com/laurent22/joplin/releases/tag/v2.10.2) (p) | 2022-12-18T18:05:08Z | 4,642 | 593 | 642 | 5,877 |
| [v2.9.17](https://github.com/laurent22/joplin/releases/tag/v2.9.17) | 2022-11-15T10:28:37Z | 336,477 | 108,809 | 83,403 | 528,689 |
| [v2.9.12](https://github.com/laurent22/joplin/releases/tag/v2.9.12) (p) | 2022-11-01T17:06:05Z | 11,832 | 614 | 548 | 12,994 |
| [v2.9.11](https://github.com/laurent22/joplin/releases/tag/v2.9.11) (p) | 2022-10-23T16:09:58Z | 3,969 | 533 | 764 | 5,266 |
| [v2.9.4](https://github.com/laurent22/joplin/releases/tag/v2.9.4) (p) | 2022-08-18T16:52:26Z | 9,015 | 1,870 | 2,203 | 13,088 |
| [v2.12.16](https://github.com/laurent22/joplin/releases/tag/v2.12.16) | 2023-09-11T22:33:37Z | 28,829 | 14,678 | 2,471 | 45,978 |
| [v2.12.15](https://github.com/laurent22/joplin/releases/tag/v2.12.15) | 2023-08-27T11:35:39Z | 65,725 | 28,184 | 8,496 | 102,405 |
| [v2.12.12](https://github.com/laurent22/joplin/releases/tag/v2.12.12) (p) | 2023-08-19T22:44:56Z | 3,403 | 399 | 431 | 4,233 |
| [v2.12.10](https://github.com/laurent22/joplin/releases/tag/v2.12.10) (p) | 2023-07-30T18:25:58Z | 8,044 | 3,821 | 918 | 12,783 |
| [v2.12.9](https://github.com/laurent22/joplin/releases/tag/v2.12.9) (p) | 2023-07-25T16:06:08Z | 2,854 | 375 | 324 | 3,553 |
| [v2.12.7](https://github.com/laurent22/joplin/releases/tag/v2.12.7) (p) | 2023-07-13T12:55:31Z | 2,205 | 669 | 596 | 3,470 |
| [v2.12.5](https://github.com/laurent22/joplin/releases/tag/v2.12.5) (p) | 2023-07-12T15:03:46Z | 2,134 | 172 | 164 | 2,470 |
| [v2.12.4](https://github.com/laurent22/joplin/releases/tag/v2.12.4) (p) | 2023-07-07T22:36:53Z | 2,531 | 1,842 | 226 | 4,599 |
| [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,720 | 67,262 | 38,943 | 296,925 |
| [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.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,768 | 48,356 | 22,501 | 196,625 |
| [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.11.3](https://github.com/laurent22/joplin/releases/tag/v2.11.3) (p) | 2023-05-16T09:09:57Z | 157 | 49 | 46 | 252 |
| [v2.10.18](https://github.com/laurent22/joplin/releases/tag/v2.10.18) | 2023-05-09T13:27:43Z | 57,464 | 24,272 | 6,818 | 88,554 |
| [v2.10.17](https://github.com/laurent22/joplin/releases/tag/v2.10.17) | 2023-05-08T17:27:28Z | 19,826 | 11,518 | 900 | 32,244 |
| [v2.10.16](https://github.com/laurent22/joplin/releases/tag/v2.10.16) | 2023-04-27T09:27:45Z | 10,413 | 4,265 | 791 | 15,469 |
| [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,169 | 831 | 1,081 | 7,081 |
| [v2.10.12](https://github.com/laurent22/joplin/releases/tag/v2.10.12) (p) | 2023-03-23T12:17:13Z | 4,133 | 521 | 607 | 5,261 |
| [v2.10.11](https://github.com/laurent22/joplin/releases/tag/v2.10.11) (p) | 2023-03-17T10:54:02Z | 3,504 | 386 | 409 | 4,299 |
| [v2.10.10](https://github.com/laurent22/joplin/releases/tag/v2.10.10) (p) | 2023-03-13T23:16:37Z | 2,998 | 286 | 258 | 3,542 |
| [v2.10.9](https://github.com/laurent22/joplin/releases/tag/v2.10.9) (p) | 2023-03-12T16:16:45Z | 2,544 | 216 | 299 | 3,059 |
| [v2.10.8](https://github.com/laurent22/joplin/releases/tag/v2.10.8) (p) | 2023-02-26T12:53:55Z | 5,023 | 574 | 875 | 6,472 |
| [v2.10.7](https://github.com/laurent22/joplin/releases/tag/v2.10.7) (p) | 2023-02-24T10:56:20Z | 2,699 | 193 | 282 | 3,174 |
| [v2.10.6](https://github.com/laurent22/joplin/releases/tag/v2.10.6) (p) | 2023-02-20T14:00:05Z | 3,477 | 344 | 292 | 4,113 |
| [v2.10.5](https://github.com/laurent22/joplin/releases/tag/v2.10.5) | 2023-01-16T15:00:53Z | 385 | 107 | 329 | 821 |
| [v2.10.4](https://github.com/laurent22/joplin/releases/tag/v2.10.4) (p) | 2023-01-05T13:09:20Z | 8,701 | 1,306 | 1,815 | 11,822 |
| [v2.10.3](https://github.com/laurent22/joplin/releases/tag/v2.10.3) (p) | 2022-12-31T15:53:23Z | 3,268 | 316 | 421 | 4,005 |
| [v2.10.2](https://github.com/laurent22/joplin/releases/tag/v2.10.2) (p) | 2022-12-18T18:05:08Z | 4,702 | 594 | 643 | 5,939 |
| [v2.9.17](https://github.com/laurent22/joplin/releases/tag/v2.9.17) | 2022-11-15T10:28:37Z | 336,575 | 108,809 | 83,411 | 528,795 |
| [v2.9.12](https://github.com/laurent22/joplin/releases/tag/v2.9.12) (p) | 2022-11-01T17:06:05Z | 11,888 | 614 | 548 | 13,050 |
| [v2.9.11](https://github.com/laurent22/joplin/releases/tag/v2.9.11) (p) | 2022-10-23T16:09:58Z | 4,029 | 533 | 764 | 5,326 |
| [v2.9.4](https://github.com/laurent22/joplin/releases/tag/v2.9.4) (p) | 2022-08-18T16:52:26Z | 9,071 | 1,870 | 2,203 | 13,144 |
| [v2.9.3](https://github.com/laurent22/joplin/releases/tag/v2.9.3) (p) | 2022-08-18T13:11:09Z | 368 | 95 | 278 | 741 |
| [v2.9.2](https://github.com/laurent22/joplin/releases/tag/v2.9.2) (p) | 2022-08-12T18:12:12Z | 1,539 | 449 | 0 | 1,988 |
| [v2.9.1](https://github.com/laurent22/joplin/releases/tag/v2.9.1) (p) | 2022-07-11T09:59:32Z | 8,457 | 1,345 | 1,415 | 11,217 |
| [v2.8.8](https://github.com/laurent22/joplin/releases/tag/v2.8.8) | 2022-05-17T14:48:06Z | 352,556 | 114,433 | 113,626 | 580,615 |
| [v2.8.7](https://github.com/laurent22/joplin/releases/tag/v2.8.7) (p) | 2022-05-06T11:34:27Z | 4,969 | 368 | 432 | 5,769 |
| [v2.8.6](https://github.com/laurent22/joplin/releases/tag/v2.8.6) (p) | 2022-05-03T10:08:25Z | 4,587 | 405 | 335 | 5,327 |
| [v2.8.5](https://github.com/laurent22/joplin/releases/tag/v2.8.5) (p) | 2022-04-27T13:51:50Z | 4,687 | 373 | 360 | 5,420 |
| [v2.8.4](https://github.com/laurent22/joplin/releases/tag/v2.8.4) (p) | 2022-04-19T18:00:09Z | 5,163 | 592 | 336 | 6,091 |
| [v2.8.2](https://github.com/laurent22/joplin/releases/tag/v2.8.2) (p) | 2022-04-14T11:35:45Z | 4,581 | 282 | 284 | 5,147 |
| [v2.7.15](https://github.com/laurent22/joplin/releases/tag/v2.7.15) | 2022-03-17T13:03:23Z | 156,929 | 56,788 | 51,295 | 265,012 |
| [v2.7.14](https://github.com/laurent22/joplin/releases/tag/v2.7.14) | 2022-02-27T11:30:53Z | 35,170 | 16,786 | 4,817 | 56,773 |
| [v2.7.13](https://github.com/laurent22/joplin/releases/tag/v2.7.13) | 2022-02-24T17:42:12Z | 55,604 | 25,730 | 11,732 | 93,066 |
| [v2.7.12](https://github.com/laurent22/joplin/releases/tag/v2.7.12) (p) | 2022-02-14T15:06:14Z | 5,352 | 467 | 507 | 6,326 |
| [v2.7.11](https://github.com/laurent22/joplin/releases/tag/v2.7.11) (p) | 2022-02-12T13:00:02Z | 4,510 | 198 | 177 | 4,885 |
| [v2.7.10](https://github.com/laurent22/joplin/releases/tag/v2.7.10) (p) | 2022-02-11T18:19:09Z | 4,010 | 129 | 98 | 4,237 |
| [v2.7.8](https://github.com/laurent22/joplin/releases/tag/v2.7.8) (p) | 2022-01-19T09:35:27Z | 6,242 | 773 | 832 | 7,847 |
| [v2.7.7](https://github.com/laurent22/joplin/releases/tag/v2.7.7) (p) | 2022-01-18T14:05:07Z | 4,345 | 159 | 146 | 4,650 |
| [v2.7.6](https://github.com/laurent22/joplin/releases/tag/v2.7.6) (p) | 2022-01-17T17:08:28Z | 4,267 | 186 | 123 | 4,576 |
| [v2.6.10](https://github.com/laurent22/joplin/releases/tag/v2.6.10) | 2021-12-19T11:31:16Z | 137,128 | 51,218 | 49,336 | 237,682 |
| [v2.6.9](https://github.com/laurent22/joplin/releases/tag/v2.6.9) | 2021-12-17T11:57:32Z | 20,000 | 9,505 | 3,199 | 32,704 |
| [v2.6.7](https://github.com/laurent22/joplin/releases/tag/v2.6.7) (p) | 2021-12-16T10:47:23Z | 4,481 | 182 | 114 | 4,777 |
| [v2.6.6](https://github.com/laurent22/joplin/releases/tag/v2.6.6) (p) | 2021-12-13T12:31:43Z | 4,482 | 259 | 176 | 4,917 |
| [v2.6.5](https://github.com/laurent22/joplin/releases/tag/v2.6.5) (p) | 2021-12-13T10:07:04Z | 3,800 | 54 | 39 | 3,893 |
| [v2.6.4](https://github.com/laurent22/joplin/releases/tag/v2.6.4) (p) | 2021-12-09T19:53:43Z | 4,553 | 293 | 209 | 5,055 |
| [v2.6.2](https://github.com/laurent22/joplin/releases/tag/v2.6.2) (p) | 2021-11-18T12:19:12Z | 6,326 | 795 | 704 | 7,825 |
| [v2.5.12](https://github.com/laurent22/joplin/releases/tag/v2.5.12) | 2021-11-08T11:07:11Z | 83,795 | 32,511 | 25,244 | 141,550 |
| [v2.5.10](https://github.com/laurent22/joplin/releases/tag/v2.5.10) | 2021-11-01T08:22:42Z | 47,836 | 19,054 | 10,102 | 76,992 |
| [v2.5.8](https://github.com/laurent22/joplin/releases/tag/v2.5.8) | 2021-10-31T11:38:03Z | 16,548 | 6,584 | 2,336 | 25,468 |
| [v2.5.7](https://github.com/laurent22/joplin/releases/tag/v2.5.7) (p) | 2021-10-29T14:47:33Z | 4,067 | 206 | 170 | 4,443 |
| [v2.5.6](https://github.com/laurent22/joplin/releases/tag/v2.5.6) (p) | 2021-10-28T22:03:09Z | 4,114 | 180 | 110 | 4,404 |
| [v2.5.4](https://github.com/laurent22/joplin/releases/tag/v2.5.4) (p) | 2021-10-19T10:10:54Z | 5,608 | 570 | 587 | 6,765 |
| [v2.4.12](https://github.com/laurent22/joplin/releases/tag/v2.4.12) | 2021-10-13T17:24:34Z | 48,400 | 19,992 | 9,793 | 78,185 |
| [v2.5.1](https://github.com/laurent22/joplin/releases/tag/v2.5.1) (p) | 2021-10-02T09:51:58Z | 6,694 | 909 | 949 | 8,552 |
| [v2.4.9](https://github.com/laurent22/joplin/releases/tag/v2.4.9) | 2021-09-29T19:08:58Z | 60,089 | 23,268 | 15,934 | 99,291 |
| [v2.4.8](https://github.com/laurent22/joplin/releases/tag/v2.4.8) (p) | 2021-09-22T19:01:46Z | 10,463 | 1,776 | 537 | 12,776 |
| [v2.4.7](https://github.com/laurent22/joplin/releases/tag/v2.4.7) (p) | 2021-09-19T12:53:22Z | 4,617 | 261 | 213 | 5,091 |
| [v2.4.6](https://github.com/laurent22/joplin/releases/tag/v2.4.6) (p) | 2021-09-09T18:57:17Z | 5,371 | 463 | 522 | 6,356 |
| [v2.4.5](https://github.com/laurent22/joplin/releases/tag/v2.4.5) (p) | 2021-09-06T18:03:28Z | 4,617 | 279 | 227 | 5,123 |
| [v2.4.4](https://github.com/laurent22/joplin/releases/tag/v2.4.4) (p) | 2021-08-30T16:02:51Z | 4,836 | 382 | 377 | 5,595 |
| [v2.4.3](https://github.com/laurent22/joplin/releases/tag/v2.4.3) (p) | 2021-08-28T15:27:32Z | 4,320 | 209 | 184 | 4,713 |
| [v2.4.2](https://github.com/laurent22/joplin/releases/tag/v2.4.2) (p) | 2021-08-27T17:13:21Z | 4,110 | 153 | 93 | 4,356 |
| [v2.4.1](https://github.com/laurent22/joplin/releases/tag/v2.4.1) (p) | 2021-08-21T11:52:30Z | 4,916 | 374 | 339 | 5,629 |
| [v2.3.5](https://github.com/laurent22/joplin/releases/tag/v2.3.5) | 2021-08-17T06:43:30Z | 86,216 | 31,462 | 33,165 | 150,843 |
| [v2.3.3](https://github.com/laurent22/joplin/releases/tag/v2.3.3) | 2021-08-14T09:19:40Z | 19,297 | 6,889 | 4,065 | 30,251 |
| [v2.2.7](https://github.com/laurent22/joplin/releases/tag/v2.2.7) | 2021-08-11T11:03:26Z | 19,189 | 7,527 | 2,610 | 29,326 |
| [v2.2.6](https://github.com/laurent22/joplin/releases/tag/v2.2.6) (p) | 2021-08-09T19:29:20Z | 11,344 | 4,621 | 960 | 16,925 |
| [v2.2.5](https://github.com/laurent22/joplin/releases/tag/v2.2.5) (p) | 2021-08-07T10:35:24Z | 4,663 | 280 | 210 | 5,153 |
| [v2.2.4](https://github.com/laurent22/joplin/releases/tag/v2.2.4) (p) | 2021-08-05T16:42:48Z | 4,310 | 210 | 135 | 4,655 |
| [v2.2.2](https://github.com/laurent22/joplin/releases/tag/v2.2.2) (p) | 2021-07-19T10:28:35Z | 6,257 | 739 | 650 | 7,646 |
| [v2.1.9](https://github.com/laurent22/joplin/releases/tag/v2.1.9) | 2021-07-19T10:28:43Z | 50,832 | 18,968 | 16,825 | 86,625 |
| [v2.2.1](https://github.com/laurent22/joplin/releases/tag/v2.2.1) (p) | 2021-07-09T17:38:25Z | 5,877 | 419 | 396 | 6,692 |
| [v2.1.8](https://github.com/laurent22/joplin/releases/tag/v2.1.8) | 2021-07-03T08:25:16Z | 34,144 | 12,211 | 12,741 | 59,096 |
| [v2.1.7](https://github.com/laurent22/joplin/releases/tag/v2.1.7) | 2021-06-26T19:48:55Z | 17,435 | 6,413 | 3,644 | 27,492 |
| [v2.1.5](https://github.com/laurent22/joplin/releases/tag/v2.1.5) (p) | 2021-06-23T15:08:52Z | 4,757 | 256 | 204 | 5,217 |
| [v2.1.3](https://github.com/laurent22/joplin/releases/tag/v2.1.3) (p) | 2021-06-19T16:32:51Z | 4,833 | 314 | 219 | 5,366 |
| [v2.0.11](https://github.com/laurent22/joplin/releases/tag/v2.0.11) | 2021-06-16T17:55:49Z | 27,239 | 9,285 | 9,914 | 46,438 |
| [v2.0.10](https://github.com/laurent22/joplin/releases/tag/v2.0.10) | 2021-06-16T07:58:29Z | 6,373 | 950 | 402 | 7,725 |
| [v2.0.9](https://github.com/laurent22/joplin/releases/tag/v2.0.9) (p) | 2021-06-12T09:30:30Z | 4,840 | 310 | 903 | 6,053 |
| [v2.0.8](https://github.com/laurent22/joplin/releases/tag/v2.0.8) (p) | 2021-06-10T16:15:08Z | 4,251 | 249 | 602 | 5,102 |
| [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,516 | 1,345 | 1,415 | 11,276 |
| [v2.8.8](https://github.com/laurent22/joplin/releases/tag/v2.8.8) | 2022-05-17T14:48:06Z | 352,683 | 114,436 | 113,630 | 580,749 |
| [v2.8.7](https://github.com/laurent22/joplin/releases/tag/v2.8.7) (p) | 2022-05-06T11:34:27Z | 5,028 | 368 | 432 | 5,828 |
| [v2.8.6](https://github.com/laurent22/joplin/releases/tag/v2.8.6) (p) | 2022-05-03T10:08:25Z | 4,644 | 405 | 336 | 5,385 |
| [v2.8.5](https://github.com/laurent22/joplin/releases/tag/v2.8.5) (p) | 2022-04-27T13:51:50Z | 4,746 | 373 | 360 | 5,479 |
| [v2.8.4](https://github.com/laurent22/joplin/releases/tag/v2.8.4) (p) | 2022-04-19T18:00:09Z | 5,219 | 592 | 336 | 6,147 |
| [v2.8.2](https://github.com/laurent22/joplin/releases/tag/v2.8.2) (p) | 2022-04-14T11:35:45Z | 4,641 | 282 | 284 | 5,207 |
| [v2.7.15](https://github.com/laurent22/joplin/releases/tag/v2.7.15) | 2022-03-17T13:03:23Z | 156,991 | 56,788 | 51,299 | 265,078 |
| [v2.7.14](https://github.com/laurent22/joplin/releases/tag/v2.7.14) | 2022-02-27T11:30:53Z | 35,231 | 16,786 | 4,817 | 56,834 |
| [v2.7.13](https://github.com/laurent22/joplin/releases/tag/v2.7.13) | 2022-02-24T17:42:12Z | 55,666 | 25,730 | 11,732 | 93,128 |
| [v2.7.12](https://github.com/laurent22/joplin/releases/tag/v2.7.12) (p) | 2022-02-14T15:06:14Z | 5,413 | 467 | 507 | 6,387 |
| [v2.7.11](https://github.com/laurent22/joplin/releases/tag/v2.7.11) (p) | 2022-02-12T13:00:02Z | 4,569 | 198 | 177 | 4,944 |
| [v2.7.10](https://github.com/laurent22/joplin/releases/tag/v2.7.10) (p) | 2022-02-11T18:19:09Z | 4,065 | 129 | 98 | 4,292 |
| [v2.7.8](https://github.com/laurent22/joplin/releases/tag/v2.7.8) (p) | 2022-01-19T09:35:27Z | 6,301 | 773 | 832 | 7,906 |
| [v2.7.7](https://github.com/laurent22/joplin/releases/tag/v2.7.7) (p) | 2022-01-18T14:05:07Z | 4,400 | 159 | 146 | 4,705 |
| [v2.7.6](https://github.com/laurent22/joplin/releases/tag/v2.7.6) (p) | 2022-01-17T17:08:28Z | 4,324 | 186 | 123 | 4,633 |
| [v2.6.10](https://github.com/laurent22/joplin/releases/tag/v2.6.10) | 2021-12-19T11:31:16Z | 137,195 | 51,218 | 49,336 | 237,749 |
| [v2.6.9](https://github.com/laurent22/joplin/releases/tag/v2.6.9) | 2021-12-17T11:57:32Z | 20,058 | 9,505 | 3,200 | 32,763 |
| [v2.6.7](https://github.com/laurent22/joplin/releases/tag/v2.6.7) (p) | 2021-12-16T10:47:23Z | 4,538 | 182 | 114 | 4,834 |
| [v2.6.6](https://github.com/laurent22/joplin/releases/tag/v2.6.6) (p) | 2021-12-13T12:31:43Z | 4,536 | 259 | 177 | 4,972 |
| [v2.6.5](https://github.com/laurent22/joplin/releases/tag/v2.6.5) (p) | 2021-12-13T10:07:04Z | 3,857 | 54 | 39 | 3,950 |
| [v2.6.4](https://github.com/laurent22/joplin/releases/tag/v2.6.4) (p) | 2021-12-09T19:53:43Z | 4,611 | 293 | 209 | 5,113 |
| [v2.6.2](https://github.com/laurent22/joplin/releases/tag/v2.6.2) (p) | 2021-11-18T12:19:12Z | 6,381 | 795 | 704 | 7,880 |
| [v2.5.12](https://github.com/laurent22/joplin/releases/tag/v2.5.12) | 2021-11-08T11:07:11Z | 83,868 | 32,511 | 25,244 | 141,623 |
| [v2.5.10](https://github.com/laurent22/joplin/releases/tag/v2.5.10) | 2021-11-01T08:22:42Z | 47,903 | 19,054 | 10,102 | 77,059 |
| [v2.5.8](https://github.com/laurent22/joplin/releases/tag/v2.5.8) | 2021-10-31T11:38:03Z | 16,607 | 6,584 | 2,336 | 25,527 |
| [v2.5.7](https://github.com/laurent22/joplin/releases/tag/v2.5.7) (p) | 2021-10-29T14:47:33Z | 4,118 | 206 | 170 | 4,494 |
| [v2.5.6](https://github.com/laurent22/joplin/releases/tag/v2.5.6) (p) | 2021-10-28T22:03:09Z | 4,174 | 180 | 110 | 4,464 |
| [v2.5.4](https://github.com/laurent22/joplin/releases/tag/v2.5.4) (p) | 2021-10-19T10:10:54Z | 5,664 | 570 | 589 | 6,823 |
| [v2.4.12](https://github.com/laurent22/joplin/releases/tag/v2.4.12) | 2021-10-13T17:24:34Z | 48,464 | 19,992 | 9,793 | 78,249 |
| [v2.5.1](https://github.com/laurent22/joplin/releases/tag/v2.5.1) (p) | 2021-10-02T09:51:58Z | 6,757 | 909 | 949 | 8,615 |
| [v2.4.9](https://github.com/laurent22/joplin/releases/tag/v2.4.9) | 2021-09-29T19:08:58Z | 60,151 | 23,269 | 15,937 | 99,357 |
| [v2.4.8](https://github.com/laurent22/joplin/releases/tag/v2.4.8) (p) | 2021-09-22T19:01:46Z | 10,518 | 1,776 | 537 | 12,831 |
| [v2.4.7](https://github.com/laurent22/joplin/releases/tag/v2.4.7) (p) | 2021-09-19T12:53:22Z | 4,674 | 261 | 213 | 5,148 |
| [v2.4.6](https://github.com/laurent22/joplin/releases/tag/v2.4.6) (p) | 2021-09-09T18:57:17Z | 5,434 | 463 | 522 | 6,419 |
| [v2.4.5](https://github.com/laurent22/joplin/releases/tag/v2.4.5) (p) | 2021-09-06T18:03:28Z | 4,677 | 279 | 227 | 5,183 |
| [v2.4.4](https://github.com/laurent22/joplin/releases/tag/v2.4.4) (p) | 2021-08-30T16:02:51Z | 4,899 | 382 | 377 | 5,658 |
| [v2.4.3](https://github.com/laurent22/joplin/releases/tag/v2.4.3) (p) | 2021-08-28T15:27:32Z | 4,377 | 209 | 184 | 4,770 |
| [v2.4.2](https://github.com/laurent22/joplin/releases/tag/v2.4.2) (p) | 2021-08-27T17:13:21Z | 4,166 | 153 | 94 | 4,413 |
| [v2.4.1](https://github.com/laurent22/joplin/releases/tag/v2.4.1) (p) | 2021-08-21T11:52:30Z | 4,971 | 374 | 339 | 5,684 |
| [v2.3.5](https://github.com/laurent22/joplin/releases/tag/v2.3.5) | 2021-08-17T06:43:30Z | 86,297 | 31,466 | 33,171 | 150,934 |
| [v2.3.3](https://github.com/laurent22/joplin/releases/tag/v2.3.3) | 2021-08-14T09:19:40Z | 19,360 | 6,889 | 4,065 | 30,314 |
| [v2.2.7](https://github.com/laurent22/joplin/releases/tag/v2.2.7) | 2021-08-11T11:03:26Z | 19,255 | 7,527 | 2,610 | 29,392 |
| [v2.2.6](https://github.com/laurent22/joplin/releases/tag/v2.2.6) (p) | 2021-08-09T19:29:20Z | 11,397 | 4,621 | 960 | 16,978 |
| [v2.2.5](https://github.com/laurent22/joplin/releases/tag/v2.2.5) (p) | 2021-08-07T10:35:24Z | 4,718 | 280 | 210 | 5,208 |
| [v2.2.4](https://github.com/laurent22/joplin/releases/tag/v2.2.4) (p) | 2021-08-05T16:42:48Z | 4,366 | 210 | 135 | 4,711 |
| [v2.2.2](https://github.com/laurent22/joplin/releases/tag/v2.2.2) (p) | 2021-07-19T10:28:35Z | 6,324 | 739 | 650 | 7,713 |
| [v2.1.9](https://github.com/laurent22/joplin/releases/tag/v2.1.9) | 2021-07-19T10:28:43Z | 50,903 | 18,970 | 16,825 | 86,698 |
| [v2.2.1](https://github.com/laurent22/joplin/releases/tag/v2.2.1) (p) | 2021-07-09T17:38:25Z | 5,937 | 419 | 397 | 6,753 |
| [v2.1.8](https://github.com/laurent22/joplin/releases/tag/v2.1.8) | 2021-07-03T08:25:16Z | 34,205 | 12,211 | 12,741 | 59,157 |
| [v2.1.7](https://github.com/laurent22/joplin/releases/tag/v2.1.7) | 2021-06-26T19:48:55Z | 17,490 | 6,414 | 3,644 | 27,548 |
| [v2.1.5](https://github.com/laurent22/joplin/releases/tag/v2.1.5) (p) | 2021-06-23T15:08:52Z | 4,811 | 256 | 204 | 5,271 |
| [v2.1.3](https://github.com/laurent22/joplin/releases/tag/v2.1.3) (p) | 2021-06-19T16:32:51Z | 4,890 | 314 | 219 | 5,423 |
| [v2.0.11](https://github.com/laurent22/joplin/releases/tag/v2.0.11) | 2021-06-16T17:55:49Z | 27,308 | 9,285 | 9,917 | 46,510 |
| [v2.0.10](https://github.com/laurent22/joplin/releases/tag/v2.0.10) | 2021-06-16T07:58:29Z | 6,435 | 950 | 402 | 7,787 |
| [v2.0.9](https://github.com/laurent22/joplin/releases/tag/v2.0.9) (p) | 2021-06-12T09:30:30Z | 4,900 | 310 | 903 | 6,113 |
| [v2.0.8](https://github.com/laurent22/joplin/releases/tag/v2.0.8) (p) | 2021-06-10T16:15:08Z | 4,313 | 249 | 602 | 5,164 |
| [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,333 | 505 | 1,683 | 8,521 |
| [v2.0.2](https://github.com/laurent22/joplin/releases/tag/v2.0.2) (p) | 2021-05-21T18:07:48Z | 6,393 | 505 | 1,683 | 8,581 |
| [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,442 | 16,318 | 19,450 | 78,210 |
| [v1.8.4](https://github.com/laurent22/joplin/releases/tag/v1.8.4) (p) | 2021-05-09T18:05:05Z | 4,187 | 154 | 475 | 4,816 |
| [v1.8.3](https://github.com/laurent22/joplin/releases/tag/v1.8.3) (p) | 2021-05-04T10:38:16Z | 5,209 | 322 | 953 | 6,484 |
| [v1.8.2](https://github.com/laurent22/joplin/releases/tag/v1.8.2) (p) | 2021-04-25T10:50:51Z | 5,489 | 453 | 1,303 | 7,245 |
| [v1.8.1](https://github.com/laurent22/joplin/releases/tag/v1.8.1) (p) | 2021-03-29T10:46:41Z | 6,499 | 842 | 2,468 | 9,809 |
| [v1.7.11](https://github.com/laurent22/joplin/releases/tag/v1.7.11) | 2021-02-03T12:50:01Z | 121,669 | 43,027 | 64,476 | 229,172 |
| [v1.8.5](https://github.com/laurent22/joplin/releases/tag/v1.8.5) | 2021-05-10T11:58:14Z | 42,519 | 16,319 | 19,450 | 78,288 |
| [v1.8.4](https://github.com/laurent22/joplin/releases/tag/v1.8.4) (p) | 2021-05-09T18:05:05Z | 4,250 | 154 | 475 | 4,879 |
| [v1.8.3](https://github.com/laurent22/joplin/releases/tag/v1.8.3) (p) | 2021-05-04T10:38:16Z | 5,267 | 322 | 953 | 6,542 |
| [v1.8.2](https://github.com/laurent22/joplin/releases/tag/v1.8.2) (p) | 2021-04-25T10:50:51Z | 5,549 | 453 | 1,304 | 7,306 |
| [v1.8.1](https://github.com/laurent22/joplin/releases/tag/v1.8.1) (p) | 2021-03-29T10:46:41Z | 6,556 | 842 | 2,468 | 9,866 |
| [v1.7.11](https://github.com/laurent22/joplin/releases/tag/v1.7.11) | 2021-02-03T12:50:01Z | 121,744 | 43,031 | 64,477 | 229,252 |
| [v1.7.10](https://github.com/laurent22/joplin/releases/tag/v1.7.10) | 2021-01-30T13:25:29Z | 14,646 | 4,887 | 4,534 | 24,067 |
| [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,123 | 7,735 | 7,640 | 39,498 |
| [v1.6.8](https://github.com/laurent22/joplin/releases/tag/v1.6.8) | 2021-01-20T18:11:34Z | 24,194 | 7,735 | 7,640 | 39,569 |
| [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,495 | 4,672 | 4,575 | 24,742 |
| [v1.6.7](https://github.com/laurent22/joplin/releases/tag/v1.6.7) | 2021-01-11T23:20:33Z | 15,564 | 4,672 | 4,576 | 24,812 |
| [v1.6.6](https://github.com/laurent22/joplin/releases/tag/v1.6.6) | 2021-01-09T16:15:31Z | 12,872 | 3,444 | 4,823 | 21,139 |
| [v1.6.5](https://github.com/laurent22/joplin/releases/tag/v1.6.5) (p) | 2021-01-09T01:24:32Z | 4,444 | 93 | 326 | 4,863 |
| [v1.6.5](https://github.com/laurent22/joplin/releases/tag/v1.6.5) (p) | 2021-01-09T01:24:32Z | 4,496 | 93 | 326 | 4,915 |
| [v1.6.4](https://github.com/laurent22/joplin/releases/tag/v1.6.4) (p) | 2021-01-07T19:11:32Z | 427 | 95 | 222 | 744 |
| [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 | 15,994 | 5,236 | 5,554 | 26,784 |
| [v1.5.14](https://github.com/laurent22/joplin/releases/tag/v1.5.14) | 2020-12-30T01:48:46Z | 16,059 | 5,236 | 5,554 | 26,849 |
| [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 | 231 | 1,269 |
| [v1.5.12](https://github.com/laurent22/joplin/releases/tag/v1.5.12) | 2020-12-28T15:14:08Z | 2,576 | 1,799 | 946 | 5,321 |
@@ -235,47 +235,47 @@ updated: 2025-12-01T02:03:38Z
| [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,354 | 13,639 | 11,719 | 55,712 |
| [v1.4.19](https://github.com/laurent22/joplin/releases/tag/v1.4.19) | 2020-12-01T11:11:16Z | 30,417 | 13,639 | 11,719 | 55,775 |
| [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,250 | 195 | 613 | 6,058 |
| [v1.4.11](https://github.com/laurent22/joplin/releases/tag/v1.4.11) (p) | 2020-11-19T23:06:51Z | 5,309 | 195 | 613 | 6,117 |
| [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 | 35,953 | 11,393 | 10,551 | 57,897 |
| [v1.3.18](https://github.com/laurent22/joplin/releases/tag/v1.3.18) | 2020-11-06T12:07:02Z | 36,018 | 11,393 | 10,551 | 57,962 |
| [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 | 779 | 131 | 72 | 982 |
| [v1.3.15](https://github.com/laurent22/joplin/releases/tag/v1.3.15) | 2020-11-04T12:22:50Z | 2,829 | 1,349 | 874 | 5,052 |
| [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,835 | 1,349 | 874 | 5,058 |
| [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 | 930 | 281 | 654 | 1,865 |
| [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.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 | 49,946 | 17,797 | 14,097 | 81,840 |
| [v1.2.4](https://github.com/laurent22/joplin/releases/tag/v1.2.4) (p) | 2020-09-30T07:34:29Z | 866 | 291 | 823 | 1,980 |
| [v1.2.6](https://github.com/laurent22/joplin/releases/tag/v1.2.6) | 2020-10-09T13:56:59Z | 50,028 | 17,797 | 14,097 | 81,922 |
| [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.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.1.4](https://github.com/laurent22/joplin/releases/tag/v1.1.4) | 2020-09-21T11:20:09Z | 28,240 | 13,560 | 7,790 | 49,590 |
| [v1.1.4](https://github.com/laurent22/joplin/releases/tag/v1.1.4) | 2020-09-21T11:20:09Z | 28,240 | 13,561 | 7,791 | 49,592 |
| [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.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,073 | 10,069 | 5,678 | 38,820 |
| [v1.0.245](https://github.com/laurent22/joplin/releases/tag/v1.0.245) | 2020-09-09T12:56:10Z | 23,073 | 10,070 | 5,678 | 38,821 |
| [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,691 | 5,992 | 5,154 | 37,837 |
| [v1.0.239](https://github.com/laurent22/joplin/releases/tag/v1.0.239) (p) | 2020-09-01T21:56:36Z | 1,012 | 270 | 426 | 1,708 |
| [v1.0.241](https://github.com/laurent22/joplin/releases/tag/v1.0.241) | 2020-09-04T18:06:00Z | 26,693 | 5,992 | 5,154 | 37,839 |
| [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,085 | 530 | 945 | 3,560 |
| [v1.0.234](https://github.com/laurent22/joplin/releases/tag/v1.0.234) (p) | 2020-08-17T23:13:02Z | 718 | 166 | 124 | 1,008 |
| [v1.0.233](https://github.com/laurent22/joplin/releases/tag/v1.0.233) | 2020-08-01T14:51:15Z | 48,579 | 18,250 | 12,392 | 79,221 |
| [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,651 | 18,251 | 12,392 | 79,294 |
| [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,014 | 15,328 | 9,680 | 67,022 |
| [v1.0.227](https://github.com/laurent22/joplin/releases/tag/v1.0.227) | 2020-07-07T20:44:54Z | 42,020 | 15,328 | 9,680 | 67,028 |
| [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.223](https://github.com/laurent22/joplin/releases/tag/v1.0.223) (p) | 2020-06-20T11:51:27Z | 233 | 154 | 102 | 489 |
@@ -283,40 +283,40 @@ updated: 2025-12-01T02:03:38Z
| [v1.0.220](https://github.com/laurent22/joplin/releases/tag/v1.0.220) | 2020-06-13T18:26:22Z | 32,835 | 9,974 | 6,450 | 49,259 |
| [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.217](https://github.com/laurent22/joplin/releases/tag/v1.0.217) (p) | 2020-06-06T15:17:27Z | 283 | 140 | 86 | 509 |
| [v1.0.216](https://github.com/laurent22/joplin/releases/tag/v1.0.216) | 2020-05-24T14:21:01Z | 42,335 | 14,364 | 10,229 | 66,928 |
| [v1.0.216](https://github.com/laurent22/joplin/releases/tag/v1.0.216) | 2020-05-24T14:21:01Z | 42,409 | 14,364 | 10,229 | 67,002 |
| [v1.0.214](https://github.com/laurent22/joplin/releases/tag/v1.0.214) (p) | 2020-05-21T17:15:15Z | 7,109 | 3,515 | 790 | 11,414 |
| [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 | 177 | 114 | 647 |
| [v1.0.209](https://github.com/laurent22/joplin/releases/tag/v1.0.209) (p) | 2020-05-17T18:32:51Z | 1,451 | 896 | 174 | 2,521 |
| [v1.0.207](https://github.com/laurent22/joplin/releases/tag/v1.0.207) (p) | 2020-05-10T16:37:35Z | 1,254 | 307 | 1,046 | 2,607 |
| [v1.0.201](https://github.com/laurent22/joplin/releases/tag/v1.0.201) | 2020-04-15T22:55:13Z | 54,431 | 20,108 | 18,226 | 92,765 |
| [v1.0.201](https://github.com/laurent22/joplin/releases/tag/v1.0.201) | 2020-04-15T22:55:13Z | 54,431 | 20,109 | 18,227 | 92,767 |
| [v1.0.200](https://github.com/laurent22/joplin/releases/tag/v1.0.200) | 2020-04-12T12:17:46Z | 9,641 | 4,941 | 1,934 | 16,516 |
| [v1.0.199](https://github.com/laurent22/joplin/releases/tag/v1.0.199) | 2020-04-10T18:41:58Z | 19,952 | 5,937 | 3,842 | 29,731 |
| [v1.0.197](https://github.com/laurent22/joplin/releases/tag/v1.0.197) | 2020-03-30T17:21:22Z | 23,526 | 9,875 | 6,696 | 40,097 |
| [v1.0.195](https://github.com/laurent22/joplin/releases/tag/v1.0.195) | 2020-03-22T19:56:12Z | 19,183 | 8,003 | 4,537 | 31,723 |
| [v1.0.199](https://github.com/laurent22/joplin/releases/tag/v1.0.199) | 2020-04-10T18:41:58Z | 19,962 | 5,937 | 3,842 | 29,741 |
| [v1.0.197](https://github.com/laurent22/joplin/releases/tag/v1.0.197) | 2020-03-30T17:21:22Z | 23,528 | 9,876 | 6,706 | 40,110 |
| [v1.0.195](https://github.com/laurent22/joplin/releases/tag/v1.0.195) | 2020-03-22T19:56:12Z | 19,185 | 8,006 | 4,538 | 31,729 |
| [v1.0.194](https://github.com/laurent22/joplin/releases/tag/v1.0.194) (p) | 2020-03-14T00:00:32Z | 1,344 | 1,434 | 544 | 3,322 |
| [v1.0.193](https://github.com/laurent22/joplin/releases/tag/v1.0.193) | 2020-03-08T08:58:53Z | 28,851 | 10,973 | 7,448 | 47,272 |
| [v1.0.192](https://github.com/laurent22/joplin/releases/tag/v1.0.192) (p) | 2020-03-06T23:27:52Z | 590 | 167 | 115 | 872 |
| [v1.0.190](https://github.com/laurent22/joplin/releases/tag/v1.0.190) (p) | 2020-03-06T01:22:22Z | 465 | 136 | 111 | 712 |
| [v1.0.189](https://github.com/laurent22/joplin/releases/tag/v1.0.189) (p) | 2020-03-04T17:27:15Z | 458 | 139 | 124 | 721 |
| [v1.0.193](https://github.com/laurent22/joplin/releases/tag/v1.0.193) | 2020-03-08T08:58:53Z | 28,852 | 10,974 | 7,453 | 47,279 |
| [v1.0.192](https://github.com/laurent22/joplin/releases/tag/v1.0.192) (p) | 2020-03-06T23:27:52Z | 594 | 167 | 115 | 876 |
| [v1.0.190](https://github.com/laurent22/joplin/releases/tag/v1.0.190) (p) | 2020-03-06T01:22:22Z | 469 | 136 | 111 | 716 |
| [v1.0.189](https://github.com/laurent22/joplin/releases/tag/v1.0.189) (p) | 2020-03-04T17:27:15Z | 462 | 139 | 124 | 725 |
| [v1.0.187](https://github.com/laurent22/joplin/releases/tag/v1.0.187) (p) | 2020-03-01T12:31:06Z | 985 | 281 | 299 | 1,565 |
| [v1.0.179](https://github.com/laurent22/joplin/releases/tag/v1.0.179) | 2020-01-24T22:42:41Z | 71,840 | 29,137 | 22,604 | 123,581 |
| [v1.0.179](https://github.com/laurent22/joplin/releases/tag/v1.0.179) | 2020-01-24T22:42:41Z | 71,846 | 29,142 | 22,606 | 123,594 |
| [v1.0.178](https://github.com/laurent22/joplin/releases/tag/v1.0.178) | 2020-01-20T19:06:45Z | 17,682 | 6,014 | 2,623 | 26,319 |
| [v1.0.177](https://github.com/laurent22/joplin/releases/tag/v1.0.177) (p) | 2019-12-30T14:40:40Z | 2,015 | 481 | 758 | 3,254 |
| [v1.0.176](https://github.com/laurent22/joplin/releases/tag/v1.0.176) (p) | 2019-12-14T10:36:44Z | 3,179 | 2,582 | 497 | 6,258 |
| [v1.0.175](https://github.com/laurent22/joplin/releases/tag/v1.0.175) | 2019-12-08T11:48:47Z | 73,813 | 17,029 | 16,633 | 107,475 |
| [v1.0.174](https://github.com/laurent22/joplin/releases/tag/v1.0.174) | 2019-11-12T18:20:58Z | 30,786 | 11,812 | 8,258 | 50,856 |
| [v1.0.173](https://github.com/laurent22/joplin/releases/tag/v1.0.173) | 2019-11-11T08:33:35Z | 5,159 | 2,128 | 776 | 8,063 |
| [v1.0.170](https://github.com/laurent22/joplin/releases/tag/v1.0.170) | 2019-10-13T22:13:04Z | 27,921 | 8,836 | 7,721 | 44,478 |
| [v1.0.170](https://github.com/laurent22/joplin/releases/tag/v1.0.170) | 2019-10-13T22:13:04Z | 27,922 | 8,837 | 7,721 | 44,480 |
| [v1.0.169](https://github.com/laurent22/joplin/releases/tag/v1.0.169) | 2019-09-27T18:35:13Z | 17,300 | 5,977 | 3,787 | 27,064 |
| [v1.0.168](https://github.com/laurent22/joplin/releases/tag/v1.0.168) | 2019-09-25T21:21:38Z | 5,398 | 2,320 | 749 | 8,467 |
| [v1.0.167](https://github.com/laurent22/joplin/releases/tag/v1.0.167) | 2019-09-10T08:48:37Z | 17,026 | 5,751 | 3,744 | 26,521 |
| [v1.0.168](https://github.com/laurent22/joplin/releases/tag/v1.0.168) | 2019-09-25T21:21:38Z | 5,400 | 2,320 | 749 | 8,469 |
| [v1.0.167](https://github.com/laurent22/joplin/releases/tag/v1.0.167) | 2019-09-10T08:48:37Z | 17,028 | 5,751 | 3,745 | 26,524 |
| [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.165](https://github.com/laurent22/joplin/releases/tag/v1.0.165) | 2019-08-14T21:46:29Z | 19,128 | 7,026 | 5,494 | 31,648 |
| [v1.0.161](https://github.com/laurent22/joplin/releases/tag/v1.0.161) | 2019-07-13T18:30:00Z | 19,461 | 6,395 | 4,163 | 30,019 |
| [v1.0.160](https://github.com/laurent22/joplin/releases/tag/v1.0.160) | 2019-06-15T00:21:40Z | 30,887 | 7,801 | 8,139 | 46,827 |
| [v1.0.159](https://github.com/laurent22/joplin/releases/tag/v1.0.159) | 2019-06-08T00:00:19Z | 5,294 | 2,224 | 1,323 | 8,841 |
| [v1.0.158](https://github.com/laurent22/joplin/releases/tag/v1.0.158) | 2019-05-27T19:01:18Z | 9,895 | 3,594 | 1,964 | 15,453 |
| [v1.0.160](https://github.com/laurent22/joplin/releases/tag/v1.0.160) | 2019-06-15T00:21:40Z | 30,893 | 7,801 | 8,139 | 46,833 |
| [v1.0.159](https://github.com/laurent22/joplin/releases/tag/v1.0.159) | 2019-06-08T00:00:19Z | 5,298 | 2,224 | 1,323 | 8,845 |
| [v1.0.158](https://github.com/laurent22/joplin/releases/tag/v1.0.158) | 2019-05-27T19:01:18Z | 9,895 | 3,595 | 1,964 | 15,454 |
| [v1.0.157](https://github.com/laurent22/joplin/releases/tag/v1.0.157) | 2019-05-26T17:55:53Z | 2,254 | 885 | 316 | 3,455 |
| [v1.0.153](https://github.com/laurent22/joplin/releases/tag/v1.0.153) (p) | 2019-05-15T06:27:29Z | 918 | 145 | 133 | 1,196 |
| [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 |
@@ -325,20 +325,20 @@ updated: 2025-12-01T02:03:38Z
| [v1.0.148](https://github.com/laurent22/joplin/releases/tag/v1.0.148) (p) | 2019-05-08T19:12:24Z | 188 | 95 | 121 | 404 |
| [v1.0.145](https://github.com/laurent22/joplin/releases/tag/v1.0.145) | 2019-05-03T09:16:53Z | 7,087 | 2,905 | 1,468 | 11,460 |
| [v1.0.143](https://github.com/laurent22/joplin/releases/tag/v1.0.143) | 2019-04-22T10:51:38Z | 11,998 | 3,597 | 2,813 | 18,408 |
| [v1.0.142](https://github.com/laurent22/joplin/releases/tag/v1.0.142) | 2019-04-02T16:44:51Z | 14,873 | 4,612 | 4,761 | 24,246 |
| [v1.0.140](https://github.com/laurent22/joplin/releases/tag/v1.0.140) | 2019-03-10T20:59:58Z | 13,753 | 4,223 | 3,409 | 21,385 |
| [v1.0.142](https://github.com/laurent22/joplin/releases/tag/v1.0.142) | 2019-04-02T16:44:51Z | 14,873 | 4,612 | 4,762 | 24,247 |
| [v1.0.140](https://github.com/laurent22/joplin/releases/tag/v1.0.140) | 2019-03-10T20:59:58Z | 13,757 | 4,223 | 3,410 | 21,390 |
| [v1.0.139](https://github.com/laurent22/joplin/releases/tag/v1.0.139) (p) | 2019-03-09T10:06:48Z | 190 | 106 | 70 | 366 |
| [v1.0.138](https://github.com/laurent22/joplin/releases/tag/v1.0.138) (p) | 2019-03-03T17:23:00Z | 258 | 131 | 108 | 497 |
| [v1.0.138](https://github.com/laurent22/joplin/releases/tag/v1.0.138) (p) | 2019-03-03T17:23:00Z | 262 | 131 | 108 | 501 |
| [v1.0.137](https://github.com/laurent22/joplin/releases/tag/v1.0.137) (p) | 2019-03-03T01:12:51Z | 652 | 98 | 108 | 858 |
| [v1.0.135](https://github.com/laurent22/joplin/releases/tag/v1.0.135) | 2019-02-27T23:36:57Z | 12,708 | 4,003 | 4,112 | 20,823 |
| [v1.0.135](https://github.com/laurent22/joplin/releases/tag/v1.0.135) | 2019-02-27T23:36:57Z | 12,708 | 4,004 | 4,112 | 20,824 |
| [v1.0.134](https://github.com/laurent22/joplin/releases/tag/v1.0.134) | 2019-02-27T10:21:44Z | 1,516 | 614 | 246 | 2,376 |
| [v1.0.132](https://github.com/laurent22/joplin/releases/tag/v1.0.132) | 2019-02-26T23:02:05Z | 1,154 | 495 | 121 | 1,770 |
| [v1.0.127](https://github.com/laurent22/joplin/releases/tag/v1.0.127) | 2019-02-14T23:12:48Z | 10,040 | 3,218 | 2,963 | 16,221 |
| [v1.0.132](https://github.com/laurent22/joplin/releases/tag/v1.0.132) | 2019-02-26T23:02:05Z | 1,154 | 496 | 121 | 1,771 |
| [v1.0.127](https://github.com/laurent22/joplin/releases/tag/v1.0.127) | 2019-02-14T23:12:48Z | 10,040 | 3,219 | 2,963 | 16,222 |
| [v1.0.126](https://github.com/laurent22/joplin/releases/tag/v1.0.126) (p) | 2019-02-09T19:46:16Z | 1,003 | 118 | 139 | 1,260 |
| [v1.0.125](https://github.com/laurent22/joplin/releases/tag/v1.0.125) | 2019-01-26T18:14:33Z | 10,450 | 3,602 | 1,728 | 15,780 |
| [v1.0.120](https://github.com/laurent22/joplin/releases/tag/v1.0.120) | 2019-01-10T21:42:53Z | 15,710 | 5,257 | 6,551 | 27,518 |
| [v1.0.119](https://github.com/laurent22/joplin/releases/tag/v1.0.119) | 2018-12-18T12:40:22Z | 9,039 | 3,306 | 2,045 | 14,390 |
| [v1.0.118](https://github.com/laurent22/joplin/releases/tag/v1.0.118) | 2019-01-11T08:34:13Z | 769 | 292 | 117 | 1,178 |
| [v1.0.125](https://github.com/laurent22/joplin/releases/tag/v1.0.125) | 2019-01-26T18:14:33Z | 10,452 | 3,603 | 1,728 | 15,783 |
| [v1.0.120](https://github.com/laurent22/joplin/releases/tag/v1.0.120) | 2019-01-10T21:42:53Z | 15,710 | 5,258 | 6,551 | 27,519 |
| [v1.0.119](https://github.com/laurent22/joplin/releases/tag/v1.0.119) | 2018-12-18T12:40:22Z | 9,043 | 3,306 | 2,045 | 14,394 |
| [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,945 | 6,414 | 27,711 |
| [v1.0.116](https://github.com/laurent22/joplin/releases/tag/v1.0.116) | 2018-11-20T19:09:24Z | 4,073 | 1,173 | 745 | 5,991 |
| [v1.0.115](https://github.com/laurent22/joplin/releases/tag/v1.0.115) | 2018-11-16T16:52:02Z | 3,727 | 1,345 | 829 | 5,901 |
@@ -350,20 +350,20 @@ updated: 2025-12-01T02:03:38Z
| [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.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,747 | 7,420 | 27,381 |
| [v1.0.104](https://github.com/laurent22/joplin/releases/tag/v1.0.104) | 2018-06-28T20:25:36Z | 15,214 | 4,747 | 7,421 | 27,382 |
| [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.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,805 | 1,271 | 1,755 | 5,831 |
| [v1.0.96](https://github.com/laurent22/joplin/releases/tag/v1.0.96) | 2018-05-26T16:36:39Z | 2,805 | 1,271 | 1,756 | 5,832 |
| [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 | 450 | 2,304 |
| [v1.0.93](https://github.com/laurent22/joplin/releases/tag/v1.0.93) | 2018-05-14T11:36:01Z | 1,886 | 1,378 | 808 | 4,072 |
| [v1.0.93](https://github.com/laurent22/joplin/releases/tag/v1.0.93) | 2018-05-14T11:36:01Z | 1,886 | 1,385 | 808 | 4,079 |
| [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.89](https://github.com/laurent22/joplin/releases/tag/v1.0.89) | 2018-05-09T13:05:05Z | 551 | 290 | 161 | 1,002 |
| [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,645 | 2,581 | 2,708 | 10,934 |
| [v1.0.83](https://github.com/laurent22/joplin/releases/tag/v1.0.83) | 2018-04-04T19:43:58Z | 5,647 | 2,581 | 2,708 | 10,936 |
| [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 |
@@ -376,29 +376,29 @@ updated: 2025-12-01T02:03:38Z
| [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,200 | 587 | 1,151 | 2,938 |
| [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 | 350 | 403 | 1,470 |
| [v0.10.61](https://github.com/laurent22/joplin/releases/tag/v0.10.61) | 2018-02-08T18:27:39Z | 1,129 | 683 | 990 | 2,802 |
| [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 |
| [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.47](https://github.com/laurent22/joplin/releases/tag/v0.10.47) | 2018-01-16T17:27:17Z | 1,337 | 1,321 | 94 | 2,752 |
| [v0.10.43](https://github.com/laurent22/joplin/releases/tag/v0.10.43) | 2018-01-08T10:12:10Z | 3,492 | 2,408 | 1,246 | 7,146 |
| [v0.10.43](https://github.com/laurent22/joplin/releases/tag/v0.10.43) | 2018-01-08T10:12:10Z | 3,492 | 2,408 | 1,247 | 7,147 |
| [v0.10.41](https://github.com/laurent22/joplin/releases/tag/v0.10.41) | 2018-01-05T20:38:12Z | 1,249 | 1,605 | 270 | 3,124 |
| [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,001 | 4,451 | 3,335 | 13,787 |
| [v0.10.39](https://github.com/laurent22/joplin/releases/tag/v0.10.39) | 2017-12-11T21:19:44Z | 6,001 | 4,451 | 3,336 | 13,788 |
| [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.37](https://github.com/laurent22/joplin/releases/tag/v0.10.37) | 2017-12-07T19:38:05Z | 294 | 897 | 112 | 1,303 |
| [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 | 441 | 2,868 |
| [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.28](https://github.com/laurent22/joplin/releases/tag/v0.10.28) | 2017-11-30T01:07:46Z | 1,499 | 1,768 | 910 | 4,177 |
| [v0.10.26](https://github.com/laurent22/joplin/releases/tag/v0.10.26) | 2017-11-29T16:02:17Z | 311 | 763 | 293 | 1,367 |
| [v0.10.25](https://github.com/laurent22/joplin/releases/tag/v0.10.25) | 2017-11-24T14:27:49Z | 263 | 758 | 6,813 | 7,834 |
| [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 | 263 | 758 | 6,815 | 7,836 |
| [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.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 |

View File

@@ -76,12 +76,18 @@ Your Contributions.
waived such rights for your Contributions to the Company, or that your
employer has executed a separate Corporate CLA with the Company.
5. You represent that each of Your Contributions is Your original creation (see
section 7 for submissions on behalf of others). You represent that Your
Contribution submissions include complete details of any third-party license
or other restriction (including, but not limited to, related patents and
trademarks) of which you are personally aware and which are associated with
any part of Your Contributions.
5. You represent that each of Your Contributions is Your original creation or
that You otherwise have the lawful right to submit it, and that each
Contribution is a work of human authorship. You represent that, although
automated or artificial intelligence tools may have been used to assist in
the creation of a Contribution, You have exercised creative judgment in
reviewing, selecting, modifying, or arranging the Contribution, and that no
Contribution is submitted that was generated entirely by automated means
without meaningful human involvement. You represent that Your Contribution
submissions include complete details of any third-party license or other
restriction (including, but not limited to, related patents and trademarks)
of which you are personally aware and which are associated with any part of
Your Contributions.
6. You are not expected to provide support for Your Contributions, except to the
extent You desire to provide support. You may provide support for free, for a

View File

@@ -75,6 +75,7 @@
"prosemirror-model",
"prosemirror-state",
"prosemirror-view",
"prosemirror-tables",
"@fortawesome/fontawesome-svg-core",
"@fortawesome/free-solid-svg-icons",
"@svgr/webpack",

298
yarn.lock
View File

@@ -9002,9 +9002,9 @@ __metadata:
languageName: node
linkType: hard
"@expo/cli@npm:0.24.20":
version: 0.24.20
resolution: "@expo/cli@npm:0.24.20"
"@expo/cli@npm:0.24.22":
version: 0.24.22
resolution: "@expo/cli@npm:0.24.22"
dependencies:
"@0no-co/graphql.web": "npm:^1.0.8"
"@babel/runtime": "npm:^7.20.0"
@@ -9019,11 +9019,12 @@ __metadata:
"@expo/osascript": "npm:^2.2.5"
"@expo/package-manager": "npm:^1.8.6"
"@expo/plist": "npm:^0.3.5"
"@expo/prebuild-config": "npm:^9.0.11"
"@expo/prebuild-config": "npm:^9.0.12"
"@expo/schema-utils": "npm:^0.1.0"
"@expo/spawn-async": "npm:^1.7.2"
"@expo/ws-tunnel": "npm:^1.0.1"
"@expo/xcpretty": "npm:^4.3.0"
"@react-native/dev-middleware": "npm:0.79.5"
"@react-native/dev-middleware": "npm:0.79.6"
"@urql/core": "npm:^5.0.6"
"@urql/exchange-retry": "npm:^1.3.0"
accepts: "npm:^1.3.8"
@@ -9069,7 +9070,7 @@ __metadata:
ws: "npm:^8.12.1"
bin:
expo-internal: build/bin/cli
checksum: 10/f6fb154c08dbc04761590b5b0eeb8b7936c1120fe50c06a7fa9948fe2ead17ee64c6eb466c3dda5441df205d968159af9a85bf147a8e78916bf53d5aab5706c9
checksum: 10/2d49b3b24092380846a19519e9e7554ce0fa8958b3d5abc6c5a9485efe8195446dc64771f08a096f6c56de0705b74e59a1608c268620ec810b156aa252ad487f
languageName: node
linkType: hard
@@ -9268,21 +9269,28 @@ __metadata:
languageName: node
linkType: hard
"@expo/prebuild-config@npm:^9.0.11":
version: 9.0.11
resolution: "@expo/prebuild-config@npm:9.0.11"
"@expo/prebuild-config@npm:^9.0.12":
version: 9.0.12
resolution: "@expo/prebuild-config@npm:9.0.12"
dependencies:
"@expo/config": "npm:~11.0.13"
"@expo/config-plugins": "npm:~10.1.2"
"@expo/config-types": "npm:^53.0.5"
"@expo/image-utils": "npm:^0.7.6"
"@expo/json-file": "npm:^9.1.5"
"@react-native/normalize-colors": "npm:0.79.5"
"@react-native/normalize-colors": "npm:0.79.6"
debug: "npm:^4.3.1"
resolve-from: "npm:^5.0.0"
semver: "npm:^7.6.0"
xml2js: "npm:0.6.0"
checksum: 10/220461fa9d98ab8a95e7c39c943667e9d5980f4b90b1ffbaaef4e3f0db3a862ed4919ccbab0768129a89d2187b8c0f80b4159d9a57e86fa79bdaafaa1e98444c
checksum: 10/7b31e0b7499c4df314d21ceec3e4effff01826616b823a18c8cabfded4a52659a842650e8da75ec289e09a71877b839e448f62eaf375dcd6baaad78e1534eca8
languageName: node
linkType: hard
"@expo/schema-utils@npm:^0.1.0":
version: 0.1.8
resolution: "@expo/schema-utils@npm:0.1.8"
checksum: 10/72c02dcd107da08bd0df829b57edca77e48d9e3386304510a043c8d19892d20ec230ccdb27f5b2e08b3576046b3bfe66afdaf1e071c6a0296fa3817dbaa49932
languageName: node
linkType: hard
@@ -10433,13 +10441,13 @@ __metadata:
"@joplin/renderer": "npm:~3.5"
"@joplin/tools": "npm:~3.5"
"@joplin/utils": "npm:~3.5"
"@playwright/test": "npm:1.55.0"
"@playwright/test": "npm:1.55.1"
"@sentry/electron": "npm:4.24.0"
"@testing-library/react-hooks": "npm:8.0.1"
"@types/jest": "npm:29.5.14"
"@types/mustache": "npm:4.2.6"
"@types/node": "npm:18.19.130"
"@types/react": "npm:18.3.23"
"@types/react": "npm:18.3.25"
"@types/react-dom": "npm:18.3.7"
"@types/react-redux": "npm:7.1.33"
"@types/styled-components": "npm:5.1.32"
@@ -10518,7 +10526,7 @@ __metadata:
"@joplin/turndown": "npm:~4.0.80"
"@joplin/turndown-plugin-gfm": "npm:~1.0.62"
"@joplin/utils": "npm:~3.5"
"@js-draw/material-icons": "npm:1.32.0"
"@js-draw/material-icons": "npm:1.33.0"
"@pmmmwh/react-refresh-webpack-plugin": "npm:^0.6.0"
"@react-native-clipboard/clipboard": "npm:1.16.3"
"@react-native-community/cli": "npm:16.0.3"
@@ -10528,7 +10536,7 @@ __metadata:
"@react-native-community/geolocation": "npm:3.4.0"
"@react-native-community/netinfo": "npm:11.4.1"
"@react-native-community/push-notification-ios": "npm:1.11.0"
"@react-native-documents/picker": "npm:10.1.6"
"@react-native-documents/picker": "npm:10.1.7"
"@react-native-vector-icons/fontawesome5": "npm:12.3.0"
"@react-native-vector-icons/get-image": "npm:12.3.0"
"@react-native-vector-icons/ionicons": "npm:12.3.0"
@@ -10544,7 +10552,7 @@ __metadata:
"@types/node": "npm:18.19.130"
"@types/react": "npm:19.0.14"
"@types/react-redux": "npm:7.1.33"
"@types/serviceworker": "npm:0.0.153"
"@types/serviceworker": "npm:0.0.157"
"@types/tar-stream": "npm:3.1.4"
assert-browserify: "npm:2.0.0"
babel-jest: "npm:29.7.0"
@@ -10558,7 +10566,7 @@ __metadata:
deprecated-react-native-prop-types: "npm:5.0.0"
esbuild: "npm:0.25.10"
events: "npm:3.3.0"
expo: "npm:53.0.20"
expo: "npm:53.0.23"
expo-av: "npm:15.1.7"
expo-camera: "npm:16.1.11"
expo-local-authentication: "npm:16.0.5"
@@ -10568,7 +10576,7 @@ __metadata:
jest: "npm:29.7.0"
jest-environment-jsdom: "npm:29.7.0"
jetifier: "npm:2.0.0"
js-draw: "npm:1.32.0"
js-draw: "npm:1.33.0"
jsdom: "npm:26.1.0"
lodash: "npm:4.17.21"
md5: "npm:2.3.0"
@@ -10601,7 +10609,7 @@ __metadata:
react-native-url-polyfill: "npm:2.0.0"
react-native-version-info: "npm:1.1.1"
react-native-web: "npm:0.21.1"
react-native-webview: "npm:13.15.0"
react-native-webview: "npm:13.16.0"
react-native-zip-archive: "npm:7.0.2"
react-redux: "npm:8.1.3"
react-refresh: "npm:0.17.0"
@@ -10689,7 +10697,7 @@ __metadata:
"@testing-library/react-hooks": "npm:8.0.1"
"@types/jest": "npm:29.5.14"
"@types/node": "npm:18.19.130"
"@types/react": "npm:18.3.23"
"@types/react": "npm:18.3.25"
"@types/react-redux": "npm:7.1.33"
"@types/styled-components": "npm:5.1.32"
dompurify: "npm:3.2.7"
@@ -10800,7 +10808,7 @@ __metadata:
"@types/mustache": "npm:4.2.6"
"@types/node": "npm:18.19.130"
"@types/node-rsa": "npm:1.1.4"
"@types/react": "npm:18.3.23"
"@types/react": "npm:18.3.25"
"@types/uuid": "npm:10.0.0"
adm-zip: "npm:0.5.16"
async-mutex: "npm:0.5.0"
@@ -10882,7 +10890,7 @@ __metadata:
"@joplin/lib": "npm:~3.5"
"@types/jest": "npm:29.5.14"
"@types/pdfjs-dist": "npm:2.10.378"
"@types/react": "npm:18.3.23"
"@types/react": "npm:18.3.25"
"@types/react-dom": "npm:18.3.7"
"@types/styled-components": "npm:5.1.32"
async-mutex: "npm:0.5.0"
@@ -11023,7 +11031,7 @@ __metadata:
"@types/mustache": "npm:4.2.6"
"@types/node": "npm:18.19.130"
"@types/node-os-utils": "npm:1.3.4"
"@types/nodemailer": "npm:6.4.19"
"@types/nodemailer": "npm:6.4.20"
"@types/uuid": "npm:10.0.0"
"@types/yargs": "npm:17.0.33"
"@types/zxcvbn": "npm:4.4.5"
@@ -11104,7 +11112,7 @@ __metadata:
relative: "npm:3.0.2"
request: "npm:2.88.2"
rss: "npm:1.2.2"
sass: "npm:1.93.0"
sass: "npm:1.93.2"
sharp: "npm:0.34.4"
source-map-support: "npm:0.5.21"
sqlite3: "npm:5.1.6"
@@ -11163,7 +11171,7 @@ __metadata:
dependencies:
"@adobe/css-tools": "npm:4.4.4"
"@rollup/plugin-commonjs": "npm:28.0.6"
"@rollup/plugin-node-resolve": "npm:16.0.1"
"@rollup/plugin-node-resolve": "npm:16.0.2"
"@rollup/plugin-replace": "npm:6.0.2"
browserify: "npm:14.5.0"
html-entities: "npm:1.4.0"
@@ -11357,21 +11365,21 @@ __metadata:
languageName: node
linkType: hard
"@js-draw/material-icons@npm:1.32.0":
version: 1.32.0
resolution: "@js-draw/material-icons@npm:1.32.0"
"@js-draw/material-icons@npm:1.33.0":
version: 1.33.0
resolution: "@js-draw/material-icons@npm:1.33.0"
peerDependencies:
js-draw: ^1.0.1
checksum: 10/89f2b1cf13ccb2146743f13ea6c85e0f086a926eafeb22f5fef7c1622826181a0c0f59dd571e5975a67eb3407f2388b21e32218dad4eafef43cc2d1c4d059337
checksum: 10/7f3ac9ee3b6a1d575abe45e93ff7a2b9f2f2e5fa6d73e67a5a1f4a0be7a03239543d007bf42db6de619fcef68e016ac47a00f053efee2dee0d005177f6cb0733
languageName: node
linkType: hard
"@js-draw/math@npm:^1.32.0":
version: 1.32.0
resolution: "@js-draw/math@npm:1.32.0"
"@js-draw/math@npm:^1.33.0":
version: 1.33.0
resolution: "@js-draw/math@npm:1.33.0"
dependencies:
bezier-js: "npm:6.1.3"
checksum: 10/3fd9d9fb8fb7ae231851e43eb2503d03fd963f665ecdebd99bf74feef24086eee07680d431f413b9b1a72ecd6c3b46ab539d8116bc3a10f1f3cfaefb168a28e7
checksum: 10/8772e7a2392d9e1e27276777ca47ef3e31a407aa2a23efc2b914eb8963467e340b2031998571095c9309c0724fe0858b775fe939727cae9d013abcd819f49a5d
languageName: node
linkType: hard
@@ -13081,14 +13089,14 @@ __metadata:
languageName: node
linkType: hard
"@playwright/test@npm:1.55.0":
version: 1.55.0
resolution: "@playwright/test@npm:1.55.0"
"@playwright/test@npm:1.55.1":
version: 1.55.1
resolution: "@playwright/test@npm:1.55.1"
dependencies:
playwright: "npm:1.55.0"
playwright: "npm:1.55.1"
bin:
playwright: cli.js
checksum: 10/bfaede669b0583ee6ec48bfe9ff531cac58da1b83031907d5d0fd71c33bab6766669a23359e401066d97c8e8fa76566900f71be7b290d12213ebb3530a82afbb
checksum: 10/c67a46353c58aaeac551bce2654cdef0e9a0ad76b1667514832d34acd4b26ec72f35ea7595cd3fad4c4e1e039d5bb876b8d62c89af4525d455285f6fff9f0642
languageName: node
linkType: hard
@@ -13585,13 +13593,13 @@ __metadata:
languageName: node
linkType: hard
"@react-native-documents/picker@npm:10.1.6":
version: 10.1.6
resolution: "@react-native-documents/picker@npm:10.1.6"
"@react-native-documents/picker@npm:10.1.7":
version: 10.1.7
resolution: "@react-native-documents/picker@npm:10.1.7"
peerDependencies:
react: "*"
react-native: "*"
checksum: 10/0b1834bcf74ce6793ca3cbcb2d7bc9370fedc584df8fae7f1da23928b79dfa985b7a109fba542e348e39518562f4c94acf15cbf91c79a6570ac672920ee4af14
checksum: 10/b9f629501d80e1047305016dc9233d2696c476dc85e106b1becc3ec123f9eaba05b1ed8da16603c06a7264887aab367a9dff0fb3a5c93051d446f6553a1948cc
languageName: node
linkType: hard
@@ -13697,6 +13705,16 @@ __metadata:
languageName: node
linkType: hard
"@react-native/babel-plugin-codegen@npm:0.79.6":
version: 0.79.6
resolution: "@react-native/babel-plugin-codegen@npm:0.79.6"
dependencies:
"@babel/traverse": "npm:^7.25.3"
"@react-native/codegen": "npm:0.79.6"
checksum: 10/80ee3ef484d1d98fee0fdf6a770008767d883b2a5cf77628a664095c0aa15ad10380cc227c33dd474c39dc283b4aef5953a0c8079b2caa923c946c6978d0d27b
languageName: node
linkType: hard
"@react-native/babel-plugin-codegen@npm:0.80.1":
version: 0.80.1
resolution: "@react-native/babel-plugin-codegen@npm:0.80.1"
@@ -13762,6 +13780,61 @@ __metadata:
languageName: node
linkType: hard
"@react-native/babel-preset@npm:0.79.6":
version: 0.79.6
resolution: "@react-native/babel-preset@npm:0.79.6"
dependencies:
"@babel/core": "npm:^7.25.2"
"@babel/plugin-proposal-export-default-from": "npm:^7.24.7"
"@babel/plugin-syntax-dynamic-import": "npm:^7.8.3"
"@babel/plugin-syntax-export-default-from": "npm:^7.24.7"
"@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3"
"@babel/plugin-syntax-optional-chaining": "npm:^7.8.3"
"@babel/plugin-transform-arrow-functions": "npm:^7.24.7"
"@babel/plugin-transform-async-generator-functions": "npm:^7.25.4"
"@babel/plugin-transform-async-to-generator": "npm:^7.24.7"
"@babel/plugin-transform-block-scoping": "npm:^7.25.0"
"@babel/plugin-transform-class-properties": "npm:^7.25.4"
"@babel/plugin-transform-classes": "npm:^7.25.4"
"@babel/plugin-transform-computed-properties": "npm:^7.24.7"
"@babel/plugin-transform-destructuring": "npm:^7.24.8"
"@babel/plugin-transform-flow-strip-types": "npm:^7.25.2"
"@babel/plugin-transform-for-of": "npm:^7.24.7"
"@babel/plugin-transform-function-name": "npm:^7.25.1"
"@babel/plugin-transform-literals": "npm:^7.25.2"
"@babel/plugin-transform-logical-assignment-operators": "npm:^7.24.7"
"@babel/plugin-transform-modules-commonjs": "npm:^7.24.8"
"@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.24.7"
"@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.24.7"
"@babel/plugin-transform-numeric-separator": "npm:^7.24.7"
"@babel/plugin-transform-object-rest-spread": "npm:^7.24.7"
"@babel/plugin-transform-optional-catch-binding": "npm:^7.24.7"
"@babel/plugin-transform-optional-chaining": "npm:^7.24.8"
"@babel/plugin-transform-parameters": "npm:^7.24.7"
"@babel/plugin-transform-private-methods": "npm:^7.24.7"
"@babel/plugin-transform-private-property-in-object": "npm:^7.24.7"
"@babel/plugin-transform-react-display-name": "npm:^7.24.7"
"@babel/plugin-transform-react-jsx": "npm:^7.25.2"
"@babel/plugin-transform-react-jsx-self": "npm:^7.24.7"
"@babel/plugin-transform-react-jsx-source": "npm:^7.24.7"
"@babel/plugin-transform-regenerator": "npm:^7.24.7"
"@babel/plugin-transform-runtime": "npm:^7.24.7"
"@babel/plugin-transform-shorthand-properties": "npm:^7.24.7"
"@babel/plugin-transform-spread": "npm:^7.24.7"
"@babel/plugin-transform-sticky-regex": "npm:^7.24.7"
"@babel/plugin-transform-typescript": "npm:^7.25.2"
"@babel/plugin-transform-unicode-regex": "npm:^7.24.7"
"@babel/template": "npm:^7.25.0"
"@react-native/babel-plugin-codegen": "npm:0.79.6"
babel-plugin-syntax-hermes-parser: "npm:0.25.1"
babel-plugin-transform-flow-enums: "npm:^0.0.2"
react-refresh: "npm:^0.14.0"
peerDependencies:
"@babel/core": "*"
checksum: 10/dd9d69cf68ee2e52d4c1c3d23ff69087b86a6daf885f83bef220cb881512868e9eee1920571de43d3844757b23b0ca8b3459f38bee8c71c58b6d6029bc31483f
languageName: node
linkType: hard
"@react-native/babel-preset@npm:0.80.1":
version: 0.80.1
resolution: "@react-native/babel-preset@npm:0.80.1"
@@ -13847,6 +13920,23 @@ __metadata:
languageName: node
linkType: hard
"@react-native/codegen@npm:0.79.6":
version: 0.79.6
resolution: "@react-native/codegen@npm:0.79.6"
dependencies:
"@babel/core": "npm:^7.25.2"
"@babel/parser": "npm:^7.25.3"
glob: "npm:^7.1.1"
hermes-parser: "npm:0.25.1"
invariant: "npm:^2.2.4"
nullthrows: "npm:^1.1.1"
yargs: "npm:^17.6.2"
peerDependencies:
"@babel/core": "*"
checksum: 10/24c4461d6d9b8db1fc540e074d8948f487e710e86903344f9fab0a39f1ddcdab2b674c88adb82c95cfd3c41f5ae5b8703713aca5c1037d3297a3178cb400fe84
languageName: node
linkType: hard
"@react-native/codegen@npm:0.80.1":
version: 0.80.1
resolution: "@react-native/codegen@npm:0.80.1"
@@ -13890,10 +13980,10 @@ __metadata:
languageName: node
linkType: hard
"@react-native/debugger-frontend@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/debugger-frontend@npm:0.79.5"
checksum: 10/d9556110ad0e59b0d800fb159bfb1b118056f3176c5940a242a058e8deee2c2f0eeb47b3d7ab474127b37e3caebe07738cd0022bfa9c4865b7e42336a4f52c53
"@react-native/debugger-frontend@npm:0.79.6":
version: 0.79.6
resolution: "@react-native/debugger-frontend@npm:0.79.6"
checksum: 10/fca04379d556a62367628f3ec8478bd0eceeef34d6f7e434e5285edba6d8f7d1b833ec62ff8b96a3444d55da569cdd9c36690439736b263898478e23a2b7fe12
languageName: node
linkType: hard
@@ -13916,12 +14006,12 @@ __metadata:
languageName: node
linkType: hard
"@react-native/dev-middleware@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/dev-middleware@npm:0.79.5"
"@react-native/dev-middleware@npm:0.79.6":
version: 0.79.6
resolution: "@react-native/dev-middleware@npm:0.79.6"
dependencies:
"@isaacs/ttlcache": "npm:^1.4.1"
"@react-native/debugger-frontend": "npm:0.79.5"
"@react-native/debugger-frontend": "npm:0.79.6"
chrome-launcher: "npm:^0.15.2"
chromium-edge-launcher: "npm:^0.2.0"
connect: "npm:^3.6.5"
@@ -13931,7 +14021,7 @@ __metadata:
open: "npm:^7.0.3"
serve-static: "npm:^1.16.2"
ws: "npm:^6.2.3"
checksum: 10/baa7b37711211aec9537725de77d082f5f9ec777107f3e7a7e93a67aa07b1da79f851f37dacc65bec3d012df4d352941e02fc1153ad19602258b641c78318065
checksum: 10/e1073a149e054bdc10028e65505d9f9db8bf47d9902a37f2a1afd283d9607a70e605bc20cd4c1ea445061b6840c28730edb500833e08e5e601c076e48c9a7274
languageName: node
linkType: hard
@@ -13996,10 +14086,10 @@ __metadata:
languageName: node
linkType: hard
"@react-native/normalize-colors@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/normalize-colors@npm:0.79.5"
checksum: 10/c2ec87cacba9439f17dede82befd9c9bd4a8b0ed2fdd508765609119e233bed75cf9097364f9e39633686d98061bab9577d08b2c93956e93a99210a49ca9022a
"@react-native/normalize-colors@npm:0.79.6":
version: 0.79.6
resolution: "@react-native/normalize-colors@npm:0.79.6"
checksum: 10/e8596238ddf6f666cb336cd410616f2222f513022ea37fd908826161cf554d00c5d43eb8b50e8a2f7000b9f73ee99c37e9813261cad4b896f7a74f26aaed0ebe
languageName: node
linkType: hard
@@ -14115,9 +14205,9 @@ __metadata:
languageName: node
linkType: hard
"@rollup/plugin-node-resolve@npm:16.0.1":
version: 16.0.1
resolution: "@rollup/plugin-node-resolve@npm:16.0.1"
"@rollup/plugin-node-resolve@npm:16.0.2":
version: 16.0.2
resolution: "@rollup/plugin-node-resolve@npm:16.0.2"
dependencies:
"@rollup/pluginutils": "npm:^5.0.1"
"@types/resolve": "npm:1.20.2"
@@ -14129,7 +14219,7 @@ __metadata:
peerDependenciesMeta:
rollup:
optional: true
checksum: 10/88fee8c003a5730cca2c06edd200ec6a46c7ab28bed3a99aea6d3070f34f980f575fcbea906946579e41b0be6fd7a2fbc24cdf0ca24f172a555f130726915d8b
checksum: 10/79ea6cd8fe474161914bc9bf1618787a75378f9e52fb70ffe698322140e8100a0b706198ba5cc615eabf774e78979542df2ad6bc1fe0ad5e820e20f2537de48c
languageName: node
linkType: hard
@@ -16692,13 +16782,13 @@ __metadata:
languageName: node
linkType: hard
"@types/nodemailer@npm:6.4.19":
version: 6.4.19
resolution: "@types/nodemailer@npm:6.4.19"
"@types/nodemailer@npm:6.4.20":
version: 6.4.20
resolution: "@types/nodemailer@npm:6.4.20"
dependencies:
"@aws-sdk/client-ses": "npm:^3.731.1"
"@types/node": "npm:*"
checksum: 10/f903e32f3d80e64fc101275ea8b244ee1afb95640facdbd031eeb70d658941e8871a54b4c0918593a5e90eb5433fd65dea7924a7caa47898c8a3b08dcb856ed4
checksum: 10/050b6aa95c97a1bf645f0735c5da20a5b4aa30df1fc8bcc8a60c039b5f45f450689e5f51821d144d038148f3dae8427bb7d91f503c6390960a1390e562147a4f
languageName: node
linkType: hard
@@ -16854,13 +16944,13 @@ __metadata:
languageName: node
linkType: hard
"@types/react@npm:18.3.23":
version: 18.3.23
resolution: "@types/react@npm:18.3.23"
"@types/react@npm:18.3.25":
version: 18.3.25
resolution: "@types/react@npm:18.3.25"
dependencies:
"@types/prop-types": "npm:*"
csstype: "npm:^3.0.2"
checksum: 10/4b965dffe34a1f8aac8e2d7e976f113373f38134f9e37239f7e75d7ac6b3c2e1333a8df21febf1fe7749640f8de5708f7668cdfc70bffebda1cc4d3346724fd5
checksum: 10/1b806cc90207558bd96384f2a74e8a457c6211bd71b22d74ed7efb745e4e1b4fe7aea59eb906b8cf96571f9e0ab4fdb89e00395a082a361cfd7a8baf50bcb9c1
languageName: node
linkType: hard
@@ -17004,10 +17094,10 @@ __metadata:
languageName: node
linkType: hard
"@types/serviceworker@npm:0.0.153":
version: 0.0.153
resolution: "@types/serviceworker@npm:0.0.153"
checksum: 10/d5a0f9d638ebc1a0cbc9292d9a6146b940ac6d32e32b88fcf5e5bd2cd02c343c78ec7c0f9fb4340e3b3762dd5c48a3b54ea546520d31dc7ca319e95c9540a029
"@types/serviceworker@npm:0.0.157":
version: 0.0.157
resolution: "@types/serviceworker@npm:0.0.157"
checksum: 10/47fd9ff27c075c1d93077de5a771e690da45e30f2f89a0b0f53628f6e7b34ba4f48fdb64ab52b4bef27ed75788d8161175cef16244f3df14e6aa7c863a1913b2
languageName: node
linkType: hard
@@ -20223,9 +20313,9 @@ __metadata:
languageName: node
linkType: hard
"babel-preset-expo@npm:~13.2.3":
version: 13.2.3
resolution: "babel-preset-expo@npm:13.2.3"
"babel-preset-expo@npm:~13.2.4":
version: 13.2.4
resolution: "babel-preset-expo@npm:13.2.4"
dependencies:
"@babel/helper-module-imports": "npm:^7.25.9"
"@babel/plugin-proposal-decorators": "npm:^7.12.9"
@@ -20241,7 +20331,7 @@ __metadata:
"@babel/plugin-transform-runtime": "npm:^7.24.7"
"@babel/preset-react": "npm:^7.22.15"
"@babel/preset-typescript": "npm:^7.23.0"
"@react-native/babel-preset": "npm:0.79.5"
"@react-native/babel-preset": "npm:0.79.6"
babel-plugin-react-native-web: "npm:~0.19.13"
babel-plugin-syntax-hermes-parser: "npm:^0.25.1"
babel-plugin-transform-flow-enums: "npm:^0.0.2"
@@ -20253,7 +20343,7 @@ __metadata:
peerDependenciesMeta:
babel-plugin-react-compiler:
optional: true
checksum: 10/9081ab23348dc94cf5c2d89a01cab7c591fe64fc6520f770c90112202fc6ecd4bef7526bbf3c083c2341e00617ea6c6d9ae23813103905159dd1c9a03f5b7f49
checksum: 10/645501ff71738f9fbf34559a135f36b3ed6539f8ba88f204c5e72948e621f1216c8a4bbd0775137e7763ba93d5ecae9d9031e797f71ee69d88e8ace5c15e47fe
languageName: node
linkType: hard
@@ -28354,18 +28444,18 @@ __metadata:
languageName: node
linkType: hard
"expo@npm:53.0.20":
version: 53.0.20
resolution: "expo@npm:53.0.20"
"expo@npm:53.0.23":
version: 53.0.23
resolution: "expo@npm:53.0.23"
dependencies:
"@babel/runtime": "npm:^7.20.0"
"@expo/cli": "npm:0.24.20"
"@expo/cli": "npm:0.24.22"
"@expo/config": "npm:~11.0.13"
"@expo/config-plugins": "npm:~10.1.2"
"@expo/fingerprint": "npm:0.13.4"
"@expo/metro-config": "npm:0.20.17"
"@expo/vector-icons": "npm:^14.0.0"
babel-preset-expo: "npm:~13.2.3"
babel-preset-expo: "npm:~13.2.4"
expo-asset: "npm:~11.1.7"
expo-constants: "npm:~17.1.7"
expo-file-system: "npm:~18.1.11"
@@ -28392,7 +28482,7 @@ __metadata:
expo: bin/cli
expo-modules-autolinking: bin/autolinking
fingerprint: bin/fingerprint
checksum: 10/73cb5ad2e3eb8927390ff685b3f53b0a496fdaa99ec73f6cc3faa732534ad5ec0b5378abdf3da2d0c692397b758eadeed1eabad7f412f0b7576c29f2518e6f93
checksum: 10/7382f1895119304ce610e8f145adbb4a4b298043dec74308be3af2979d6d08c882185752937c7c3fce19aef982d1a67d3736858c47c977abca90566dabd6d188
languageName: node
linkType: hard
@@ -34904,13 +34994,13 @@ __metadata:
languageName: node
linkType: hard
"js-draw@npm:1.32.0":
version: 1.32.0
resolution: "js-draw@npm:1.32.0"
"js-draw@npm:1.33.0":
version: 1.33.0
resolution: "js-draw@npm:1.33.0"
dependencies:
"@js-draw/math": "npm:^1.32.0"
"@js-draw/math": "npm:^1.33.0"
"@melloware/coloris": "npm:0.22.0"
checksum: 10/3da563efc659e5081fa102bb570791581edcddedc40b2ba60596b7edd3dfe5dcd96b39a3486f266f6489a0ace4a335f5ee54d4aa726cab13e90d3d38a415a23a
checksum: 10/1ea87975079b6c5ba2cfaa366e4d5b6d921f118e162a51706053f481c2969eff0b896343782c2a05405fdfa874fc92543a10e4bcd2d224a81d753c59d0e3a43b
languageName: node
linkType: hard
@@ -41789,27 +41879,27 @@ __metadata:
languageName: node
linkType: hard
"playwright-core@npm:1.55.0":
version: 1.55.0
resolution: "playwright-core@npm:1.55.0"
"playwright-core@npm:1.55.1":
version: 1.55.1
resolution: "playwright-core@npm:1.55.1"
bin:
playwright-core: cli.js
checksum: 10/843376a8e2c1100d3f0625b2c85a69dc4418a0b268bdc7da6cf28252f6dc50c299c734f090a67c7f36cfa9140dacf5b95e817664e69642bcbaf06eea4d216c35
checksum: 10/953a43039dbcca04513bd3138a9dee249a136d5377da00d49402ffcd24d33ca84dc1dc04636d1b76e9f8c9fd28a302b89cda1ae544d72b5d829c28e623bfcb0b
languageName: node
linkType: hard
"playwright@npm:1.55.0":
version: 1.55.0
resolution: "playwright@npm:1.55.0"
"playwright@npm:1.55.1":
version: 1.55.1
resolution: "playwright@npm:1.55.1"
dependencies:
fsevents: "npm:2.3.2"
playwright-core: "npm:1.55.0"
playwright-core: "npm:1.55.1"
dependenciesMeta:
fsevents:
optional: true
bin:
playwright: cli.js
checksum: 10/df02529693923b9b671822dac8a2ac8a8471c9142a05e14a24f8d0038a7dcf705712363991a070fc927466bbbe70e485145cf7a77882613822f56960afc4b892
checksum: 10/5dcf9ce564cacf6c06ebc864bb2b1f709c641792560d49889ed4c98e230be54a963ec8aaafff11269735d8d22da4900bd2d4ef9f1748d132326ffda8fb1f3f20
languageName: node
linkType: hard
@@ -44015,16 +44105,16 @@ __metadata:
languageName: node
linkType: hard
"react-native-webview@npm:13.15.0":
version: 13.15.0
resolution: "react-native-webview@npm:13.15.0"
"react-native-webview@npm:13.16.0":
version: 13.16.0
resolution: "react-native-webview@npm:13.16.0"
dependencies:
escape-string-regexp: "npm:^4.0.0"
invariant: "npm:2.2.4"
peerDependencies:
react: "*"
react-native: "*"
checksum: 10/7d4b23cb0536199ab2339644d8b6ce720e794ac9a8200f23746032a768a61af8727d2401fc000be09b4fb478dce6f795d7d400945a17ab013a3bbc92a5077e4e
checksum: 10/dc3d84c4785676bac3f316ec2119d3239f64fd8b18400dcc1cf8fa4f05b9bf677cf167aa4e7da72c9f60760eb6303c99da22324e15ff5fb380a3b1b31f531132
languageName: node
linkType: hard
@@ -46244,9 +46334,9 @@ __metadata:
languageName: node
linkType: hard
"sass@npm:1.93.0":
version: 1.93.0
resolution: "sass@npm:1.93.0"
"sass@npm:1.93.2":
version: 1.93.2
resolution: "sass@npm:1.93.2"
dependencies:
"@parcel/watcher": "npm:^2.4.1"
chokidar: "npm:^4.0.0"
@@ -46257,7 +46347,7 @@ __metadata:
optional: true
bin:
sass: sass.js
checksum: 10/37f22e016e1d5ad04754123009064260c1f568771688742a2b34c763d7c30b706e8c991dffb578ca6a1ecf598ea57e7392597719786eff5eb47b099b7dbf03cd
checksum: 10/2fc0dcafdf3050f4131650a00ddd772790af1dc3e31a97c6533f323efabddce81bd139a920f13a88af80d630a255f51ee91067a7478364b2a13fcc6f5f7e8308
languageName: node
linkType: hard