You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-12-29 23:48:19 +02:00
Compare commits
107 Commits
cli-v3.5.1
...
join_serve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c44aad544e | ||
|
|
996a0894ae | ||
|
|
66fa3fc808 | ||
|
|
dab55daf95 | ||
|
|
7f1c31e03f | ||
|
|
0a8255f091 | ||
|
|
9f3e6650a9 | ||
|
|
4a17da3df5 | ||
|
|
2c4f0d4d8c | ||
|
|
9c1c2fb0d4 | ||
|
|
2332e4bf62 | ||
|
|
a488ac1b27 | ||
|
|
6daa41ca66 | ||
|
|
cc9517f1a2 | ||
|
|
200a471e55 | ||
|
|
c21d37bd91 | ||
|
|
e36cd0e60b | ||
|
|
871f55bf11 | ||
|
|
22c9fed663 | ||
|
|
ea362d7a82 | ||
|
|
9ae9347f89 | ||
|
|
ae8bb902f9 | ||
|
|
90eeec23de | ||
|
|
474fd094c4 | ||
|
|
937d8fa4f7 | ||
|
|
45c9844616 | ||
|
|
12b8ef5a54 | ||
|
|
18f72c224e | ||
|
|
7ca3aaa83f | ||
|
|
04b1443e5a | ||
|
|
c461741778 | ||
|
|
2865b0a803 | ||
|
|
21e49be22f | ||
|
|
fef761cbab | ||
|
|
c15a353dc2 | ||
|
|
ffb32766c1 | ||
|
|
038908550e | ||
|
|
42f59134ae | ||
|
|
fc0014c0b5 | ||
|
|
42d8df3036 | ||
|
|
1fad9ca1cc | ||
|
|
ae289be77a | ||
|
|
7f6bfe9c6e | ||
|
|
ead4001b7a | ||
|
|
7b95ef72a0 | ||
|
|
a4556bf598 | ||
|
|
8d6268dc92 | ||
|
|
7ffcbdf60a | ||
|
|
76989ddc45 | ||
|
|
1db1254617 | ||
|
|
9810bffddc | ||
|
|
b25e18107b | ||
|
|
edc5fe5d1b | ||
|
|
7ffb44b3a4 | ||
|
|
32f4c33140 | ||
|
|
1a7b09c91c | ||
|
|
e5bf8e0e58 | ||
|
|
94725c533c | ||
|
|
359c92b64f | ||
|
|
8f8b8ad943 | ||
|
|
dd2f329fd5 | ||
|
|
813f594cb4 | ||
|
|
0e0ce49867 | ||
|
|
e485d318b7 | ||
|
|
4e82d81df1 | ||
|
|
d5dbda201b | ||
|
|
831258506b | ||
|
|
67f3329ecb | ||
|
|
ed7e6751f0 | ||
|
|
35e69486d3 | ||
|
|
918c8830e0 | ||
|
|
c3b4a4b955 | ||
|
|
44a14fabbd | ||
|
|
49399cd1fa | ||
|
|
fc4cd2e942 | ||
|
|
cd6e457dc5 | ||
|
|
2e9bf3a4e5 | ||
|
|
547ceea4b0 | ||
|
|
776ff5e7ea | ||
|
|
2b3bac0d43 | ||
|
|
e48efe2e8d | ||
|
|
5f6382fbc0 | ||
|
|
3d5d82081a | ||
|
|
cff96b1306 | ||
|
|
98c5a9c096 | ||
|
|
e92430b3ed | ||
|
|
848d1bfe64 | ||
|
|
a386283530 | ||
|
|
6101031269 | ||
|
|
2fc3431f46 | ||
|
|
361fa2c768 | ||
|
|
f4a0a2466b | ||
|
|
dbf225d6ad | ||
|
|
4773a3831c | ||
|
|
6a19690581 | ||
|
|
b7a771d58d | ||
|
|
e3daefb81a | ||
|
|
b4253dace8 | ||
|
|
fcf3be1be1 | ||
|
|
99aebbad81 | ||
|
|
81b695a2a9 | ||
|
|
2dbba27357 | ||
|
|
97fa85a3f7 | ||
|
|
defe36bba1 | ||
|
|
711d214741 | ||
|
|
0795c67354 | ||
|
|
e9a9f68568 |
@@ -701,6 +701,7 @@ packages/app-mobile/components/NoteEditor/ImageEditor/ImageEditor.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/autosave.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/isEditableResource.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/promptRestoreAutosave.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownEditor.test.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownEditor.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.test.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.js
|
||||
@@ -1643,6 +1644,7 @@ packages/lib/services/synchronizer/Synchronizer.sharing.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.tags.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.tools.test.js
|
||||
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
|
||||
packages/lib/services/synchronizer/handleConflictAction.test.js
|
||||
packages/lib/services/synchronizer/migrations/1.js
|
||||
packages/lib/services/synchronizer/migrations/2.js
|
||||
packages/lib/services/synchronizer/migrations/3.js
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -674,6 +674,7 @@ packages/app-mobile/components/NoteEditor/ImageEditor/ImageEditor.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/autosave.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/isEditableResource.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/promptRestoreAutosave.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownEditor.test.js
|
||||
packages/app-mobile/components/NoteEditor/MarkdownEditor.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.test.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.js
|
||||
@@ -1616,6 +1617,7 @@ packages/lib/services/synchronizer/Synchronizer.sharing.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.tags.test.js
|
||||
packages/lib/services/synchronizer/Synchronizer.tools.test.js
|
||||
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
|
||||
packages/lib/services/synchronizer/handleConflictAction.test.js
|
||||
packages/lib/services/synchronizer/migrations/1.js
|
||||
packages/lib/services/synchronizer/migrations/2.js
|
||||
packages/lib/services/synchronizer/migrations/3.js
|
||||
|
||||
@@ -31,7 +31,7 @@ Please see the [donation page](https://github.com/laurent22/joplin/blob/dev/read
|
||||
# Sponsors
|
||||
|
||||
<!-- SPONSORS-ORG -->
|
||||
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://citricsheep.com"><img title="Citric Sheep" width="256" src="https://joplinapp.org/images/sponsors/CitricSheep.png"/></a> <a href="https://sorted.travel/?utm_source=joplinapp"><img title="Sorted Travel" width="256" src="https://joplinapp.org/images/sponsors/SortedTravel.png"/></a> <a href="https://celebian.com"><img title="Celebian" width="256" src="https://joplinapp.org/images/sponsors/Celebian.png"/></a> <a href="https://bestkru.com"><img title="BestKru" width="256" src="https://joplinapp.org/images/sponsors/BestKru.png"/></a> <a href="https://www.socialfollowers.uk/buy-tiktok-followers/"><img title="Social Followers" width="256" src="https://joplinapp.org/images/sponsors/SocialFollowers.png"/></a> <a href="https://stormlikes.com/"><img title="Stormlikes" width="256" src="https://joplinapp.org/images/sponsors/Stormlikes.png"/></a> <a href="https://route4me.com"><img title="Route4Me" width="256" src="https://joplinapp.org/images/sponsors/Route4Me.png"/></a> <a href="https://topagency.webflow.io"><img title="WebDesignAgency" width="256" src="https://joplinapp.org/images/sponsors/WebDesignAgency.png" alt="topagency"/></a> <a href="https://www.slotozilla.com/nz/no-deposit-bonus"><img title="casino without making any upfront cost" width="256" src="https://joplinapp.org/images/sponsors/Slotozilla.png" alt="casino without making any upfront cost"/></a> <a href="https://writepaper.com/"><img title="best service to write my paper for me" width="256" src="https://joplinapp.org/images/sponsors/WritePaper.png" alt="best service to write my paper for me"/></a> <a href="https://paperwriter.com/"><img title="high-quality paper writing service PaperWriter" width="256" src="https://joplinapp.org/images/sponsors/PaperWriter.png" alt="high-quality paper writing service PaperWriter"/></a> <a href="https://www.bestetf.net/"><img title="BestETF" width="256" src="https://joplinapp.org/images/sponsors/BestEtf.png" alt="BestETF"/></a> <a href="https://freespinny.io/free-spins-no-deposit/"><img title="Freespinny.io Free Spins Bonus site" width="256" src="https://joplinapp.org/images/sponsors/Freespinny.png" alt="Freespinny.io Free Spins Bonus site"/></a> <a href="https://essayshark.com"><img title="EssayShark - essay writers for hire" width="256" src="https://joplinapp.org/images/sponsors/EssayShark.png" alt="EssayShark - essay writers for hire"/></a> <a href="https://pokieslab1.com/real-money-pokies/"><img title="Australian Real Money Pokies" width="256" src="https://joplinapp.org/images/sponsors/PokiesLab.png" alt="Australian Real Money Pokies"/></a> <a href="https://pokiesman1.net/real-money-pokies/"><img title="Australian Real Money Pokies" width="256" src="https://joplinapp.org/images/sponsors/Pokiesman.png" alt="Australian Real Money Pokies"/></a> <a href="https://domyessay.com"><img title="Essay writers DoMyEssay are dedicated to providing top-notch, custom-written papers that meet your academic requirements" width="256" src="https://joplinapp.org/images/sponsors/DoMyEssay.png" alt="DoMyEssay"/></a> <a href="https://essaypro.com/"><img title="best essay writing service" width="256" src="https://joplinapp.org/images/sponsors/EssayPro.png" alt="best essay writing service"/></a> <a href="https://socialkings.online"><img title="Boost your reach and buy real followers" width="256" src="https://joplinapp.org/images/sponsors/SocialKings.png" alt="Boost your reach and buy real followers"/></a> <a href="https://uk.notgamstop.com/bonuses/free-spins-no-deposit-no-gamstop/"><img title="free spins no deposit at NotGamstop" width="256" src="https://joplinapp.org/images/sponsors/NotGamStop.jpg" alt="free spins no deposit at NotGamstop"/></a> <a href="https://www.writemyessay.com/"><img title="writing service for students WriteMyEssay" width="256" src="https://joplinapp.org/images/sponsors/WriteMyEssay.png" alt="writing service for students WriteMyEssay"/></a>
|
||||
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://citricsheep.com"><img title="Citric Sheep" width="256" src="https://joplinapp.org/images/sponsors/CitricSheep.png"/></a> <a href="https://sorted.travel/?utm_source=joplinapp"><img title="Sorted Travel" width="256" src="https://joplinapp.org/images/sponsors/SortedTravel.png"/></a> <a href="https://celebian.com"><img title="Celebian" width="256" src="https://joplinapp.org/images/sponsors/Celebian.png"/></a> <a href="https://bestkru.com"><img title="BestKru" width="256" src="https://joplinapp.org/images/sponsors/BestKru.png"/></a> <a href="https://www.socialfollowers.uk/buy-tiktok-followers/"><img title="Social Followers" width="256" src="https://joplinapp.org/images/sponsors/SocialFollowers.png"/></a> <a href="https://stormlikes.com/"><img title="Stormlikes" width="256" src="https://joplinapp.org/images/sponsors/Stormlikes.png"/></a> <a href="https://route4me.com"><img title="Route4Me" width="256" src="https://joplinapp.org/images/sponsors/Route4Me.png"/></a> <a href="https://topagency.webflow.io"><img title="WebDesignAgency" width="256" src="https://joplinapp.org/images/sponsors/WebDesignAgency.png" alt="topagency"/></a> <a href="https://www.slotozilla.com/nz/no-deposit-bonus"><img title="casino without making any upfront cost" width="256" src="https://joplinapp.org/images/sponsors/Slotozilla.png" alt="casino without making any upfront cost"/></a> <a href="https://writepaper.com/"><img title="best service to write my paper for me" width="256" src="https://joplinapp.org/images/sponsors/WritePaper.png" alt="best service to write my paper for me"/></a> <a href="https://paperwriter.com/"><img title="high-quality paper writing service PaperWriter" width="256" src="https://joplinapp.org/images/sponsors/PaperWriter.png" alt="high-quality paper writing service PaperWriter"/></a> <a href="https://www.bestetf.net/"><img title="BestETF" width="256" src="https://joplinapp.org/images/sponsors/BestEtf.png" alt="BestETF"/></a> <a href="https://freespinny.io/free-spins-no-deposit/"><img title="Freespinny.io Free Spins Bonus site" width="256" src="https://joplinapp.org/images/sponsors/Freespinny.png" alt="Freespinny.io Free Spins Bonus site"/></a> <a href="https://essayshark.com"><img title="EssayShark - essay writers for hire" width="256" src="https://joplinapp.org/images/sponsors/EssayShark.png" alt="EssayShark - essay writers for hire"/></a> <a href="https://pokieslab1.com/real-money-pokies/"><img title="Australian Real Money Pokies" width="256" src="https://joplinapp.org/images/sponsors/PokiesLab.png" alt="Australian Real Money Pokies"/></a> <a href="https://pokiesman1.net/real-money-pokies/"><img title="Australian Real Money Pokies" width="256" src="https://joplinapp.org/images/sponsors/Pokiesman.png" alt="Australian Real Money Pokies"/></a> <a href="https://domyessay.com"><img title="Essay writers DoMyEssay are dedicated to providing top-notch, custom-written papers that meet your academic requirements" width="256" src="https://joplinapp.org/images/sponsors/DoMyEssay.png" alt="DoMyEssay"/></a> <a href="https://essaypro.com/"><img title="best essay writing service" width="256" src="https://joplinapp.org/images/sponsors/EssayPro.png" alt="best essay writing service"/></a> <a href="https://socialkings.online"><img title="Boost your reach and buy real followers" width="256" src="https://joplinapp.org/images/sponsors/SocialKings.png" alt="Boost your reach and buy real followers"/></a> <a href="https://uk.notgamstop.com/bonuses/free-spins-no-deposit-no-gamstop/"><img title="free spins no deposit at NotGamstop" width="256" src="https://joplinapp.org/images/sponsors/NotGamStop.jpg" alt="free spins no deposit at NotGamstop"/></a> <a href="https://www.writemyessay.com/"><img title="writing service for students WriteMyEssay" width="256" src="https://joplinapp.org/images/sponsors/WriteMyEssay.png" alt="writing service for students WriteMyEssay"/></a> <a href="https://essayservice.com/"><img title="For those in need of immediate academic assistance, EssayService offers a fast and reliable service to write my essay for me now, ensuring high-quality results within tight deadlines" width="256" src="https://joplinapp.org/images/sponsors/EssayService.png" alt="For those in need of immediate academic assistance, EssayService offers a fast and reliable service to write my essay for me now, ensuring high-quality results within tight deadlines"/></a>
|
||||
<!-- SPONSORS-ORG -->
|
||||
|
||||
* * *
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
"cspell": "5.21.2",
|
||||
"eslint": "8.57.1",
|
||||
"eslint-interactive": "10.8.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
"eslint-plugin-jest": "27.9.0",
|
||||
"eslint-plugin-promise": "6.6.0",
|
||||
"eslint-plugin-react": "7.37.5",
|
||||
|
||||
Binary file not shown.
@@ -7,6 +7,7 @@ import useKeyboardHandler from './DialogButtonRow/useKeyboardHandler';
|
||||
export interface ButtonSpec {
|
||||
name: string;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface ClickEvent {
|
||||
@@ -51,21 +52,29 @@ export default function DialogButtonRow(props: Props) {
|
||||
if (props.onClick) props.onClick(event);
|
||||
}, [props.onClick]);
|
||||
|
||||
const onKeyDown = useKeyboardHandler({ onOkButtonClick, onCancelButtonClick });
|
||||
const okButtonShow = props.okButtonShow ?? true;
|
||||
const cancelButtonShow = props.cancelButtonShow ?? true;
|
||||
const canClickOk = okButtonShow && !props.okButtonDisabled;
|
||||
const canClickCancel = cancelButtonShow && !props.cancelButtonDisabled;
|
||||
|
||||
const onKeyDown = useKeyboardHandler({
|
||||
onOkButtonClick: canClickOk ? onOkButtonClick : null,
|
||||
onCancelButtonClick: canClickCancel ? onCancelButtonClick : null,
|
||||
});
|
||||
|
||||
const buttonComps = [];
|
||||
|
||||
if (props.customButtons) {
|
||||
for (const b of props.customButtons) {
|
||||
buttonComps.push(
|
||||
<button key={b.name} style={buttonStyle} onClick={() => onCustomButtonClick({ buttonName: b.name })} onKeyDown={onKeyDown}>
|
||||
<button key={b.name} style={buttonStyle} onClick={() => onCustomButtonClick({ buttonName: b.name })} disabled={b.disabled} onKeyDown={onKeyDown}>
|
||||
{b.label}
|
||||
</button>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (props.okButtonShow !== false) {
|
||||
if (okButtonShow) {
|
||||
buttonComps.push(
|
||||
<button disabled={props.okButtonDisabled} key="ok" style={buttonStyle} onClick={onOkButtonClick} ref={props.okButtonRef} onKeyDown={onKeyDown}>
|
||||
{props.okButtonLabel ? props.okButtonLabel : _('OK')}
|
||||
@@ -73,7 +82,7 @@ export default function DialogButtonRow(props: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
if (props.cancelButtonShow !== false) {
|
||||
if (cancelButtonShow) {
|
||||
buttonComps.push(
|
||||
<button disabled={props.cancelButtonDisabled} key="cancel" style={{ ...buttonStyle }} onClick={onCancelButtonClick}>
|
||||
{props.cancelButtonLabel ? props.cancelButtonLabel : _('Cancel')}
|
||||
|
||||
@@ -2,11 +2,10 @@ import * as React from 'react';
|
||||
import { useEffect, useState, useRef, useCallback } from 'react';
|
||||
import { isInsideContainer } from '@joplin/lib/dom';
|
||||
|
||||
type OnButtonClick = ()=> void;
|
||||
interface Props {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
onOkButtonClick: Function;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
onCancelButtonClick: Function;
|
||||
onOkButtonClick: null|OnButtonClick;
|
||||
onCancelButtonClick: null|OnButtonClick;
|
||||
}
|
||||
|
||||
const globalKeydownHandlers: string[] = [];
|
||||
@@ -48,15 +47,17 @@ export default (props: Props) => {
|
||||
|
||||
if (!isTopDialog() || isInSubModal(event.target)) return;
|
||||
|
||||
if (event.keyCode === 13) {
|
||||
if (event.keyCode === 13 && props.onOkButtonClick) {
|
||||
if ('nodeName' in event.target && event.target.nodeName === 'INPUT') {
|
||||
const target = event.target as HTMLInputElement;
|
||||
|
||||
if (target.type !== 'button' && target.type !== 'checkbox') {
|
||||
event.preventDefault();
|
||||
props.onOkButtonClick();
|
||||
}
|
||||
}
|
||||
} else if (event.keyCode === 27) {
|
||||
} else if (event.keyCode === 27 && props.onCancelButtonClick) {
|
||||
event.preventDefault();
|
||||
props.onCancelButtonClick();
|
||||
}
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
|
||||
@@ -172,7 +172,12 @@ export default function NoteContentPropertiesDialog(props: NoteContentProperties
|
||||
<div style={{ ...labelCompStyle, marginTop: 10 }}>
|
||||
{readTimeLabel}
|
||||
</div>
|
||||
<DialogButtonRow themeId={props.themeId} onClick={buttonRow_click} okButtonShow={false} cancelButtonLabel={_('Close')}/>
|
||||
<DialogButtonRow
|
||||
themeId={props.themeId}
|
||||
onClick={buttonRow_click}
|
||||
okButtonShow={false}
|
||||
cancelButtonLabel={_('Close')}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,36 @@ const logger = Logger.create('useEditorSearch');
|
||||
// Registers a helper CodeMirror extension to be used with
|
||||
// useEditorSearchHandler.
|
||||
|
||||
export default function useEditorSearchExtension(CodeMirror: CodeMirror5Emulation) {
|
||||
interface SetMarkersOptions {
|
||||
selectedIndex: number;
|
||||
searchTimestamp: number;
|
||||
showEditorMarkers?: boolean;
|
||||
withSelection?: boolean;
|
||||
}
|
||||
type Keyword = { value: string };
|
||||
|
||||
export type OnSetMarkers = (cm: CodeMirror5Emulation, keywords: Keyword[], options: SetMarkersOptions)=> number;
|
||||
|
||||
|
||||
// Modified from codemirror/addons/search/search.js
|
||||
const searchOverlay = (query: RegExp) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
return { token: function(stream: any) {
|
||||
query.lastIndex = stream.pos;
|
||||
const match = query.exec(stream.string);
|
||||
if (match && match.index === stream.pos) {
|
||||
stream.pos += match[0].length || 1;
|
||||
return 'search-marker';
|
||||
} else if (match) {
|
||||
stream.pos = match.index;
|
||||
} else {
|
||||
stream.skipToEnd();
|
||||
}
|
||||
return null;
|
||||
} };
|
||||
};
|
||||
|
||||
export default function useEditorSearchExtension() {
|
||||
|
||||
const [markers, setMarkers] = useState([]);
|
||||
const [overlay, setOverlay] = useState(null);
|
||||
@@ -48,23 +77,6 @@ export default function useEditorSearchExtension(CodeMirror: CodeMirror5Emulatio
|
||||
setOverlayTimeout(null);
|
||||
}, [scrollbarMarks, overlay, overlayTimeout]);
|
||||
|
||||
// Modified from codemirror/addons/search/search.js
|
||||
const searchOverlay = useCallback((query: RegExp) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
return { token: function(stream: any) {
|
||||
query.lastIndex = stream.pos;
|
||||
const match = query.exec(stream.string);
|
||||
if (match && match.index === stream.pos) {
|
||||
stream.pos += match[0].length || 1;
|
||||
return 'search-marker';
|
||||
} else if (match) {
|
||||
stream.pos = match.index;
|
||||
} else {
|
||||
stream.skipToEnd();
|
||||
}
|
||||
return null;
|
||||
} };
|
||||
}, []);
|
||||
|
||||
// Highlights the currently active found work
|
||||
// It's possible to get tricky with this functions and just use findNext/findPrev
|
||||
@@ -115,16 +127,17 @@ export default function useEditorSearchExtension(CodeMirror: CodeMirror5Emulatio
|
||||
};
|
||||
}, []);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
CodeMirror?.defineExtension('setMarkers', function(keywords: any, options: any) {
|
||||
const onSetMarkers: OnSetMarkers = (cm, keywords, options) => {
|
||||
// Pass arguments in via options to allow the extension to work if multiple editors are open simultaneously
|
||||
// See https://github.com/laurent22/joplin/issues/13399.
|
||||
if (!options) {
|
||||
options = { selectedIndex: 0, searchTimestamp: 0 };
|
||||
}
|
||||
|
||||
if (options.showEditorMarkers === false) {
|
||||
clearMarkers();
|
||||
clearOverlay(this);
|
||||
return;
|
||||
clearOverlay(cm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
clearMarkers();
|
||||
@@ -145,7 +158,7 @@ export default function useEditorSearchExtension(CodeMirror: CodeMirror5Emulatio
|
||||
const scrollTo = i === 0 && (previousKeywordValue !== keyword.value || previousIndex !== options.selectedIndex || options.searchTimestamp !== previousSearchTimestamp);
|
||||
|
||||
try {
|
||||
const match = highlightSearch(this, searchTerm, options.selectedIndex, scrollTo, !!options.withSelection);
|
||||
const match = highlightSearch(cm, searchTerm, options.selectedIndex, scrollTo, !!options.withSelection);
|
||||
if (match) marks.push(match);
|
||||
} catch (error) {
|
||||
if (error.name !== 'SyntaxError') {
|
||||
@@ -165,7 +178,7 @@ export default function useEditorSearchExtension(CodeMirror: CodeMirror5Emulatio
|
||||
// SEARCHOVERLAY
|
||||
// We only want to highlight all matches when there is only 1 search term
|
||||
if (keywords.length !== 1 || keywords[0].value === '') {
|
||||
clearOverlay(this);
|
||||
clearOverlay(cm);
|
||||
const prev = keywords.length > 1 ? keywords[0].value : '';
|
||||
setPreviousKeywordValue(prev);
|
||||
return 0;
|
||||
@@ -175,22 +188,22 @@ export default function useEditorSearchExtension(CodeMirror: CodeMirror5Emulatio
|
||||
|
||||
// Determine the number of matches in the source, this is passed on
|
||||
// to the NoteEditor component
|
||||
const regexMatches = this.getValue().match(searchTerm);
|
||||
const regexMatches = cm.getValue().match(searchTerm);
|
||||
const nMatches = regexMatches ? regexMatches.length : 0;
|
||||
|
||||
// Don't bother clearing and re-calculating the overlay if the search term
|
||||
// hasn't changed
|
||||
if (keywords[0].value === previousKeywordValue) return nMatches;
|
||||
|
||||
clearOverlay(this);
|
||||
clearOverlay(cm);
|
||||
setPreviousKeywordValue(keywords[0].value);
|
||||
|
||||
// These operations are pretty slow, so we won't add use them until the user
|
||||
// has finished typing, 500ms is probably enough time
|
||||
const timeout = shim.setTimeout(() => {
|
||||
const scrollMarks = this.showMatchesOnScrollbar?.(searchTerm, true, 'cm-search-marker-scrollbar');
|
||||
const scrollMarks = cm.showMatchesOnScrollbar?.(searchTerm, true, 'cm-search-marker-scrollbar');
|
||||
const overlay = searchOverlay(searchTerm);
|
||||
this.addOverlay(overlay);
|
||||
cm.addOverlay(overlay);
|
||||
setOverlay(overlay);
|
||||
setScrollbarMarks(scrollMarks);
|
||||
}, 500);
|
||||
@@ -199,5 +212,9 @@ export default function useEditorSearchExtension(CodeMirror: CodeMirror5Emulatio
|
||||
overlayTimeoutRef.current = timeout;
|
||||
|
||||
return nMatches;
|
||||
});
|
||||
};
|
||||
const onSetMarkersRef = useRef(onSetMarkers);
|
||||
onSetMarkersRef.current = onSetMarkers;
|
||||
|
||||
return { onSetMarkersRef };
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ import { RefObject, useEffect, useMemo, useRef } from 'react';
|
||||
import usePrevious from '../../../../hooks/usePrevious';
|
||||
import { RenderedBody } from './types';
|
||||
import { SearchMarkers } from '../../../utils/useSearchMarkers';
|
||||
import CodeMirror5Emulation from '@joplin/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation';
|
||||
import useEditorSearchExtension from './useEditorSearchExtension';
|
||||
const debounce = require('debounce');
|
||||
|
||||
interface Props {
|
||||
@@ -10,8 +12,7 @@ interface Props {
|
||||
searchMarkers: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
webviewRef: RefObject<any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
editorRef: RefObject<any>;
|
||||
editorRef: RefObject<CodeMirror5Emulation>;
|
||||
|
||||
noteContent: string;
|
||||
renderedBody: RenderedBody;
|
||||
@@ -23,6 +24,8 @@ const useEditorSearchHandler = (props: Props) => {
|
||||
webviewRef, editorRef, renderedBody, noteContent, searchMarkers, showEditorMarkers,
|
||||
} = props;
|
||||
|
||||
const { onSetMarkersRef } = useEditorSearchExtension();
|
||||
|
||||
const previousContent = usePrevious(noteContent);
|
||||
const previousRenderedBody = usePrevious(renderedBody);
|
||||
const previousSearchMarkers = usePrevious(searchMarkers);
|
||||
@@ -31,15 +34,15 @@ const useEditorSearchHandler = (props: Props) => {
|
||||
|
||||
// Fixes https://github.com/laurent22/joplin/issues/7565
|
||||
const debouncedMarkers = useMemo(() => debounce((searchMarkers: SearchMarkers) => {
|
||||
if (!editorRef.current) return;
|
||||
if (!onSetMarkersRef.current) return;
|
||||
|
||||
if (showEditorMarkersRef.current) {
|
||||
const matches = editorRef.current.setMarkers(searchMarkers.keywords, searchMarkers.options);
|
||||
const matches = onSetMarkersRef.current(editorRef.current, searchMarkers.keywords, searchMarkers.options);
|
||||
props.setLocalSearchResultCount(matches);
|
||||
} else {
|
||||
editorRef.current.setMarkers(searchMarkers.keywords, { ...searchMarkers.options, showEditorMarkers: false });
|
||||
onSetMarkersRef.current(editorRef.current, searchMarkers.keywords, { ...searchMarkers.options, showEditorMarkers: false });
|
||||
}
|
||||
}, 50), [editorRef, props.setLocalSearchResultCount]);
|
||||
}, 50), [editorRef, onSetMarkersRef, props.setLocalSearchResultCount]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!searchMarkers) return () => {};
|
||||
@@ -59,7 +62,7 @@ const useEditorSearchHandler = (props: Props) => {
|
||||
}
|
||||
return () => {};
|
||||
}, [
|
||||
editorRef,
|
||||
onSetMarkersRef,
|
||||
webviewRef,
|
||||
searchMarkers,
|
||||
previousSearchMarkers,
|
||||
@@ -71,6 +74,10 @@ const useEditorSearchHandler = (props: Props) => {
|
||||
debouncedMarkers,
|
||||
]);
|
||||
|
||||
return {
|
||||
// Returned to allow quickly setting the initial search markers just after the editor loads.
|
||||
onSetInitialMarkersRef: onSetMarkersRef,
|
||||
};
|
||||
};
|
||||
|
||||
export default useEditorSearchHandler;
|
||||
|
||||
@@ -695,7 +695,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [renderedBody, webviewReady]);
|
||||
|
||||
useEditorSearchHandler({
|
||||
const { onSetInitialMarkersRef } = useEditorSearchHandler({
|
||||
setLocalSearchResultCount: props.setLocalSearchResultCount,
|
||||
searchMarkers: props.searchMarkers,
|
||||
webviewRef,
|
||||
@@ -737,6 +737,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
|
||||
<Editor
|
||||
value={props.content}
|
||||
searchMarkers={props.searchMarkers}
|
||||
onSetMarkersRef={onSetInitialMarkersRef}
|
||||
ref={editorRef}
|
||||
mode={props.contentMarkupLanguage === MarkupToHtml.MARKUP_LANGUAGE_HTML ? 'xml' : 'joplin-markdown'}
|
||||
codeMirrorTheme={styles.editor.codeMirrorTheme}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect, useImperativeHandle, useState, useRef, useCallback, forwardRef } from 'react';
|
||||
import { useEffect, useImperativeHandle, useState, useRef, useCallback, forwardRef, RefObject } from 'react';
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
|
||||
import CodeMirror from 'codemirror';
|
||||
@@ -16,7 +16,7 @@ import useListIdent from './utils/useListIdent';
|
||||
import useScrollUtils from './utils/useScrollUtils';
|
||||
import useCursorUtils from './utils/useCursorUtils';
|
||||
import useLineSorting from './utils/useLineSorting';
|
||||
import useEditorSearch from '../utils/useEditorSearchExtension';
|
||||
import { OnSetMarkers } from '../utils/useEditorSearchExtension';
|
||||
import useJoplinMode from './utils/useJoplinMode';
|
||||
import useKeymap from './utils/useKeymap';
|
||||
import useExternalPlugins from './utils/useExternalPlugins';
|
||||
@@ -77,6 +77,7 @@ export interface EditorProps {
|
||||
value: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
searchMarkers: any;
|
||||
onSetMarkersRef: RefObject<OnSetMarkers>;
|
||||
mode: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
style: any;
|
||||
@@ -119,7 +120,6 @@ function Editor(props: EditorProps, ref: any) {
|
||||
useScrollUtils(CodeMirror);
|
||||
useCursorUtils(CodeMirror);
|
||||
useLineSorting(CodeMirror);
|
||||
useEditorSearch(CodeMirror);
|
||||
useJoplinMode(CodeMirror);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
const pluginOptions: any = useExternalPlugins(CodeMirror, props.plugins);
|
||||
@@ -228,7 +228,7 @@ function Editor(props: EditorProps, ref: any) {
|
||||
// It's possible for searchMarkers to be available before the editor
|
||||
// In these cases we set the markers asap so the user can see them as
|
||||
// soon as the editor is ready
|
||||
if (props.searchMarkers) { cm.setMarkers(props.searchMarkers.keywords, props.searchMarkers.options); }
|
||||
if (props.searchMarkers) { props.onSetMarkersRef.current(cm, props.searchMarkers.keywords, props.searchMarkers.options); }
|
||||
|
||||
return () => {
|
||||
// Clean up codemirror
|
||||
|
||||
@@ -11,7 +11,6 @@ import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
import setupVim from '@joplin/editor/CodeMirror/utils/setupVim';
|
||||
import { dirname } from 'path';
|
||||
import useKeymap from './utils/useKeymap';
|
||||
import useEditorSearch from '../utils/useEditorSearchExtension';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
import { SearchMarkers } from '../../../utils/useSearchMarkers';
|
||||
import localisation from './utils/localisation';
|
||||
@@ -44,8 +43,6 @@ const Editor = (props: Props, ref: ForwardedRef<CodeMirrorControl>) => {
|
||||
onLogMessageRef.current = props.onLogMessage;
|
||||
}, [props.onEvent, props.onLogMessage]);
|
||||
|
||||
useEditorSearch(editor);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor) {
|
||||
return () => {};
|
||||
|
||||
@@ -919,8 +919,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: Ref<NoteBodyEditorRef>) => {
|
||||
|
||||
editor.on('SetContent', () => {
|
||||
preprocessContent();
|
||||
|
||||
props_onMessage.current({ channel: 'noteRenderComplete' });
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -1047,7 +1045,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: Ref<NoteBodyEditorRef>) => {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { onInitialContentSet } = useCursorPositioning({
|
||||
const { onRestoreCursorPosition } = useCursorPositioning({
|
||||
initialCursorLocation: props.initialCursorLocation.richText as Bookmark,
|
||||
onCursorUpdate: props.onCursorMotion,
|
||||
editor,
|
||||
@@ -1120,8 +1118,12 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: Ref<NoteBodyEditorRef>) => {
|
||||
// times would result in an empty note.
|
||||
// https://github.com/laurent22/joplin/issues/3534
|
||||
editor.undoManager.reset();
|
||||
|
||||
// Only restore the cursor position from the global state when switching notes.
|
||||
// See https://github.com/laurent22/joplin/issues/13579
|
||||
onRestoreCursorPosition();
|
||||
} else {
|
||||
// Restore the cursor location
|
||||
// Restore the cursor location from the current note
|
||||
editor.selection.bookmarkManager.moveToBookmark(bookmark);
|
||||
}
|
||||
|
||||
@@ -1130,6 +1132,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: Ref<NoteBodyEditorRef>) => {
|
||||
resourceInfos: props.resourceInfos,
|
||||
contentKey: props.contentKey,
|
||||
};
|
||||
props_onMessage.current({ channel: 'noteRenderComplete' });
|
||||
}
|
||||
|
||||
const allAssetsOptions: NoteStyleOptions = {
|
||||
@@ -1143,7 +1146,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: Ref<NoteBodyEditorRef>) => {
|
||||
await loadDocumentAssets(props.themeId, editor, allAssets);
|
||||
|
||||
dispatchDidUpdate(editor);
|
||||
onInitialContentSet();
|
||||
};
|
||||
|
||||
void loadContent();
|
||||
|
||||
@@ -13,7 +13,7 @@ const useCursorPositioning = ({ initialCursorLocation, editor, onCursorUpdate }:
|
||||
initialCursorLocationRef.current = initialCursorLocation;
|
||||
|
||||
const appliedInitialCursorLocationRef = useRef(false);
|
||||
const onInitialContentSet = useCallback(() => {
|
||||
const onRestoreCursorPosition = useCallback(() => {
|
||||
if (editor) {
|
||||
if (initialCursorLocationRef.current) {
|
||||
editor.selection.moveToBookmark(initialCursorLocationRef.current);
|
||||
@@ -26,8 +26,6 @@ const useCursorPositioning = ({ initialCursorLocation, editor, onCursorUpdate }:
|
||||
useEffect(() => {
|
||||
if (!editor) return () => {};
|
||||
|
||||
editor.on('ContentSet', onInitialContentSet);
|
||||
|
||||
const onSelectionChange = () => {
|
||||
// Wait until the initial cursor position has been set. This avoids resetting
|
||||
// the initial cursor position to zero when the editor first loads.
|
||||
@@ -44,12 +42,11 @@ const useCursorPositioning = ({ initialCursorLocation, editor, onCursorUpdate }:
|
||||
editor.on('SelectionChange', onSelectionChange);
|
||||
|
||||
return () => {
|
||||
editor.off('ContentSet', onInitialContentSet);
|
||||
editor.off('SelectionChange', onSelectionChange);
|
||||
};
|
||||
}, [editor, onCursorUpdate, onInitialContentSet]);
|
||||
}, [editor, onCursorUpdate, onRestoreCursorPosition]);
|
||||
|
||||
return { onInitialContentSet };
|
||||
return { onRestoreCursorPosition };
|
||||
};
|
||||
|
||||
export default useCursorPositioning;
|
||||
|
||||
@@ -335,6 +335,7 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
selectedNoteHash: props.selectedNoteHash,
|
||||
lastEditorScrollPercents: props.lastEditorScrollPercents,
|
||||
editorRef,
|
||||
editorName: props.bodyEditor,
|
||||
});
|
||||
const onMessage = useMessageHandler(scrollWhenReadyRef, clearScrollWhenReady, windowId, editorRef, setLocalSearchResultCount, props.dispatch, formNote, htmlToMarkdown, markupToHtml);
|
||||
|
||||
|
||||
@@ -6,21 +6,25 @@ import useNowEffect from '@joplin/lib/hooks/useNowEffect';
|
||||
|
||||
interface Props {
|
||||
noteId: string;
|
||||
editorName: string;
|
||||
selectedNoteHash: string;
|
||||
lastEditorScrollPercents: NoteIdToScrollPercent;
|
||||
editorRef: RefObject<NoteBodyEditorRef>;
|
||||
}
|
||||
|
||||
const useScrollWhenReadyOptions = ({ noteId, selectedNoteHash, lastEditorScrollPercents, editorRef }: Props) => {
|
||||
const useScrollWhenReadyOptions = ({ noteId, editorName, selectedNoteHash, lastEditorScrollPercents, editorRef }: Props) => {
|
||||
const scrollWhenReadyRef = useRef<ScrollOptions|null>(null);
|
||||
|
||||
const previousNoteId = usePrevious(noteId);
|
||||
const noteIdChanged = noteId !== previousNoteId;
|
||||
const previousEditor = usePrevious(editorName);
|
||||
const editorChanged = editorName !== previousEditor;
|
||||
const lastScrollPercentsRef = useRef<NoteIdToScrollPercent>(null);
|
||||
lastScrollPercentsRef.current = lastEditorScrollPercents;
|
||||
|
||||
// This needs to be a nowEffect to prevent race conditions
|
||||
useNowEffect(() => {
|
||||
if (noteId === previousNoteId) return () => {};
|
||||
if (!editorChanged && !noteIdChanged) return () => {};
|
||||
|
||||
if (editorRef.current) {
|
||||
editorRef.current.resetScroll();
|
||||
@@ -32,7 +36,7 @@ const useScrollWhenReadyOptions = ({ noteId, selectedNoteHash, lastEditorScrollP
|
||||
value: selectedNoteHash ? selectedNoteHash : lastScrollPercent,
|
||||
};
|
||||
return () => {};
|
||||
}, [noteId, previousNoteId, selectedNoteHash, editorRef]);
|
||||
}, [editorChanged, noteIdChanged, noteId, selectedNoteHash, editorRef]);
|
||||
|
||||
const clearScrollWhenReady = useCallback(() => {
|
||||
scrollWhenReadyRef.current = null;
|
||||
|
||||
@@ -501,7 +501,12 @@ class NotePropertiesDialog extends React.Component<Props, State> {
|
||||
<div role='table' aria-labelledby='note-properties-dialog-title'>
|
||||
{noteComps}
|
||||
</div>
|
||||
<DialogButtonRow themeId={this.props.themeId} okButtonShow={!this.isReadOnly()} okButtonRef={this.okButton} onClick={this.buttonRow_click}/>
|
||||
<DialogButtonRow
|
||||
themeId={this.props.themeId}
|
||||
okButtonShow={!this.isReadOnly()}
|
||||
okButtonRef={this.okButton}
|
||||
onClick={this.buttonRow_click}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ async function initialize() {
|
||||
panes: Setting.value('noteVisiblePanes'),
|
||||
});
|
||||
|
||||
InteropService.instance().document = document;
|
||||
InteropService.instance().domParser = new DOMParser();
|
||||
InteropService.instance().xmlSerializer = new XMLSerializer();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import Dialog from '../Dialog';
|
||||
import DialogButtonRow, { ClickEvent, ButtonSpec } from '../DialogButtonRow';
|
||||
import DialogButtonRow, { ClickEvent } from '../DialogButtonRow';
|
||||
import DialogTitle from '../DialogTitle';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { FolderEntity } from '@joplin/lib/services/database/types';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
import ShareService, { ApiShare } from '@joplin/lib/services/share/ShareService';
|
||||
@@ -129,7 +129,6 @@ function ShareFolderDialog(props: Props) {
|
||||
const [share, setShare] = useState<StateShare>(null);
|
||||
const [shareUsers, setShareUsers] = useState<StateShareUser[]>([]);
|
||||
const [shareState, setShareState] = useState<ShareState>(ShareState.Idle);
|
||||
const [customButtons, setCustomButtons] = useState<ButtonSpec[]>([]);
|
||||
const [recipientsBeingUpdated, setRecipientsBeingUpdated] = useState<Record<string, boolean>>({});
|
||||
|
||||
async function synchronize(event: AsyncEffectEvent = null) {
|
||||
@@ -163,13 +162,6 @@ function ShareFolderDialog(props: Props) {
|
||||
void ShareService.instance().refreshShareUsers(share.id);
|
||||
}, [share]);
|
||||
|
||||
useEffect(() => {
|
||||
setCustomButtons(share ? [{
|
||||
name: 'unshare',
|
||||
label: _('Unshare'),
|
||||
}] : []);
|
||||
}, [share]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!share) return;
|
||||
const sus = props.shareUsers[share.id];
|
||||
@@ -177,10 +169,6 @@ function ShareFolderDialog(props: Props) {
|
||||
setShareUsers(sus);
|
||||
}, [share, props.shareUsers]);
|
||||
|
||||
useEffect(() => {
|
||||
void ShareService.instance().refreshShares();
|
||||
}, [props.folderId]);
|
||||
|
||||
const permissionsFromString = (p: string): SharePermissions => {
|
||||
return {
|
||||
can_read: 1,
|
||||
@@ -269,7 +257,7 @@ function ShareFolderDialog(props: Props) {
|
||||
}, []);
|
||||
|
||||
function renderAddRecipient() {
|
||||
const disabled = shareState !== ShareState.Idle;
|
||||
const disabled = shareState !== ShareState.Idle && shareState !== ShareState.Synchronizing;
|
||||
|
||||
const dropdown = !props.canUseSharePermissions ? null : <Dropdown className="permission-dropdown" options={permissionOptions} value={recipientPermissions} onChange={recipientPermissions_change}/>;
|
||||
|
||||
@@ -395,6 +383,17 @@ function ShareFolderDialog(props: Props) {
|
||||
props.onClose();
|
||||
}
|
||||
|
||||
const customButtons = useMemo(() => {
|
||||
return share ? [{
|
||||
name: 'unshare',
|
||||
label: _('Unshare'),
|
||||
// Don't allow unsharing the folder during the "create" action. Doing so might
|
||||
// be able to cause issues similar to #13518 (e.g. if the "unshare" action completes while
|
||||
// the "share" action is still in progress).
|
||||
disabled: shareState === ShareState.Creating || shareState === ShareState.Synchronizing,
|
||||
}] : [];
|
||||
}, [share, shareState]);
|
||||
|
||||
function renderContent() {
|
||||
return (
|
||||
<StyledRoot className="share-folder-dialog">
|
||||
|
||||
@@ -74,6 +74,8 @@ const useOnSidebarKeyDownHandler = (props: Props) => {
|
||||
const selectedItem = listItems[selectedIndex];
|
||||
let indexChange = 0;
|
||||
|
||||
const ctrlAltOrMeta = event.ctrlKey || event.altKey || event.metaKey;
|
||||
|
||||
if (selectedItem && isToggleShortcut(event.code, selectedItem, collapsedFolderIds)) {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -111,7 +113,7 @@ const useOnSidebarKeyDownHandler = (props: Props) => {
|
||||
} else if (event.code === 'Enter' && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
void CommandService.instance().execute('focusElement', 'noteList');
|
||||
} else if (selectedIndex && selectedIndex >= 0 && event.key.length === 1) {
|
||||
} else if (selectedIndex && selectedIndex >= 0 && event.key.length === 1 && !ctrlAltOrMeta) {
|
||||
const nextMatch = findNextTypeAheadMatch(selectedIndex, event.key, listItems);
|
||||
if (nextMatch !== -1) {
|
||||
indexChange = nextMatch - selectedIndex;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { StyledHeader, StyledHeaderIcon, StyledHeaderLabel } from '../styles';
|
||||
import { HeaderId, HeaderListItem } from '../types';
|
||||
import bridge from '../../../services/bridge';
|
||||
@@ -25,8 +25,6 @@ const HeaderItem: React.FC<Props> = props => {
|
||||
const item = props.item;
|
||||
const onItemClick = item.onClick;
|
||||
const itemId = item.id;
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const expanded = item.expanded;
|
||||
|
||||
const onClick: React.MouseEventHandler<HTMLElement> = useCallback(event => {
|
||||
if (onItemClick) {
|
||||
@@ -46,14 +44,6 @@ const HeaderItem: React.FC<Props> = props => {
|
||||
}
|
||||
}, [itemId]);
|
||||
|
||||
const handleMouseEnter = useCallback(() => {
|
||||
setIsHovered(true);
|
||||
}, []);
|
||||
|
||||
const handleMouseLeave = useCallback(() => {
|
||||
setIsHovered(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ListItemWrapper
|
||||
containerRef={props.anchorRef}
|
||||
@@ -70,10 +60,8 @@ const HeaderItem: React.FC<Props> = props => {
|
||||
>
|
||||
<StyledHeader
|
||||
onClick={onClick}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<StyledHeaderIcon aria-hidden='true' role='img' className={isHovered ? `fas ${expanded ? 'fa-caret-down' : 'fa-caret-right'}` : item.iconName}/>
|
||||
<StyledHeaderIcon aria-hidden='true' role='img' className={item.iconName}/>
|
||||
<StyledHeaderLabel>{item.label}</StyledHeaderLabel>
|
||||
</StyledHeader>
|
||||
</ListItemWrapper>
|
||||
|
||||
@@ -326,5 +326,16 @@ test.describe('markdownEditor', () => {
|
||||
return true;
|
||||
}).toBe(true);
|
||||
});
|
||||
|
||||
test('should still support the legacy Markdown editor', async ({ electronApp, mainWindow }) => {
|
||||
const mainScreen = await new MainScreen(mainWindow).setup();
|
||||
await mainScreen.waitFor();
|
||||
|
||||
await setSettingValue(electronApp, mainWindow, 'editor.legacyMarkdown', true);
|
||||
await mainScreen.createNewNote('Test');
|
||||
|
||||
// Should show the legacy editor
|
||||
await expect(mainWindow.locator('.rli-editor .CodeMirror5')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "3.5.5",
|
||||
"version": "3.5.6",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.bundle.js",
|
||||
"private": true,
|
||||
@@ -12,7 +12,7 @@
|
||||
"electronRebuild": "gulp electronRebuild",
|
||||
"tsc": "tsc --project tsconfig.json",
|
||||
"watch": "tsc --watch --preserveWatchOutput --project tsconfig.json",
|
||||
"start": "gulp before-start && JOPLIN_SOURCE_MAP_ENABLED=1 electron . --env dev --log-level debug --open-dev-tools --no-welcome",
|
||||
"start": "gulp before-start && electron . --env dev --log-level debug --open-dev-tools --no-welcome",
|
||||
"test": "jest",
|
||||
"test-ui": "gulp before-start && playwright test",
|
||||
"test-ci": "yarn test",
|
||||
@@ -202,12 +202,12 @@
|
||||
"taboverride": "4.0.3",
|
||||
"tesseract.js": "6.0.1",
|
||||
"tinymce": "6.8.5",
|
||||
"ts-jest": "29.3.4",
|
||||
"ts-jest": "29.4.1",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "2.1.2",
|
||||
"@electron/remote": "2.1.3",
|
||||
"@joplin/onenote-converter": "~3.5",
|
||||
"fs-extra": "11.2.0",
|
||||
"keytar": "7.9.0",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
|
||||
// source-map-support can add 1-3 seconds to the application startup
|
||||
// time -- disable it unless requested:
|
||||
if (process.env.JOPLIN_SOURCE_MAP_ENABLED) {
|
||||
// time. In the future, it may make sense to either:
|
||||
// - Use Sentry for resolving source maps: https://docs.sentry.io/platforms/javascript/guides/electron/sourcemaps/
|
||||
// - Use NodeJS source map support (if https://github.com/electron/electron/issues/38875 is resolved)
|
||||
if (!process.env.JOPLIN_SOURCE_MAP_DISABLED) {
|
||||
require('source-map-support').install();
|
||||
}
|
||||
|
||||
@@ -132,6 +132,7 @@ const EditorToolbar: React.FC<Props> = props => {
|
||||
style={styles.content}
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
onLayout={onContainerLayout}
|
||||
keyboardShouldPersistTaps="always"
|
||||
>
|
||||
{buttonInfos.map(renderButton)}
|
||||
<View style={styles.spacer}/>
|
||||
|
||||
@@ -48,6 +48,11 @@ const useStyles = (themeId: number) => {
|
||||
invisibleHeading: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
// Use compact mode on the button and expand the padding to match the original styling, to work around an Android issue #13120
|
||||
buttonStyle: {
|
||||
paddingLeft: 16,
|
||||
paddingRight: 16,
|
||||
},
|
||||
});
|
||||
}, [themeId]);
|
||||
};
|
||||
@@ -55,15 +60,22 @@ const useStyles = (themeId: number) => {
|
||||
const ModalDialog: React.FC<Props> = props => {
|
||||
const styles = useStyles(props.themeId);
|
||||
const theme = themeStyle(props.themeId);
|
||||
const containerStyle = !props.modalProps.containerStyle ? styles.container : {
|
||||
...styles.container,
|
||||
...props.modalProps.containerStyle,
|
||||
};
|
||||
const modalProps = {
|
||||
...props.modalProps,
|
||||
containerStyle,
|
||||
} as Partial<ModalElementProps>;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
transparent={true}
|
||||
visible={true}
|
||||
onRequestClose={null}
|
||||
containerStyle={styles.container}
|
||||
backgroundColor={theme.backgroundColorTransparent2}
|
||||
{...props.modalProps}
|
||||
{...modalProps}
|
||||
>
|
||||
<View style={styles.contentWrapper}>{props.children}</View>
|
||||
<View style={styles.buttonRow}>
|
||||
@@ -76,8 +88,8 @@ const ModalDialog: React.FC<Props> = props => {
|
||||
accessible={true}
|
||||
style={styles.invisibleHeading}
|
||||
/>
|
||||
<Button disabled={!props.buttonBarEnabled} onPress={props.onCancelPress}>{props.cancelTitle}</Button>
|
||||
<PrimaryButton disabled={!props.buttonBarEnabled} onPress={props.onOkPress}>{props.okTitle}</PrimaryButton>
|
||||
<Button compact contentStyle={styles.buttonStyle} disabled={!props.buttonBarEnabled} onPress={props.onCancelPress}>{props.cancelTitle}</Button>
|
||||
<PrimaryButton compact contentStyle={styles.buttonStyle} disabled={!props.buttonBarEnabled} onPress={props.onOkPress}>{props.okTitle}</PrimaryButton>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { describe, it, beforeEach } from '@jest/globals';
|
||||
import { render, waitFor } from '../../utils/testing/testingLibrary';
|
||||
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
import TestProviderStack from '../testing/TestProviderStack';
|
||||
import createMockReduxStore from '../../utils/testing/createMockReduxStore';
|
||||
import createTestEditorProps from './testing/createTestEditorProps';
|
||||
import { EditorEvent, EditorEventType } from '@joplin/editor/events';
|
||||
import { RefObject, useCallback } from 'react';
|
||||
import { EditorCommandType, EditorControl } from '@joplin/editor/types';
|
||||
import MarkdownEditor from './MarkdownEditor';
|
||||
|
||||
|
||||
interface WrapperProps {
|
||||
ref?: RefObject<EditorControl>;
|
||||
onBodyChange: (newBody: string)=> void;
|
||||
noteBody: string;
|
||||
}
|
||||
|
||||
const defaultEditorProps = createTestEditorProps();
|
||||
const testStore = createMockReduxStore();
|
||||
const WrappedEditor: React.FC<WrapperProps> = (
|
||||
{
|
||||
noteBody,
|
||||
onBodyChange,
|
||||
ref,
|
||||
}: WrapperProps,
|
||||
) => {
|
||||
const onEvent = useCallback((event: EditorEvent) => {
|
||||
if (event.kind === EditorEventType.Change) {
|
||||
onBodyChange(event.value);
|
||||
}
|
||||
}, [onBodyChange]);
|
||||
|
||||
return <TestProviderStack store={testStore}>
|
||||
<MarkdownEditor
|
||||
{...defaultEditorProps}
|
||||
onEditorEvent={onEvent}
|
||||
initialText={noteBody}
|
||||
editorRef={ref ?? defaultEditorProps.editorRef}
|
||||
/>
|
||||
</TestProviderStack>;
|
||||
};
|
||||
|
||||
describe('MarkdownEditor', () => {
|
||||
beforeEach(async () => {
|
||||
await setupDatabaseAndSynchronizer(0);
|
||||
await switchClient(0);
|
||||
Setting.setValue('editor.codeView', true);
|
||||
});
|
||||
|
||||
// Regression test for #13193. This verifies that the editor can be reached
|
||||
// over IPC.
|
||||
it('should support the "textBold" command', async () => {
|
||||
let editorBody = 'test';
|
||||
const editorRef = React.createRef<EditorControl|null>();
|
||||
render(<WrappedEditor
|
||||
ref={editorRef}
|
||||
noteBody={editorBody}
|
||||
onBodyChange={newValue => { editorBody = newValue; }}
|
||||
/>);
|
||||
|
||||
// Should mark the command as supported
|
||||
expect(await editorRef.current.supportsCommand(EditorCommandType.ToggleBolded));
|
||||
|
||||
// Command should run
|
||||
await editorRef.current.execCommand(EditorCommandType.SelectAll);
|
||||
await editorRef.current.execCommand(EditorCommandType.ToggleBolded);
|
||||
await waitFor(() => {
|
||||
expect(editorBody).toBe('**test**');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -121,6 +121,7 @@ const useStyles = (theme: Theme) => {
|
||||
height: buttonSize,
|
||||
backgroundColor: theme.backgroundColor4,
|
||||
color: theme.color4,
|
||||
margin: 2,
|
||||
},
|
||||
buttonText: buttonTextStyle,
|
||||
activeButtonText: {
|
||||
@@ -352,7 +353,7 @@ export const SearchPanel = (props: SearchPanelProps) => {
|
||||
);
|
||||
|
||||
const advancedLayout = (
|
||||
<View style={{ flexDirection: 'column', alignItems: 'center' }}>
|
||||
<View style={{ flexDirection: 'column' }}>
|
||||
<View style={{ flexDirection: 'row' }}>
|
||||
{ closeButton }
|
||||
{ labeledSearchInput }
|
||||
|
||||
@@ -54,7 +54,6 @@ const useStyles = (themeId: number, headerStyle: TextStyle|undefined) => {
|
||||
},
|
||||
tagBoxRoot: {
|
||||
flexDirection: 'column',
|
||||
flexGrow: 0.5,
|
||||
flexShrink: 1,
|
||||
},
|
||||
tagBoxScrollView: {
|
||||
@@ -86,6 +85,7 @@ const useStyles = (themeId: number, headerStyle: TextStyle|undefined) => {
|
||||
backgroundColor: theme.dividerColor,
|
||||
},
|
||||
tagSearch: {
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
},
|
||||
noTagsLabel: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { Card, TouchableRipple } from 'react-native-paper';
|
||||
import { useMemo } from 'react';
|
||||
import { StyleSheet, View, ViewStyle } from 'react-native';
|
||||
import { Platform, StyleSheet, View, ViewStyle } from 'react-native';
|
||||
|
||||
export enum InstallState {
|
||||
NotInstalled,
|
||||
@@ -20,16 +20,25 @@ interface Props {
|
||||
const useStyles = (disabled: boolean) => {
|
||||
return useMemo(() => {
|
||||
// For the TouchableRipple to work on Android, the card needs a transparent background.
|
||||
const baseCard = { backgroundColor: 'transparent' };
|
||||
const borderRadius = 12;
|
||||
const baseCard = { backgroundColor: 'transparent', borderRadius };
|
||||
return StyleSheet.create({
|
||||
cardOuterWrapper: {
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
borderRadius: 12,
|
||||
borderRadius,
|
||||
overflow: 'hidden',
|
||||
// Accessibility: Prevent the 'overflow: hidden' from hiding the focus indicator
|
||||
// on web. Only apply to web, as this causes the touchable ripple
|
||||
// from being completely contained within the card on non-web platforms.
|
||||
...(Platform.OS === 'web' ? {
|
||||
margin: -2,
|
||||
padding: 2,
|
||||
} : {}),
|
||||
},
|
||||
cardInnerWrapper: {
|
||||
width: '100%',
|
||||
borderRadius,
|
||||
},
|
||||
card: disabled ? {
|
||||
...baseCard,
|
||||
|
||||
@@ -629,7 +629,7 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
||||
);
|
||||
}
|
||||
|
||||
addSettingLink('donate_link', _('Make a donation'), 'https://joplinapp.org/donate/');
|
||||
if (Platform.OS !== 'ios') addSettingLink('donate_link', _('Make a donation'), 'https://joplinapp.org/donate/');
|
||||
addSettingLink('website_link', _('Joplin website'), 'https://joplinapp.org/');
|
||||
addSettingLink('privacy_link', _('Privacy Policy'), 'https://joplinapp.org/privacy/');
|
||||
|
||||
|
||||
@@ -515,6 +515,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
|
||||
paddingLeft: theme.marginLeft,
|
||||
borderBottomColor: theme.dividerColor,
|
||||
borderBottomWidth: 1,
|
||||
maxHeight: '40%',
|
||||
};
|
||||
|
||||
styles.titleContainerTodo = { ...styles.titleContainer };
|
||||
@@ -659,6 +660,17 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
|
||||
});
|
||||
}
|
||||
|
||||
// Reset undo/redo button state when switching to edit mode or when switching between markdown and rich text editors, since the editor is
|
||||
// recreated and loses its undo/redo history
|
||||
if (this.state.mode === 'edit' && (prevState.mode !== this.state.mode || prevProps.editorType !== this.props.editorType)) {
|
||||
this.setState({
|
||||
undoRedoButtonState: {
|
||||
canUndo: false,
|
||||
canRedo: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (prevProps.noteId && this.props.noteId && prevProps.noteId !== this.props.noteId) {
|
||||
// Easier to just go back, then go to the note since
|
||||
// the Note screen doesn't handle reloading a different note
|
||||
@@ -690,6 +702,10 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
|
||||
if (prevState.note.body !== this.state.note.body) {
|
||||
this.emitEditorPluginUpdate_();
|
||||
}
|
||||
|
||||
if (prevState.multiline !== this.state.multiline && this.titleTextFieldRef.current) {
|
||||
focus('Note::focusUpdate::title', this.titleTextFieldRef.current);
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
@@ -1703,6 +1719,7 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
|
||||
<View style={titleContainerStyle}>
|
||||
{isTodo && <Checkbox style={this.styles().checkbox} checked={!!Number(note.todo_completed)} onChange={this.todoCheckbox_change} />}
|
||||
<TextInput
|
||||
key={this.state.multiline ? 'multiLine' : 'singleLine'}
|
||||
ref={this.titleTextFieldRef}
|
||||
underlineColorAndroid="#ffffff00"
|
||||
autoCapitalize="sentences"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { View, StyleSheet, TextInput, Platform } from 'react-native';
|
||||
import { View, StyleSheet, TextInput, Platform, ScrollView, Text as TextNative } from 'react-native';
|
||||
import { AppState } from '../../utils/types';
|
||||
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
||||
import Revision from '@joplin/lib/models/Revision';
|
||||
@@ -111,6 +111,7 @@ const useStyles = (themeId: number) => {
|
||||
flex: 0,
|
||||
flexDirection: 'row',
|
||||
flexBasis: 'auto',
|
||||
maxHeight: '40%',
|
||||
},
|
||||
titleText: {
|
||||
flex: 1,
|
||||
@@ -224,12 +225,25 @@ const NoteRevisionViewer: React.FC<Props> = props => {
|
||||
|
||||
const titleComponent = (
|
||||
<View style={styles.titleViewContainer}>
|
||||
<TextInput
|
||||
style={styles.titleText}
|
||||
value={note?.title ?? ''}
|
||||
editable={false}
|
||||
multiline={multiline}
|
||||
/>
|
||||
{
|
||||
multiline ?
|
||||
<ScrollView
|
||||
style={{ flex: 1 }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
<TextNative
|
||||
selectable
|
||||
style={styles.titleText}
|
||||
>
|
||||
{note?.title ?? ''}
|
||||
</TextNative>
|
||||
</ScrollView> :
|
||||
<TextInput
|
||||
style={styles.titleText}
|
||||
value={note?.title ?? ''}
|
||||
editable={false}
|
||||
/>
|
||||
}
|
||||
{ titleToggleButton }
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ import TagEditor, { TagEditorMode } from '../TagEditor';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
||||
import { ViewStyle } from 'react-native';
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
@@ -22,6 +23,9 @@ const modalPropOverrides = {
|
||||
// Prevent the keyboard from auto-dismissing when tapping outside the search input
|
||||
keyboardShouldPersistTaps: true,
|
||||
},
|
||||
containerStyle: {
|
||||
height: '100%',
|
||||
} as ViewStyle,
|
||||
};
|
||||
|
||||
const NoteTagsDialogComponent: React.FC<Props> = props => {
|
||||
|
||||
@@ -88,7 +88,7 @@ const useWebViewSetup = ({
|
||||
`;
|
||||
|
||||
const injectedJavaScript = useMemo(() => `
|
||||
if (typeof markdownEditorBundle === 'undefined') {
|
||||
if (typeof window.markdownEditorBundle === 'undefined') {
|
||||
${shim.injectedJs('markdownEditorBundle')};
|
||||
window.markdownEditorBundle = markdownEditorBundle;
|
||||
markdownEditorBundle.setUpLogger();
|
||||
|
||||
@@ -535,7 +535,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 145;
|
||||
CURRENT_PROJECT_VERSION = 146;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
@@ -570,7 +570,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 145;
|
||||
CURRENT_PROJECT_VERSION = 146;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||
@@ -771,7 +771,7 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 145;
|
||||
CURRENT_PROJECT_VERSION = 146;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
@@ -814,7 +814,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 145;
|
||||
CURRENT_PROJECT_VERSION = 146;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@joplin/renderer": "~3.5",
|
||||
"@joplin/utils": "~3.5",
|
||||
"@react-native-clipboard/clipboard": "1.16.3",
|
||||
"@react-native-community/datetimepicker": "8.4.2",
|
||||
"@react-native-community/datetimepicker": "8.4.3",
|
||||
"@react-native-community/geolocation": "3.4.0",
|
||||
"@react-native-community/netinfo": "11.4.1",
|
||||
"@react-native-community/push-notification-ios": "1.11.0",
|
||||
@@ -66,9 +66,9 @@
|
||||
"react-native-quick-actions": "0.3.13",
|
||||
"react-native-quick-crypto": "0.7.17",
|
||||
"react-native-rsa-native": "2.0.5",
|
||||
"react-native-safe-area-context": "5.4.1",
|
||||
"react-native-safe-area-context": "5.5.2",
|
||||
"react-native-securerandom": "1.0.1",
|
||||
"react-native-share": "12.1.0",
|
||||
"react-native-share": "12.1.2",
|
||||
"react-native-sqlite-storage": "6.0.1",
|
||||
"react-native-svg": "15.13.0",
|
||||
"react-native-url-polyfill": "2.0.0",
|
||||
@@ -101,7 +101,7 @@
|
||||
"@react-native-community/cli-platform-ios": "16.0.3",
|
||||
"@react-native/babel-preset": "0.80.1",
|
||||
"@react-native/metro-config": "0.79.5",
|
||||
"@react-native/typescript-config": "0.79.5",
|
||||
"@react-native/typescript-config": "0.80.2",
|
||||
"@sqlite.org/sqlite-wasm": "3.46.0-build2",
|
||||
"@testing-library/react-native": "13.2.0",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
@@ -109,13 +109,13 @@
|
||||
"@types/node": "18.19.130",
|
||||
"@types/react": "19.0.14",
|
||||
"@types/react-redux": "7.1.33",
|
||||
"@types/serviceworker": "0.0.144",
|
||||
"@types/serviceworker": "0.0.149",
|
||||
"@types/tar-stream": "3.1.4",
|
||||
"babel-jest": "29.7.0",
|
||||
"babel-loader": "9.1.3",
|
||||
"babel-plugin-module-resolver": "4.1.0",
|
||||
"babel-plugin-react-native-web": "0.20.0",
|
||||
"esbuild": "0.25.7",
|
||||
"esbuild": "0.25.8",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"fs-extra": "11.2.0",
|
||||
"gulp": "4.0.2",
|
||||
@@ -133,7 +133,7 @@
|
||||
"sharp": "0.34.3",
|
||||
"sqlite3": "5.1.6",
|
||||
"timers-browserify": "2.0.12",
|
||||
"ts-jest": "29.3.4",
|
||||
"ts-jest": "29.4.1",
|
||||
"ts-loader": "9.5.2",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.8.3",
|
||||
|
||||
@@ -67,6 +67,11 @@ const appReducer = (state = appDefaultState, action: any) => {
|
||||
|
||||
newState.selectedNoteHash = '';
|
||||
|
||||
if (currentRoute.routeName === 'Search' && action.routeName === 'Notes') {
|
||||
// Force a reload of the note list
|
||||
newState.notesSource = '';
|
||||
}
|
||||
|
||||
if (action.routeName === 'Search') {
|
||||
newState.notesParentType = 'Search';
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ const useSafeAreaPadding = () => {
|
||||
paddingRight: safeAreaInsets.right,
|
||||
paddingLeft: safeAreaInsets.left,
|
||||
paddingTop: safeAreaInsets.top,
|
||||
paddingBottom: 0,
|
||||
paddingBottom: safeAreaInsets.bottom,
|
||||
} : {
|
||||
paddingTop: safeAreaInsets.top,
|
||||
paddingBottom: safeAreaInsets.bottom,
|
||||
|
||||
@@ -29,10 +29,14 @@ interface CssDecorationSpec extends DecorationRange {
|
||||
id?: number;
|
||||
}
|
||||
|
||||
interface RemoveMarkDecorationSpec {
|
||||
id: number;
|
||||
}
|
||||
|
||||
const addLineDecorationEffect = StateEffect.define<CssDecorationSpec>(mapRangeConfig);
|
||||
const removeLineDecorationEffect = StateEffect.define<CssDecorationSpec>(mapRangeConfig);
|
||||
const addMarkDecorationEffect = StateEffect.define<CssDecorationSpec>(mapRangeConfig);
|
||||
const removeMarkDecorationEffect = StateEffect.define<CssDecorationSpec>(mapRangeConfig);
|
||||
const removeMarkDecorationEffect = StateEffect.define<RemoveMarkDecorationSpec>();
|
||||
const refreshOverlaysEffect = StateEffect.define();
|
||||
|
||||
export interface LineWidgetOptions {
|
||||
@@ -190,12 +194,13 @@ export default class Decorator {
|
||||
decorations = decorations.update({
|
||||
add: [decoration.range(from, to)],
|
||||
});
|
||||
} else if (effect.is(removeLineDecorationEffect) || effect.is(removeMarkDecorationEffect)) {
|
||||
} else if (effect.is(removeLineDecorationEffect)) {
|
||||
const doc = transaction.state.doc;
|
||||
const targetFrom = doc.lineAt(effect.value.from).from;
|
||||
const targetTo = doc.lineAt(effect.value.to).to;
|
||||
const { from, to } = effect.value;
|
||||
// Handle the case where { from, to } point to an outdated document
|
||||
const targetFrom = doc.lineAt(from).from;
|
||||
const targetTo = doc.lineAt(to).to;
|
||||
|
||||
const targetId = effect.value.id;
|
||||
const targetDecoration = this.classNameToCssDecoration(
|
||||
effect.value.cssClass, effect.is(removeLineDecorationEffect),
|
||||
);
|
||||
@@ -203,12 +208,15 @@ export default class Decorator {
|
||||
decorations = decorations.update({
|
||||
// Returns true only for decorations that should be kept.
|
||||
filter: (from, to, value) => {
|
||||
if (targetId !== undefined) {
|
||||
return value.spec.id !== effect.value.id;
|
||||
}
|
||||
|
||||
const isInRange = from >= targetFrom && to <= targetTo;
|
||||
return isInRange && value.eq(targetDecoration);
|
||||
return !isInRange || !value.eq(targetDecoration);
|
||||
},
|
||||
});
|
||||
} else if (effect.is(removeMarkDecorationEffect)) {
|
||||
decorations = decorations.update({
|
||||
// Returns true only for decorations that should be kept.
|
||||
filter: (_from, _to, value) => {
|
||||
return value.spec.id !== effect.value.id;
|
||||
},
|
||||
});
|
||||
} else if (effect.is(addLineWidgetEffect)) {
|
||||
@@ -384,9 +392,10 @@ export default class Decorator {
|
||||
}
|
||||
|
||||
public markText(from: number, to: number, options?: MarkTextOptions) {
|
||||
const id = this._nextLineWidgetId++;
|
||||
const effectOptions: CssDecorationSpec = {
|
||||
cssClass: options.className ?? '',
|
||||
id: this._nextLineWidgetId++,
|
||||
id,
|
||||
from,
|
||||
to,
|
||||
};
|
||||
@@ -398,7 +407,7 @@ export default class Decorator {
|
||||
return {
|
||||
clear: () => {
|
||||
this.editor.dispatch({
|
||||
effects: removeMarkDecorationEffect.of(effectOptions),
|
||||
effects: removeMarkDecorationEffect.of({ id }),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@types/styled-components": "5.1.32",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"ts-jest": "29.3.4",
|
||||
"ts-jest": "29.4.1",
|
||||
"typescript": "5.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -53,7 +53,7 @@
|
||||
"prosemirror-history": "1.4.1",
|
||||
"prosemirror-inputrules": "1.5.0",
|
||||
"prosemirror-keymap": "1.2.3",
|
||||
"prosemirror-model": "1.25.2",
|
||||
"prosemirror-model": "1.25.3",
|
||||
"prosemirror-schema-list": "1.5.1",
|
||||
"prosemirror-search": "1.1.0",
|
||||
"prosemirror-state": "1.4.3",
|
||||
|
||||
@@ -52,8 +52,8 @@
|
||||
"coveralls": "3.1.1",
|
||||
"eslint": "8.57.1",
|
||||
"jest": "29.7.0",
|
||||
"prettier": "3.5.3",
|
||||
"ts-jest": "29.3.4",
|
||||
"prettier": "3.6.2",
|
||||
"ts-jest": "29.4.1",
|
||||
"typescript": "5.8.3"
|
||||
},
|
||||
"jest": {
|
||||
|
||||
@@ -78,7 +78,7 @@ export default class SyncTargetJoplinServer extends BaseSyncTarget {
|
||||
return super.fileApi();
|
||||
}
|
||||
|
||||
public static async checkConfig(options: FileApiOptions, syncTargetId: number = null) {
|
||||
public static async checkConfig(options: FileApiOptions, syncTargetId: number = null, fileApi: FileApi|null = null) {
|
||||
const output = {
|
||||
ok: false,
|
||||
errorMessage: '',
|
||||
@@ -86,50 +86,57 @@ export default class SyncTargetJoplinServer extends BaseSyncTarget {
|
||||
|
||||
syncTargetId = syncTargetId === null ? this.id() : syncTargetId;
|
||||
|
||||
let fileApi = null;
|
||||
try {
|
||||
fileApi = await newFileApi(syncTargetId, options);
|
||||
fileApi.requestRepeatCount_ = 0;
|
||||
} catch (error) {
|
||||
// If there's an error it's probably an application error, but we
|
||||
// can't proceed anyway, so exit.
|
||||
output.errorMessage = error.message;
|
||||
if (error.code) output.errorMessage += ` (Code ${error.code})`;
|
||||
return output;
|
||||
}
|
||||
|
||||
// First we try to fetch info.json. It may not be present if it's a new
|
||||
// sync target but otherwise, if it is, and it's valid, we know the
|
||||
// credentials are valid. We do this test first because it will work
|
||||
// even if account upload is disabled. And we need such account to
|
||||
// successfully login so that they can fix it by deleting extraneous
|
||||
// notes or resources.
|
||||
try {
|
||||
const r = await fileApi.get('info.json');
|
||||
if (r) {
|
||||
const parsed = JSON.parse(r);
|
||||
if (parsed) {
|
||||
output.ok = true;
|
||||
return output;
|
||||
}
|
||||
if (!fileApi) {
|
||||
try {
|
||||
fileApi = await newFileApi(syncTargetId, options);
|
||||
} catch (error) {
|
||||
// If there's an error it's probably an application error, but we
|
||||
// can't proceed anyway, so exit.
|
||||
output.errorMessage = error.message;
|
||||
if (error.code) output.errorMessage += ` (Code ${error.code})`;
|
||||
return output;
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore because we'll use the next test to check for sure if it
|
||||
// works or not.
|
||||
staticLogger.warn('Could not fetch or parse info.json:', error);
|
||||
}
|
||||
|
||||
// This is a more generic test, which writes a file and tries to read it
|
||||
// back.
|
||||
const previousRequestRepeatCount = fileApi.requestRepeatCount_;
|
||||
fileApi.requestRepeatCount_ = 0;
|
||||
|
||||
try {
|
||||
await fileApi.put('testing.txt', 'testing');
|
||||
const result = await fileApi.get('testing.txt');
|
||||
if (result !== 'testing') throw new Error(`Could not access data on server "${options.path()}"`);
|
||||
await fileApi.delete('testing.txt');
|
||||
output.ok = true;
|
||||
} catch (error) {
|
||||
output.errorMessage = error.message;
|
||||
if (error.code) output.errorMessage += ` (Code ${error.code})`;
|
||||
// First we try to fetch info.json. It may not be present if it's a new
|
||||
// sync target but otherwise, if it is, and it's valid, we know the
|
||||
// credentials are valid. We do this test first because it will work
|
||||
// even if account upload is disabled. And we need such account to
|
||||
// successfully login so that they can fix it by deleting extraneous
|
||||
// notes or resources.
|
||||
try {
|
||||
const r = await fileApi.get('info.json');
|
||||
if (r) {
|
||||
const parsed = JSON.parse(r);
|
||||
if (parsed) {
|
||||
output.ok = true;
|
||||
return output;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore because we'll use the next test to check for sure if it
|
||||
// works or not.
|
||||
staticLogger.warn('Could not fetch or parse info.json:', error);
|
||||
}
|
||||
|
||||
// This is a more generic test, which writes a file and tries to read it
|
||||
// back.
|
||||
try {
|
||||
await fileApi.put('testing.txt', 'testing');
|
||||
const result = await fileApi.get('testing.txt');
|
||||
if (result !== 'testing') throw new Error(`Could not access data on server "${options.path()}"`);
|
||||
await fileApi.delete('testing.txt');
|
||||
output.ok = true;
|
||||
} catch (error) {
|
||||
output.errorMessage = error.message;
|
||||
if (error.code) output.errorMessage += ` (Code ${error.code})`;
|
||||
}
|
||||
} finally {
|
||||
fileApi.requestRepeatCount_ = previousRequestRepeatCount;
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
@@ -52,6 +52,9 @@ export const authenticateWithCode = async (code: string) => {
|
||||
//
|
||||
// Based on the regular Joplin Server sync target.
|
||||
export default class SyncTargetJoplinServerSAML extends SyncTargetJoplinServer {
|
||||
|
||||
private lastFileApiOptions_: FileApiOptions|null = null;
|
||||
|
||||
public static override id() {
|
||||
return 11;
|
||||
}
|
||||
@@ -65,7 +68,16 @@ export default class SyncTargetJoplinServerSAML extends SyncTargetJoplinServer {
|
||||
}
|
||||
|
||||
public override async isAuthenticated() {
|
||||
return Setting.value('sync.11.id') !== '';
|
||||
if (!Setting.value('sync.11.id')) return false;
|
||||
|
||||
// We check that the file API has been initialized at least once, otherwise the below check
|
||||
// will always fail and it will be impossible to login.
|
||||
if (this.lastFileApiOptions_) {
|
||||
const check = await SyncTargetJoplinServer.checkConfig(null, null, await this.fileApi());
|
||||
return check.ok;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static override requiresPassword() {
|
||||
@@ -111,11 +123,12 @@ export default class SyncTargetJoplinServerSAML extends SyncTargetJoplinServer {
|
||||
}
|
||||
|
||||
protected override async initFileApi() {
|
||||
return initFileApi(SyncTargetJoplinServerSAML.id(), this.logger(), {
|
||||
this.lastFileApiOptions_ = {
|
||||
path: () => Setting.value('sync.11.path'),
|
||||
userContentPath: () => Setting.value('sync.11.userContentPath'),
|
||||
username: () => '',
|
||||
password: () => '',
|
||||
});
|
||||
};
|
||||
return initFileApi(SyncTargetJoplinServerSAML.id(), this.logger(), this.lastFileApiOptions_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,30 +457,30 @@ export default class Synchronizer {
|
||||
}
|
||||
}
|
||||
|
||||
// Before synchronising make sure all share_id properties are set
|
||||
// correctly so as to share/unshare the right items.
|
||||
try {
|
||||
await Folder.updateAllShareIds(this.resourceService(), this.shareService_ ? this.shareService_.shares : []);
|
||||
if (this.shareService_) await this.shareService_.checkShareConsistency();
|
||||
} catch (error) {
|
||||
if (error && error.code === ErrorCode.IsReadOnly) {
|
||||
// We ignore it because the functions above tried to modify a
|
||||
// read-only item and failed. Normally it shouldn't happen since
|
||||
// the UI should prevent, but if there's a bug in the UI or some
|
||||
// other issue we don't want sync to fail because of this.
|
||||
logger.error('Could not update share because an item is readonly:', error);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const itemUploader = new ItemUploader(this.api(), this.apiCall);
|
||||
|
||||
let errorToThrow = null;
|
||||
let syncLock = null;
|
||||
let hasCaughtError = false;
|
||||
|
||||
try {
|
||||
// Before synchronising make sure all share_id properties are set
|
||||
// correctly so as to share/unshare the right items.
|
||||
try {
|
||||
await Folder.updateAllShareIds(this.resourceService(), this.shareService_ ? this.shareService_.shares : []);
|
||||
if (this.shareService_) await this.shareService_.checkShareConsistency();
|
||||
} catch (error) {
|
||||
if (error && error.code === ErrorCode.IsReadOnly) {
|
||||
// We ignore it because the functions above tried to modify a
|
||||
// read-only item and failed. Normally it shouldn't happen since
|
||||
// the UI should prevent, but if there's a bug in the UI or some
|
||||
// other issue we don't want sync to fail because of this.
|
||||
logger.error('Could not update share because an item is readonly:', error);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const itemUploader = new ItemUploader(this.api(), this.apiCall);
|
||||
|
||||
await this.api().initialize();
|
||||
this.api().setTempDirName(Dirnames.Temp);
|
||||
|
||||
|
||||
@@ -350,7 +350,7 @@ class WebDavApi {
|
||||
// https://github.com/facebook/react-native/issues/30176
|
||||
if (!headers['Content-Type']) {
|
||||
if (method === 'PROPFIND') headers['Content-Type'] = 'text/xml';
|
||||
if (method === 'PUT') headers['Content-Type'] = 'text/plain';
|
||||
if (method === 'PUT') headers['Content-Type'] = 'application/octet-stream';
|
||||
}
|
||||
|
||||
// React-native has caching enabled by at least on Android (see https://github.com/laurent22/joplin/issues/4706 and the related PR).
|
||||
|
||||
@@ -2,6 +2,7 @@ import { utils, CommandRuntime, CommandDeclaration, CommandContext } from '../se
|
||||
import { _ } from '../locale';
|
||||
import { reg } from '../registry';
|
||||
import Setting from '../models/Setting';
|
||||
import NavService from '../services/NavService';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'synchronize',
|
||||
@@ -35,7 +36,18 @@ export const runtime = (): CommandRuntime => {
|
||||
return 'auth';
|
||||
}
|
||||
|
||||
reg.logger().error('Not authenticated with sync target - please check your credentials.');
|
||||
const error = new Error('Not authenticated with sync target - please check your credentials.');
|
||||
|
||||
utils.store.dispatch({
|
||||
type: 'SYNC_REPORT_UPDATE',
|
||||
report: { errors: [error] },
|
||||
});
|
||||
|
||||
if (Setting.value('sync.target') === 11) {
|
||||
await NavService.go('JoplinServerSamlLogin');
|
||||
}
|
||||
|
||||
reg.logger().error(error);
|
||||
return 'error';
|
||||
}
|
||||
|
||||
|
||||
@@ -42,20 +42,6 @@ const geoipServices: Record<string, GeoipService> = {
|
||||
};
|
||||
},
|
||||
|
||||
geoplugin: async (): Promise<CurrentPositionResponse> => {
|
||||
const r = await fetchJson('http://www.geoplugin.net/json.gp');
|
||||
if (!('geoplugin_latitude' in r) || !('geoplugin_longitude' in r)) throw new Error(`Invalid geolocation response: ${r ? JSON.stringify(r) : '<null>'}`);
|
||||
|
||||
return {
|
||||
timestamp: Date.now(),
|
||||
coords: {
|
||||
longitude: Number(r.geoplugin_longitude),
|
||||
altitude: 0,
|
||||
latitude: Number(r.geoplugin_latitude),
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default class {
|
||||
|
||||
@@ -182,6 +182,9 @@
|
||||
"Add body": [
|
||||
"أضف الجسم"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"إضافة جديدة"
|
||||
],
|
||||
|
||||
@@ -170,6 +170,9 @@
|
||||
"Add body": [
|
||||
""
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add or remove tags:": [
|
||||
"Добавяне и махане на тагове:"
|
||||
],
|
||||
|
||||
@@ -167,6 +167,9 @@
|
||||
"Add body": [
|
||||
"Dodaj tekst"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add or remove tags:": [
|
||||
"Dodajte ili uklonite oznake:"
|
||||
],
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
"[None]"
|
||||
],
|
||||
"A brief description of the image:": [
|
||||
""
|
||||
"Una breu descripció de la imatge:"
|
||||
],
|
||||
"A comma-separated list of words. May be used for uncommon words, to help voice typing spell them correctly.": [
|
||||
"Una llista de paraules separades per comes. Es pot utilitzar per a paraules poc comunes, per ajudar a escriure la veu a escriure-les correctament."
|
||||
@@ -242,6 +242,9 @@
|
||||
"Add body": [
|
||||
"Afegeix contingut"
|
||||
],
|
||||
"Add column": [
|
||||
"Afegeix una columna"
|
||||
],
|
||||
"Add new": [
|
||||
"Afegeix-ne un"
|
||||
],
|
||||
@@ -251,6 +254,9 @@
|
||||
"Add recipient:": [
|
||||
"Afegeix un destinatari:"
|
||||
],
|
||||
"Add row": [
|
||||
"Afegeix una fila"
|
||||
],
|
||||
"Add tags:": [
|
||||
"Afegir etiquetes:"
|
||||
],
|
||||
@@ -339,7 +345,7 @@
|
||||
"S'ha trobat un dibuix desat automàticament. Voleu adjuntar-ne una còpia a la nota?"
|
||||
],
|
||||
"An error occurred while sending the response. This can happen if the app is offline or cannot connect to the server.\nError: %s": [
|
||||
""
|
||||
"S'ha produït un error en enviar la resposta. Això pot passar si l'aplicació està fora de línia o no es pot connectar al servidor.\nError: %s"
|
||||
],
|
||||
"An error occurred: %s": [
|
||||
"S'ha produït un error: %s"
|
||||
@@ -680,6 +686,9 @@
|
||||
"Collapse all notebooks": [
|
||||
"Redueix totes les llibretes"
|
||||
],
|
||||
"Collapse title": [
|
||||
"Replega el títol"
|
||||
],
|
||||
"Collapsed": [
|
||||
"Col·lapsat"
|
||||
],
|
||||
@@ -1018,6 +1027,9 @@
|
||||
"Delete attachment \"%s\"?": [
|
||||
"Voleu suprimir l'adjunt \"%s\"?"
|
||||
],
|
||||
"Delete column": [
|
||||
"Suprimeix la columna"
|
||||
],
|
||||
"Delete expired sessions": [
|
||||
"Suprimeix les sessions expirades"
|
||||
],
|
||||
@@ -1045,6 +1057,9 @@
|
||||
"Delete profile \"%s\"": [
|
||||
"Suprimeix el perfil \"%s\"?"
|
||||
],
|
||||
"Delete row": [
|
||||
"Suprimeix la fila"
|
||||
],
|
||||
"Delete selected notes": [
|
||||
"Suprimeix les notes seleccionades"
|
||||
],
|
||||
@@ -1178,7 +1193,7 @@
|
||||
"No perdeu les contrasenyes perquè, per motius de seguretat, és l'única forma de desxifrar les dades. Per habilitar el xifrat, introduïu la vostra contrasenya."
|
||||
],
|
||||
"Do you find the Joplin web app useful?": [
|
||||
""
|
||||
"Trobes útil l'aplicació web Joplin?"
|
||||
],
|
||||
"Document scanner: Title template": [
|
||||
"Escàner de documents: plantilla de títol"
|
||||
@@ -1537,6 +1552,9 @@
|
||||
"Expand all notebooks": [
|
||||
"Desplegar totes les llibretes"
|
||||
],
|
||||
"Expand title": [
|
||||
"Expandeix el títol"
|
||||
],
|
||||
"Expanded": [
|
||||
"Expandit"
|
||||
],
|
||||
@@ -1601,7 +1619,7 @@
|
||||
"Interruptors de funcions"
|
||||
],
|
||||
"Feedback": [
|
||||
""
|
||||
"Comentaris"
|
||||
],
|
||||
"Fetched items: %d/%d.": [
|
||||
"Elements obtinguts: %d/%d."
|
||||
@@ -2015,6 +2033,9 @@
|
||||
"Joplin Server": [
|
||||
"Servidor del Joplin"
|
||||
],
|
||||
"Joplin Server (SAML)": [
|
||||
"Servidor Joplin (SAML)"
|
||||
],
|
||||
"Joplin Server Business": [
|
||||
"Joplin Servidor d'empreses"
|
||||
],
|
||||
@@ -2034,7 +2055,7 @@
|
||||
"Autenticació SSO de Joplin"
|
||||
],
|
||||
"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.": [
|
||||
""
|
||||
"Joplin admet desar la ubicació a la qual es desen o es creen les notes. Voleu habilitar-lo? Això es pot canviar en qualsevol moment a la configuració."
|
||||
],
|
||||
"Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.": [
|
||||
"El porta-retalls de webs del Joplin us permet desar pàgines web i captures de pantalla del navegador web al Joplin."
|
||||
@@ -2067,7 +2088,7 @@
|
||||
"Claus que requereixen actualització"
|
||||
],
|
||||
"Label": [
|
||||
""
|
||||
"Etiqueta"
|
||||
],
|
||||
"Landscape": [
|
||||
"Paisatge"
|
||||
@@ -2224,6 +2245,9 @@
|
||||
"Markdown editor": [
|
||||
"Editor Markdown"
|
||||
],
|
||||
"Markdown editor: Highlight active line": [
|
||||
"Editor Markdown: ressalta la línia activa"
|
||||
],
|
||||
"Markdown editor: Render images": [
|
||||
"Editor Markdown: Renderització d'imatges"
|
||||
],
|
||||
@@ -2484,7 +2508,7 @@
|
||||
"No ara"
|
||||
],
|
||||
"Not useful": [
|
||||
""
|
||||
"No és útil"
|
||||
],
|
||||
"note": [
|
||||
"nota"
|
||||
@@ -2534,6 +2558,9 @@
|
||||
"Note list style": [
|
||||
"Estil de llista de notes"
|
||||
],
|
||||
"Note not published: %s": [
|
||||
"Nota no publicada: %s"
|
||||
],
|
||||
"Note preview": [
|
||||
"Vista prèvia de la nota"
|
||||
],
|
||||
@@ -2694,7 +2721,7 @@
|
||||
"Llista ordenada"
|
||||
],
|
||||
"Other": [
|
||||
""
|
||||
"Altres"
|
||||
],
|
||||
"Other applications...": [
|
||||
"Altres aplicacions."
|
||||
@@ -2946,6 +2973,9 @@
|
||||
"Publish Note": [
|
||||
"Publica la nota"
|
||||
],
|
||||
"Publish note \"%s\" (in notebook \"%s\")?": [
|
||||
"Publicar la nota \"%s\" (a la llibreta \"%s\")?"
|
||||
],
|
||||
"Publish note...": [
|
||||
"Publica la nota..."
|
||||
],
|
||||
@@ -2958,8 +2988,11 @@
|
||||
"Publish/unpublish": [
|
||||
"Publica/despublica"
|
||||
],
|
||||
"Published at URL: %s": [
|
||||
"Publicat a la URL: %s"
|
||||
],
|
||||
"Publishes a note to Joplin Server or Joplin Cloud": [
|
||||
""
|
||||
"Publica una nota a Joplin Server o Joplin Cloud"
|
||||
],
|
||||
"QR Code": [
|
||||
"Codi QR"
|
||||
@@ -3006,6 +3039,9 @@
|
||||
"Recipients:": [
|
||||
"Destinataris:"
|
||||
],
|
||||
"Recognise text:": [
|
||||
"Reconeix el text:"
|
||||
],
|
||||
"Recognize handwritten image": [
|
||||
"Reconeix la imatge manuscrita"
|
||||
],
|
||||
@@ -3234,6 +3270,9 @@
|
||||
"Save geo-location with notes": [
|
||||
"Desa la geolocalització a les notes"
|
||||
],
|
||||
"Save geolocation?": [
|
||||
"Voleu desar la geolocalització?"
|
||||
],
|
||||
"Scan notebook": [
|
||||
"Escaneja la llibreta"
|
||||
],
|
||||
@@ -3304,7 +3343,7 @@
|
||||
"Selecciona la llibreta"
|
||||
],
|
||||
"Select one of the other supported sync targets.": [
|
||||
""
|
||||
"Seleccioneu un dels altres objectius de sincronització admesos."
|
||||
],
|
||||
"Select parent notebook": [
|
||||
"Selecciona la llibreta principal"
|
||||
@@ -3643,7 +3682,7 @@
|
||||
"Canvia a [notebook] - totes les operacions posteriors s'aplicaran en aquesta llibreta."
|
||||
],
|
||||
"Sync": [
|
||||
""
|
||||
"Sincronitza"
|
||||
],
|
||||
"Sync as many devices as you want": [
|
||||
"Sincronitza tants dispositius com vulguis"
|
||||
@@ -3723,6 +3762,9 @@
|
||||
"Take photo": [
|
||||
"Fes una foto"
|
||||
],
|
||||
"Take survey": [
|
||||
"Fer enquesta"
|
||||
],
|
||||
"Task \"%s\" failed with error: %s": [
|
||||
"La tasca \"%s\" ha fallat amb l'error: %s"
|
||||
],
|
||||
@@ -3742,7 +3784,7 @@
|
||||
"Ordre de l'editor de text"
|
||||
],
|
||||
"Thank you for the feedback!\nDo you have time to complete a short survey?": [
|
||||
""
|
||||
"Gràcies pels comentaris!\nTens temps per fer una petita enquesta?"
|
||||
],
|
||||
"The active profile cannot be deleted. Switch to a different profile and try again.": [
|
||||
"El perfil actiu no es pot esborrar. Canvia a un perfil diferent i torna-ho a provar."
|
||||
@@ -4142,6 +4184,9 @@
|
||||
"Unable to share log data. Reason: %s": [
|
||||
"No s'han pogut compartir les dades del registre. Motiu: %s"
|
||||
],
|
||||
"Unable to share note data. Reason: %s": [
|
||||
"No s'han pogut compartir les dades de la nota. Motiu: %s"
|
||||
],
|
||||
"Unchecked": [
|
||||
"Desmarcat"
|
||||
],
|
||||
@@ -4287,7 +4332,7 @@
|
||||
"S'utilitza quan es necessita un tipus de lletra d'amplada fixa per a mostrar text de manera llegible (p. ex. taules, caselles de selecció, codi). Si no es troba, s'utilitza un tipus de lletra genèric monoespai (amplada fixa)."
|
||||
],
|
||||
"Useful": [
|
||||
""
|
||||
"Útil"
|
||||
],
|
||||
"User deletions": [
|
||||
"Supressions d'usuari"
|
||||
@@ -4395,7 +4440,7 @@
|
||||
"En crear una tasca nova:"
|
||||
],
|
||||
"When enabled, requests that the images in the note be transcribed with a higher-quality on-server transcription service. Requires sync with a copy of the desktop app.": [
|
||||
""
|
||||
"Quan està activat, sol·licita que les imatges de la nota es transcriguin amb un servei de transcripció al servidor de més qualitat. Requereix sincronització amb una còpia de l'aplicació d'escriptori."
|
||||
],
|
||||
"When enabled, the application will scan your attachments and extract the text from it. This will allow you to search for text in these attachments.": [
|
||||
"Quan estigui activada, l'aplicació escanejarà els adjunts i n'extraurà el text. Això us permetrà cercar text en aquests adjunts."
|
||||
|
||||
@@ -185,6 +185,9 @@
|
||||
"Add body": [
|
||||
"Přidat tělo"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Přidat nový"
|
||||
],
|
||||
|
||||
@@ -242,6 +242,9 @@
|
||||
"Add body": [
|
||||
"Tilføj brødtekst"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Tilføj ny"
|
||||
],
|
||||
|
||||
@@ -242,6 +242,9 @@
|
||||
"Add body": [
|
||||
"Text hinzufügen"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Neu hinzufügen"
|
||||
],
|
||||
|
||||
@@ -200,6 +200,9 @@
|
||||
"Add body": [
|
||||
"Προσθήκη σώματος κειμένου"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Προσθήκη νέου"
|
||||
],
|
||||
|
||||
@@ -242,6 +242,9 @@
|
||||
"Add body": [
|
||||
""
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
""
|
||||
],
|
||||
@@ -251,6 +254,9 @@
|
||||
"Add recipient:": [
|
||||
""
|
||||
],
|
||||
"Add row": [
|
||||
""
|
||||
],
|
||||
"Add tags:": [
|
||||
""
|
||||
],
|
||||
@@ -1021,6 +1027,9 @@
|
||||
"Delete attachment \"%s\"?": [
|
||||
""
|
||||
],
|
||||
"Delete column": [
|
||||
""
|
||||
],
|
||||
"Delete expired sessions": [
|
||||
""
|
||||
],
|
||||
@@ -1048,6 +1057,9 @@
|
||||
"Delete profile \"%s\"": [
|
||||
""
|
||||
],
|
||||
"Delete row": [
|
||||
""
|
||||
],
|
||||
"Delete selected notes": [
|
||||
""
|
||||
],
|
||||
|
||||
@@ -239,6 +239,9 @@
|
||||
"Add body": [
|
||||
""
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
""
|
||||
],
|
||||
@@ -248,6 +251,9 @@
|
||||
"Add recipient:": [
|
||||
""
|
||||
],
|
||||
"Add row": [
|
||||
""
|
||||
],
|
||||
"Add tags:": [
|
||||
""
|
||||
],
|
||||
@@ -1009,6 +1015,9 @@
|
||||
"Delete attachment \"%s\"?": [
|
||||
""
|
||||
],
|
||||
"Delete column": [
|
||||
""
|
||||
],
|
||||
"Delete expired sessions": [
|
||||
""
|
||||
],
|
||||
@@ -1036,6 +1045,9 @@
|
||||
"Delete profile \"%s\"": [
|
||||
""
|
||||
],
|
||||
"Delete row": [
|
||||
""
|
||||
],
|
||||
"Delete selected notes": [
|
||||
""
|
||||
],
|
||||
|
||||
@@ -177,6 +177,9 @@
|
||||
"Add body": [
|
||||
"Aldoni korpon"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add or remove tags:": [
|
||||
"Aldoni aŭ forigi etikedojn:"
|
||||
],
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
"[none]"
|
||||
],
|
||||
"A brief description of the image:": [
|
||||
""
|
||||
"Una breve descripción de la imagen:"
|
||||
],
|
||||
"A comma-separated list of words. May be used for uncommon words, to help voice typing spell them correctly.": [
|
||||
"Una lista de palabras separadas por comas. Se puede usar para palabras poco comunes, para ayudar a la escritura por voz a deletrearlas correctamente."
|
||||
@@ -242,6 +242,9 @@
|
||||
"Add body": [
|
||||
"Añadir cuerpo"
|
||||
],
|
||||
"Add column": [
|
||||
"Añadir columna"
|
||||
],
|
||||
"Add new": [
|
||||
"Añadir nuevo"
|
||||
],
|
||||
@@ -251,6 +254,9 @@
|
||||
"Add recipient:": [
|
||||
"Agregar destinatario:"
|
||||
],
|
||||
"Add row": [
|
||||
"Añadir fila"
|
||||
],
|
||||
"Add tags:": [
|
||||
"Añadir etiquetas:"
|
||||
],
|
||||
@@ -339,7 +345,7 @@
|
||||
"Se ha encontrado un dibujo autoguardado. ¿Adjuntar una copia a la nota?"
|
||||
],
|
||||
"An error occurred while sending the response. This can happen if the app is offline or cannot connect to the server.\nError: %s": [
|
||||
""
|
||||
"Se ha producido un error al enviar la respuesta. Esto puede ocurrir si la aplicación no está en línea o no puede conectarse al servidor.\nError: %s"
|
||||
],
|
||||
"An error occurred: %s": [
|
||||
"Se ha producido un error: %s"
|
||||
@@ -680,11 +686,14 @@
|
||||
"Collapse all notebooks": [
|
||||
"Contraer todas las libretas"
|
||||
],
|
||||
"Collapse title": [
|
||||
"Contraer título"
|
||||
],
|
||||
"Collapsed": [
|
||||
"Colapsado"
|
||||
],
|
||||
"Coming alarms": [
|
||||
"Alarmas próximas"
|
||||
"Próximas alarmas"
|
||||
],
|
||||
"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\".": [
|
||||
"Lista separada por comas de rutas de acceso a directorios desde los que cargar los certificados, o ruta de acceso a archivos de certificado individuales. Por ejemplo: /my/cert_dir, /other/custom.pem. Tenga en cuenta que si realiza cambios en la configuración de TLS, debe guardar los cambios antes de hacer clic en \"Comprobar configuración de sincronización\"."
|
||||
@@ -1018,6 +1027,9 @@
|
||||
"Delete attachment \"%s\"?": [
|
||||
"¿Eliminar el archivo adjunto \"%s\"?"
|
||||
],
|
||||
"Delete column": [
|
||||
"Borrar columna"
|
||||
],
|
||||
"Delete expired sessions": [
|
||||
"Borrar sesiones expiradas"
|
||||
],
|
||||
@@ -1045,6 +1057,9 @@
|
||||
"Delete profile \"%s\"": [
|
||||
"¿Borrar perfil \"%s\"?"
|
||||
],
|
||||
"Delete row": [
|
||||
"Borrar fila"
|
||||
],
|
||||
"Delete selected notes": [
|
||||
"Borrar notas seleccionadas"
|
||||
],
|
||||
@@ -1178,7 +1193,7 @@
|
||||
"¡No pierda la contraseña ya que, por motivos de seguridad, esta será la *única* forma de descifrar los datos! Para habilitar el cifrado, ingrese su contraseña a continuación."
|
||||
],
|
||||
"Do you find the Joplin web app useful?": [
|
||||
""
|
||||
"¿Le resulta útil la aplicación web de Joplin?"
|
||||
],
|
||||
"Document scanner: Title template": [
|
||||
"Escáner de documentos: Plantilla de título"
|
||||
@@ -1537,6 +1552,9 @@
|
||||
"Expand all notebooks": [
|
||||
"Expandir todas las libretas"
|
||||
],
|
||||
"Expand title": [
|
||||
"Expandir título"
|
||||
],
|
||||
"Expanded": [
|
||||
"Expandido"
|
||||
],
|
||||
@@ -1601,7 +1619,7 @@
|
||||
"Feature flags"
|
||||
],
|
||||
"Feedback": [
|
||||
""
|
||||
"Comentarios"
|
||||
],
|
||||
"Fetched items: %d/%d.": [
|
||||
"Elementos obtenidos: %d/%d."
|
||||
@@ -2015,6 +2033,9 @@
|
||||
"Joplin Server": [
|
||||
"Servidor de Joplin"
|
||||
],
|
||||
"Joplin Server (SAML)": [
|
||||
"Servidor Joplin (SAML)"
|
||||
],
|
||||
"Joplin Server Business": [
|
||||
"Servidor Joplin para empresas"
|
||||
],
|
||||
@@ -2034,7 +2055,7 @@
|
||||
"Autenticación SSO de Joplin"
|
||||
],
|
||||
"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.": [
|
||||
""
|
||||
"Joplin permite guardar la ubicación en la que se guardan o crean las notas. ¿Quieres habilitarlo? Esto se puede cambiar en cualquier momento en la configuración."
|
||||
],
|
||||
"Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.": [
|
||||
"El Web Clipper de Joplin le permite guardar páginas web y capturas de pantalla desde su navegador a la aplicación."
|
||||
@@ -2067,7 +2088,7 @@
|
||||
"Claves que necesitan actualizarse"
|
||||
],
|
||||
"Label": [
|
||||
""
|
||||
"Etiqueta"
|
||||
],
|
||||
"Landscape": [
|
||||
"Apaisado"
|
||||
@@ -2224,6 +2245,9 @@
|
||||
"Markdown editor": [
|
||||
"Editor de Markdown"
|
||||
],
|
||||
"Markdown editor: Highlight active line": [
|
||||
"Editor de Markdown: Resaltar línea activa"
|
||||
],
|
||||
"Markdown editor: Render images": [
|
||||
"Editor de Markdown: Renderizar imágenes"
|
||||
],
|
||||
@@ -2484,7 +2508,7 @@
|
||||
"Ahora no"
|
||||
],
|
||||
"Not useful": [
|
||||
""
|
||||
"No es útil"
|
||||
],
|
||||
"note": [
|
||||
"nota"
|
||||
@@ -2534,6 +2558,9 @@
|
||||
"Note list style": [
|
||||
"Estilo de lista de nota"
|
||||
],
|
||||
"Note not published: %s": [
|
||||
"Nota no publicada: %s"
|
||||
],
|
||||
"Note preview": [
|
||||
"Vista previa de la nota"
|
||||
],
|
||||
@@ -2694,7 +2721,7 @@
|
||||
"Lista ordenada"
|
||||
],
|
||||
"Other": [
|
||||
""
|
||||
"Otro"
|
||||
],
|
||||
"Other applications...": [
|
||||
"Otras aplicaciones..."
|
||||
@@ -2946,6 +2973,9 @@
|
||||
"Publish Note": [
|
||||
"Publicar nota"
|
||||
],
|
||||
"Publish note \"%s\" (in notebook \"%s\")?": [
|
||||
"¿Publicar la nota \"%s\" (en la libreta \"%s\")?"
|
||||
],
|
||||
"Publish note...": [
|
||||
"Publicar nota..."
|
||||
],
|
||||
@@ -2958,8 +2988,11 @@
|
||||
"Publish/unpublish": [
|
||||
"Publicar/despublicar"
|
||||
],
|
||||
"Published at URL: %s": [
|
||||
"Publicado en URL: %s"
|
||||
],
|
||||
"Publishes a note to Joplin Server or Joplin Cloud": [
|
||||
""
|
||||
"Publica una nota en Joplin Server o Joplin Cloud"
|
||||
],
|
||||
"QR Code": [
|
||||
"Código QR"
|
||||
@@ -3006,6 +3039,9 @@
|
||||
"Recipients:": [
|
||||
"Destinatarios:"
|
||||
],
|
||||
"Recognise text:": [
|
||||
"Reconocer texto:"
|
||||
],
|
||||
"Recognize handwritten image": [
|
||||
"Reconocer imagen manuscrita"
|
||||
],
|
||||
@@ -3234,6 +3270,9 @@
|
||||
"Save geo-location with notes": [
|
||||
"Guardar geolocalización en las notas"
|
||||
],
|
||||
"Save geolocation?": [
|
||||
"¿Guardar la geolocalización?"
|
||||
],
|
||||
"Scan notebook": [
|
||||
"Escanear libreta"
|
||||
],
|
||||
@@ -3304,7 +3343,7 @@
|
||||
"Seleccionar libreta"
|
||||
],
|
||||
"Select one of the other supported sync targets.": [
|
||||
""
|
||||
"Seleccione uno de los otros destinos de sincronización admitidos."
|
||||
],
|
||||
"Select parent notebook": [
|
||||
"Seleccionar libreta principal"
|
||||
@@ -3643,7 +3682,7 @@
|
||||
"Cambia a [notebook] - todas las demás operaciones se realizarán en esta libreta."
|
||||
],
|
||||
"Sync": [
|
||||
""
|
||||
"Sincronizar"
|
||||
],
|
||||
"Sync as many devices as you want": [
|
||||
"Sincronice todos los dispositivos que desee"
|
||||
@@ -3723,6 +3762,9 @@
|
||||
"Take photo": [
|
||||
"Tomar foto"
|
||||
],
|
||||
"Take survey": [
|
||||
"Responder encuesta"
|
||||
],
|
||||
"Task \"%s\" failed with error: %s": [
|
||||
"Error en la tarea \"%s\" con error: %s"
|
||||
],
|
||||
@@ -3742,7 +3784,7 @@
|
||||
"Comando de editor de texto"
|
||||
],
|
||||
"Thank you for the feedback!\nDo you have time to complete a short survey?": [
|
||||
""
|
||||
"¡Gracias por los comentarios!\n¿Tienes tiempo para completar una breve encuesta?"
|
||||
],
|
||||
"The active profile cannot be deleted. Switch to a different profile and try again.": [
|
||||
"El perfil activo no puede eliminarse. Cambie de perfil e intente de nuevo."
|
||||
@@ -4142,6 +4184,9 @@
|
||||
"Unable to share log data. Reason: %s": [
|
||||
"No se pueden compartir los datos de registro. Motivo: %s"
|
||||
],
|
||||
"Unable to share note data. Reason: %s": [
|
||||
"No se pueden compartir los datos de la nota. Motivo: %s"
|
||||
],
|
||||
"Unchecked": [
|
||||
"No marcado"
|
||||
],
|
||||
@@ -4287,7 +4332,7 @@
|
||||
"Se utiliza donde se necesita una fuente de ancho fijo para presentar el texto de manera legible (por ejemplo, tablas, casillas de verificación, código). Si no se encuentra, se utiliza una fuente genérica monoespaciada (ancho fijo)."
|
||||
],
|
||||
"Useful": [
|
||||
""
|
||||
"Útil"
|
||||
],
|
||||
"User deletions": [
|
||||
"Eliminaciones de usuarios"
|
||||
@@ -4395,7 +4440,7 @@
|
||||
"Al crear una tarea nueva:"
|
||||
],
|
||||
"When enabled, requests that the images in the note be transcribed with a higher-quality on-server transcription service. Requires sync with a copy of the desktop app.": [
|
||||
""
|
||||
"Cuando está activada, solicita que las imágenes de la nota se transcriban con un servicio de transcripción en el servidor de mayor calidad. Requiere sincronización con una copia de la aplicación de escritorio."
|
||||
],
|
||||
"When enabled, the application will scan your attachments and extract the text from it. This will allow you to search for text in these attachments.": [
|
||||
"Cuando está habilitado, la aplicación escaneará sus archivos adjuntos y extraerá el texto de ellos. Esto le permitirá buscar texto en estos archivos adjuntos."
|
||||
|
||||
@@ -167,6 +167,9 @@
|
||||
"Add body": [
|
||||
"Lisa keha"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add or remove tags:": [
|
||||
"Siltide lisamine või eemaldamine:"
|
||||
],
|
||||
|
||||
@@ -162,6 +162,9 @@
|
||||
"Add body": [
|
||||
""
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add or remove tags:": [
|
||||
"Gehitu edo ezabatu etiketak:"
|
||||
],
|
||||
|
||||
@@ -200,6 +200,9 @@
|
||||
"Add body": [
|
||||
"افزودن بدنه"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"افزودن جدید"
|
||||
],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -242,6 +242,9 @@
|
||||
"Add body": [
|
||||
"Ajoutez le contenu"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Ajouter"
|
||||
],
|
||||
|
||||
@@ -230,6 +230,9 @@
|
||||
"Add body": [
|
||||
"Engadir corpo"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Engadir novo"
|
||||
],
|
||||
|
||||
@@ -248,6 +248,9 @@
|
||||
"Add body": [
|
||||
"Dodaj sadržaj"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Dodaj novu"
|
||||
],
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
"– Kamera: lehetővé teszi a képkészítést és a képek mellékelését a jegyzetekhez."
|
||||
],
|
||||
"- Location: to allow attaching geo-location information to a note.": [
|
||||
"– Helyszín: lehetővé teszi a földrajzi helymeghatározási információk mellékelését a jegyzetekhez."
|
||||
"– Hely: lehetővé teszi a földrajzi helymeghatározási információk mellékelését a jegyzetekhez."
|
||||
],
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem synchronisation.": [
|
||||
"– Tárhely: lehetővé teszi a fájlrendszer szinkronizálását és a fájlok mellékelését a jegyzetekhez."
|
||||
@@ -168,7 +168,7 @@
|
||||
"[Semmi]"
|
||||
],
|
||||
"A brief description of the image:": [
|
||||
""
|
||||
"A kép rövid leírása:"
|
||||
],
|
||||
"A comma-separated list of words. May be used for uncommon words, to help voice typing spell them correctly.": [
|
||||
"Szavak vesszővel elválasztott listája. Használható a nem túl gyakori szavak esetében, hogy segítse a hangalapú gépelést a helyesírásban."
|
||||
@@ -242,6 +242,9 @@
|
||||
"Add body": [
|
||||
"Törzsszöveg hozzáadása"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Új hozzáadása"
|
||||
],
|
||||
@@ -339,7 +342,7 @@
|
||||
"Automatikusan mentett rajz megtalálva. Mellékeli a másolatát a jegyzethez?"
|
||||
],
|
||||
"An error occurred while sending the response. This can happen if the app is offline or cannot connect to the server.\nError: %s": [
|
||||
""
|
||||
"Hiba történt a válasz elküldésekor. Ez akkor fordulhat elő, ha az alkalmazás offline állapotban van, vagy nem tud csatlakozni a kiszolgálóhoz.\nHiba: %s"
|
||||
],
|
||||
"An error occurred: %s": [
|
||||
"Hiba történt: %s"
|
||||
@@ -558,7 +561,7 @@
|
||||
"A titkosított jegyzetfüzetet nem lehet megosztani a következő címzettel: %s, mert az nem engedélyezte a végpontok közötti titkosítást. Ezt megteheti a Beállítások > Titkosítás menüben."
|
||||
],
|
||||
"Case sensitive": [
|
||||
"Kis-nagybetű érzékeny"
|
||||
"Kis- és nagybetűk megkülönböztetése"
|
||||
],
|
||||
"Change application layout": [
|
||||
"Alkalmazás elrendezésének módosítása"
|
||||
@@ -680,6 +683,9 @@
|
||||
"Collapse all notebooks": [
|
||||
"Összes jegyzetfüzet összecsukása"
|
||||
],
|
||||
"Collapse title": [
|
||||
"Cím összecsukása"
|
||||
],
|
||||
"Collapsed": [
|
||||
"Összecsukva"
|
||||
],
|
||||
@@ -836,7 +842,7 @@
|
||||
"Az alkalmazás nem hitelesíthető:\n\n%s\n\nPróbálja újra."
|
||||
],
|
||||
"Could not connect to Joplin Server. Please check the Synchronisation options in the config screen. Full error was:\n\n%s": [
|
||||
"Nem sikerült kapcsolódni a Joplin kiszolgálóhoz. Ellenőrizze a szinkronizálási beállításokat. A teljes hiba a következő volt:\n\n%s"
|
||||
"Nem sikerült kapcsolódni a Joplin Serverhez. Ellenőrizze a szinkronizálási beállításokat. A teljes hiba a következő volt:\n\n%s"
|
||||
],
|
||||
"Could not connect to plugin repository.": [
|
||||
"Nem sikerült kapcsolódni a bővítménytárolóhoz."
|
||||
@@ -1139,7 +1145,7 @@
|
||||
"Elvetés"
|
||||
],
|
||||
"Displays a geolocation URL for the note.": [
|
||||
"Megjeleníti a jegyzethez tartozó földrajzi helyszín webcímét."
|
||||
"Megjeleníti a jegyzethez tartozó földrajzi hely webcímét."
|
||||
],
|
||||
"Displays only the first top <num> notes.": [
|
||||
"Csak az első <num> leggyakrabban használt jegyzetet jeleníti meg."
|
||||
@@ -1178,7 +1184,7 @@
|
||||
"Ne veszítse el a jelszót, mert biztonsági okokból ez lesz az *egyetlen* módja az adatok visszafejtésének! A titkosítás engedélyezéséhez adja meg a jelszavát alább."
|
||||
],
|
||||
"Do you find the Joplin web app useful?": [
|
||||
""
|
||||
"Hasznosnak találja a Joplin webalkalmazását?"
|
||||
],
|
||||
"Document scanner: Title template": [
|
||||
"Dokumentumbeolvasó: Címsablon"
|
||||
@@ -1537,6 +1543,9 @@
|
||||
"Expand all notebooks": [
|
||||
"Összes jegyzetfüzet kibontása"
|
||||
],
|
||||
"Expand title": [
|
||||
"Cím kibontása"
|
||||
],
|
||||
"Expanded": [
|
||||
"Kibontva"
|
||||
],
|
||||
@@ -1601,7 +1610,7 @@
|
||||
"Funkciójelölők"
|
||||
],
|
||||
"Feedback": [
|
||||
""
|
||||
"Visszajelzés"
|
||||
],
|
||||
"Fetched items: %d/%d.": [
|
||||
"Lekérdezett elemek: %d/%d."
|
||||
@@ -2013,28 +2022,31 @@
|
||||
"Joplin mobilalkalmazás"
|
||||
],
|
||||
"Joplin Server": [
|
||||
"Joplin kiszolgáló"
|
||||
"Joplin Server"
|
||||
],
|
||||
"Joplin Server (SAML)": [
|
||||
"Joplin Server (SAML)"
|
||||
],
|
||||
"Joplin Server Business": [
|
||||
"Üzleti Joplin kiszolgáló"
|
||||
"Üzleti Joplin Server"
|
||||
],
|
||||
"Joplin Server email": [
|
||||
"Joplin kiszolgáló e-mail-címe"
|
||||
"Joplin Server e-mail-címe"
|
||||
],
|
||||
"Joplin Server Login": [
|
||||
"Bejelentkezés a Joplin kiszolgálóba"
|
||||
"Bejelentkezés a Joplin Serverbe"
|
||||
],
|
||||
"Joplin Server password": [
|
||||
"Joplin kiszolgáló jelszava"
|
||||
"Joplin Server jelszava"
|
||||
],
|
||||
"Joplin Server URL": [
|
||||
"Joplin kiszolgáló webcíme"
|
||||
"Joplin Server webcíme"
|
||||
],
|
||||
"Joplin SSO Authentication": [
|
||||
"Joplin SSO-hitelesítés"
|
||||
],
|
||||
"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.": [
|
||||
""
|
||||
"A Joplin támogatja annak a helynek a mentését, amelybe a jegyzetek el lettek mentve vagy létre lettek hozva. Szeretné engedélyezni? Ez a beállítás bármikor módosítható a beállításokban."
|
||||
],
|
||||
"Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.": [
|
||||
"A Joplin Web Clipper lehetővé teszi weboldalak és képernyőképek mentését a böngészőből a Joplin alkalmazásba."
|
||||
@@ -2067,7 +2079,7 @@
|
||||
"Frissítésre szoruló kulcsok"
|
||||
],
|
||||
"Label": [
|
||||
""
|
||||
"Címke"
|
||||
],
|
||||
"Landscape": [
|
||||
"Fekvő"
|
||||
@@ -2156,7 +2168,7 @@
|
||||
"Betöltés…"
|
||||
],
|
||||
"Location": [
|
||||
"Helyszín"
|
||||
"Hely"
|
||||
],
|
||||
"Lock file is already being hold. If you know that no synchronisation is taking place, you may delete the lock file at \"%s\" and resume the operation.": [
|
||||
"A zárolási fájl már használatban van. Ha biztos benne, hogy nem történik szinkronizálás, törölheti a zárolási fájlt itt: „%s”, és folytathatja a műveletet."
|
||||
@@ -2224,6 +2236,9 @@
|
||||
"Markdown editor": [
|
||||
"Markdown szerkesztő"
|
||||
],
|
||||
"Markdown editor: Highlight active line": [
|
||||
"Markdown szerkesztő: Aktív sor kiemelése"
|
||||
],
|
||||
"Markdown editor: Render images": [
|
||||
"Markdown szerkesztő: képek renderelése"
|
||||
],
|
||||
@@ -2484,7 +2499,7 @@
|
||||
"Most nem"
|
||||
],
|
||||
"Not useful": [
|
||||
""
|
||||
"Nem hasznos"
|
||||
],
|
||||
"note": [
|
||||
"jegyzet"
|
||||
@@ -2534,6 +2549,9 @@
|
||||
"Note list style": [
|
||||
"Jegyzetlista stílusa"
|
||||
],
|
||||
"Note not published: %s": [
|
||||
"A jegyzet nem lett közzétéve: %s"
|
||||
],
|
||||
"Note preview": [
|
||||
"Jegyzet előnézete"
|
||||
],
|
||||
@@ -2694,7 +2712,7 @@
|
||||
"Számozott lista"
|
||||
],
|
||||
"Other": [
|
||||
""
|
||||
"Egyéb"
|
||||
],
|
||||
"Other applications...": [
|
||||
"További alkalmazások…"
|
||||
@@ -2946,6 +2964,9 @@
|
||||
"Publish Note": [
|
||||
"Jegyzet közzététele"
|
||||
],
|
||||
"Publish note \"%s\" (in notebook \"%s\")?": [
|
||||
"Közzéteszi a(z) „%s” jegyzetet (a(z) „%s” jegyzetfüzetből)?"
|
||||
],
|
||||
"Publish note...": [
|
||||
"Jegyzet közzététele…"
|
||||
],
|
||||
@@ -2958,8 +2979,11 @@
|
||||
"Publish/unpublish": [
|
||||
"Közzététel / Közzététel megszüntetése"
|
||||
],
|
||||
"Published at URL: %s": [
|
||||
"Közzétéve a következő webcímen: %s"
|
||||
],
|
||||
"Publishes a note to Joplin Server or Joplin Cloud": [
|
||||
""
|
||||
"Megjegyzés közzététele a Joplin Serveren vagy a Joplin Cloudon"
|
||||
],
|
||||
"QR Code": [
|
||||
"QR-kód"
|
||||
@@ -3006,6 +3030,9 @@
|
||||
"Recipients:": [
|
||||
"Címzettek:"
|
||||
],
|
||||
"Recognise text:": [
|
||||
"Szöveg felismerése:"
|
||||
],
|
||||
"Recognize handwritten image": [
|
||||
"Kézzel írt kép felismerése"
|
||||
],
|
||||
@@ -3031,7 +3058,7 @@
|
||||
"Frissítés"
|
||||
],
|
||||
"Regular expression": [
|
||||
"Reguláris kifejezés"
|
||||
"Reguláris kifejezések"
|
||||
],
|
||||
"Reject": [
|
||||
"Elutasítás"
|
||||
@@ -3232,7 +3259,10 @@
|
||||
"Módosítások mentése?"
|
||||
],
|
||||
"Save geo-location with notes": [
|
||||
"Földrajzi helyszín mentése a jegyzetekhez"
|
||||
"Földrajzi hely mentése a jegyzetekhez"
|
||||
],
|
||||
"Save geolocation?": [
|
||||
"Menti a földrajzi helyet?"
|
||||
],
|
||||
"Scan notebook": [
|
||||
"Jegyzetfüzet beolvasása"
|
||||
@@ -3304,7 +3334,7 @@
|
||||
"Jegyzetfüzet kiválasztása"
|
||||
],
|
||||
"Select one of the other supported sync targets.": [
|
||||
""
|
||||
"Válasszon ki egy másik támogatott szinkronizálási célt."
|
||||
],
|
||||
"Select parent notebook": [
|
||||
"Szülő jegyzetfüzet kijelölése"
|
||||
@@ -3379,7 +3409,7 @@
|
||||
"Megosztások"
|
||||
],
|
||||
"Shares or unshares the specified [notebook] with [user]. Requires Joplin Cloud or Joplin Server.": [
|
||||
"Engedélyezi vagy megszünteti a megadott [jegyzetfüzet] megosztását a [felhasználóval]. Joplin Cloud vagy Joplin kiszolgáló szükséges hozzá."
|
||||
"Engedélyezi vagy megszünteti a megadott [jegyzetfüzet] megosztását a [felhasználóval]. Joplin Cloud vagy Joplin Server szükséges hozzá."
|
||||
],
|
||||
"Sharing notebook...": [
|
||||
"Jegyzetfüzet megosztása…"
|
||||
@@ -3643,7 +3673,7 @@
|
||||
"Váltás a(z) [notebook] jegyzetfüzetre – minden további művelet ebben a jegyzetfüzetben történik."
|
||||
],
|
||||
"Sync": [
|
||||
""
|
||||
"Szinkronizálás"
|
||||
],
|
||||
"Sync as many devices as you want": [
|
||||
"Szinkronizáljon annyi eszközt, amennyit csak akar"
|
||||
@@ -3723,6 +3753,9 @@
|
||||
"Take photo": [
|
||||
"Kép készítése"
|
||||
],
|
||||
"Take survey": [
|
||||
"Kérdőív kitöltése"
|
||||
],
|
||||
"Task \"%s\" failed with error: %s": [
|
||||
"A(z) „%s” feladat sikertelen volt a következő hibával: %s"
|
||||
],
|
||||
@@ -3742,7 +3775,7 @@
|
||||
"Szövegszerkesztő-parancs"
|
||||
],
|
||||
"Thank you for the feedback!\nDo you have time to complete a short survey?": [
|
||||
""
|
||||
"Köszönjük a visszajelzést!\nVan ideje kitölteni egy rövid kérdőívet?"
|
||||
],
|
||||
"The active profile cannot be deleted. Switch to a different profile and try again.": [
|
||||
"Az aktív profil nem törölhető. Váltson másik profilra, és próbálja újra."
|
||||
@@ -4142,6 +4175,9 @@
|
||||
"Unable to share log data. Reason: %s": [
|
||||
"A naplóadatok megosztása nem lehetséges. Indok: %s"
|
||||
],
|
||||
"Unable to share note data. Reason: %s": [
|
||||
"Nem lehet a jegyzet adatait megosztani. Indok: %s"
|
||||
],
|
||||
"Unchecked": [
|
||||
"Nem ellenőrzött"
|
||||
],
|
||||
@@ -4287,7 +4323,7 @@
|
||||
"Ott használatos, ahol a szöveg olvasható elrendezéséhez fix szélességű betűtípusra van szükség (például: táblázatok, jelölőnégyzetek, kódok). Ha nem található, akkor egy általános monospace (fix szélességű) betűtípus lesz használva."
|
||||
],
|
||||
"Useful": [
|
||||
""
|
||||
"Hasznos"
|
||||
],
|
||||
"User deletions": [
|
||||
"Felhasználói törlések"
|
||||
@@ -4395,7 +4431,7 @@
|
||||
"Új teendő létrehozásakor:"
|
||||
],
|
||||
"When enabled, requests that the images in the note be transcribed with a higher-quality on-server transcription service. Requires sync with a copy of the desktop app.": [
|
||||
""
|
||||
"Ha engedélyezve van, akkor a jegyzetben lévő képek átírása egy jobb minőségű, kiszolgálóoldali átírási szolgáltatással történik. Ehhez szinkronizáció szükséges az asztali alkalmazással."
|
||||
],
|
||||
"When enabled, the application will scan your attachments and extract the text from it. This will allow you to search for text in these attachments.": [
|
||||
"Amikor engedélyezve van, az alkalmazás beolvassa a mellékleteket, és kivonatolja belőlük a szöveget. Ez lehetővé teszi a szöveg keresését ezekben a mellékletekben."
|
||||
|
||||
@@ -236,6 +236,9 @@
|
||||
"Add body": [
|
||||
"Tambahkan isi"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Tambah baru"
|
||||
],
|
||||
|
||||
@@ -74,7 +74,7 @@ stats['nb_NO'] = {
|
||||
},
|
||||
};
|
||||
stats['bs_BA'] = {
|
||||
percentDone: 38,
|
||||
percentDone: 37,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
@@ -94,7 +94,7 @@ stats['bg_BG'] = {
|
||||
},
|
||||
};
|
||||
stats['ca'] = {
|
||||
percentDone: 98,
|
||||
percentDone: 100,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
@@ -168,7 +168,7 @@ stats['en_US'] = {
|
||||
},
|
||||
};
|
||||
stats['es_ES'] = {
|
||||
percentDone: 98,
|
||||
percentDone: 100,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
@@ -188,7 +188,7 @@ stats['eo'] = {
|
||||
},
|
||||
};
|
||||
stats['fi_FI'] = {
|
||||
percentDone: 77,
|
||||
percentDone: 89,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
@@ -238,7 +238,7 @@ stats['it_IT'] = {
|
||||
},
|
||||
};
|
||||
stats['hu_HU'] = {
|
||||
percentDone: 98,
|
||||
percentDone: 99,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
@@ -258,7 +258,7 @@ stats['nl_BE'] = {
|
||||
},
|
||||
};
|
||||
stats['nl_NL'] = {
|
||||
percentDone: 67,
|
||||
percentDone: 79,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
@@ -278,7 +278,7 @@ stats['fa'] = {
|
||||
},
|
||||
};
|
||||
stats['pl_PL'] = {
|
||||
percentDone: 94,
|
||||
percentDone: 93,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
@@ -308,7 +308,7 @@ stats['pt_PT'] = {
|
||||
},
|
||||
};
|
||||
stats['ro_MD'] = {
|
||||
percentDone: 98,
|
||||
percentDone: 99,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
@@ -318,7 +318,7 @@ stats['ro_MD'] = {
|
||||
},
|
||||
};
|
||||
stats['ro_RO'] = {
|
||||
percentDone: 98,
|
||||
percentDone: 99,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
@@ -338,7 +338,7 @@ stats['sl_SI'] = {
|
||||
},
|
||||
};
|
||||
stats['sk_SK'] = {
|
||||
percentDone: 98,
|
||||
percentDone: 100,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
@@ -378,7 +378,7 @@ stats['vi'] = {
|
||||
},
|
||||
};
|
||||
stats['tr_TR'] = {
|
||||
percentDone: 98,
|
||||
percentDone: 99,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
@@ -408,7 +408,7 @@ stats['el_GR'] = {
|
||||
},
|
||||
};
|
||||
stats['ru_RU'] = {
|
||||
percentDone: 86,
|
||||
percentDone: 98,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
@@ -418,7 +418,7 @@ stats['ru_RU'] = {
|
||||
},
|
||||
};
|
||||
stats['sr_RS'] = {
|
||||
percentDone: 43,
|
||||
percentDone: 42,
|
||||
pluralForms: function(n) {
|
||||
// AUTO-GENERATED by build-translations.ts
|
||||
var plural;
|
||||
|
||||
@@ -200,6 +200,9 @@
|
||||
"Add body": [
|
||||
"Aggiungi corpo"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Aggiungi nuova"
|
||||
],
|
||||
|
||||
@@ -185,6 +185,9 @@
|
||||
"Add body": [
|
||||
"本文を追加"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"新規追加"
|
||||
],
|
||||
|
||||
@@ -188,6 +188,9 @@
|
||||
"Add body": [
|
||||
"내용 추가"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"새로 추가"
|
||||
],
|
||||
|
||||
@@ -182,6 +182,9 @@
|
||||
"Add body": [
|
||||
"Legg til brødtekst"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add or remove tags:": [
|
||||
"Legge til eller fjern merkelapper:"
|
||||
],
|
||||
|
||||
@@ -203,6 +203,9 @@
|
||||
"Add body": [
|
||||
"Inhoud toevoegen"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Nieuw toevoegen"
|
||||
],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -248,6 +248,9 @@
|
||||
"Add body": [
|
||||
"Dodaj treść"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Dodaj nowy"
|
||||
],
|
||||
|
||||
@@ -230,6 +230,9 @@
|
||||
"Add body": [
|
||||
"Adicionar corpo"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Adicionar"
|
||||
],
|
||||
|
||||
@@ -200,6 +200,9 @@
|
||||
"Add body": [
|
||||
"Adicionar corpo"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Adicionar novo"
|
||||
],
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
"[niciuna]"
|
||||
],
|
||||
"A brief description of the image:": [
|
||||
""
|
||||
"O descriere sumară a imaginii:"
|
||||
],
|
||||
"A comma-separated list of words. May be used for uncommon words, to help voice typing spell them correctly.": [
|
||||
"O listă de cuvinte separate de virgule. Poate fi folosită pentru cuvinte mai puțin comune, pentru a ajuta transcrierea prin dictare să le scrie corect."
|
||||
@@ -248,6 +248,9 @@
|
||||
"Add body": [
|
||||
"Adaugă corp"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Adaugă un nou"
|
||||
],
|
||||
@@ -345,7 +348,7 @@
|
||||
"A fost găsit un desen salvat automat. Atașezi o copie a acestuia la notiță?"
|
||||
],
|
||||
"An error occurred while sending the response. This can happen if the app is offline or cannot connect to the server.\nError: %s": [
|
||||
""
|
||||
"A apărut o eroare la trimiterea răspunsului. Acest lucru se poate întîmpla dacă aplicația este offline sau nu se poate conecta la server.\nEroare: %s"
|
||||
],
|
||||
"An error occurred: %s": [
|
||||
"S-a produs o eroare: %s"
|
||||
@@ -686,6 +689,9 @@
|
||||
"Collapse all notebooks": [
|
||||
"Restrînge toate caietele"
|
||||
],
|
||||
"Collapse title": [
|
||||
"Restrînge titlul"
|
||||
],
|
||||
"Collapsed": [
|
||||
"Restrîns"
|
||||
],
|
||||
@@ -1186,7 +1192,7 @@
|
||||
"Nu pierde parola, deoarece din motive de securitate, aceasta este *unica* modalitate de decriptare a datelor! Pentru a activa criptarea, te rog să întroduci parola mai jos."
|
||||
],
|
||||
"Do you find the Joplin web app useful?": [
|
||||
""
|
||||
"Consideri că aplicația web Joplin este utilă?"
|
||||
],
|
||||
"Document scanner: Title template": [
|
||||
"Scanner documente: Șablon titlu"
|
||||
@@ -1545,6 +1551,9 @@
|
||||
"Expand all notebooks": [
|
||||
"Extinde toate caietele"
|
||||
],
|
||||
"Expand title": [
|
||||
"Extinde titlul"
|
||||
],
|
||||
"Expanded": [
|
||||
"Extins"
|
||||
],
|
||||
@@ -1609,7 +1618,7 @@
|
||||
"Feature Flags"
|
||||
],
|
||||
"Feedback": [
|
||||
""
|
||||
"Feedback"
|
||||
],
|
||||
"Fetched items: %d/%d.": [
|
||||
"Elemente preluate: %d/%d."
|
||||
@@ -2024,6 +2033,9 @@
|
||||
"Joplin Server": [
|
||||
"Server Joplin"
|
||||
],
|
||||
"Joplin Server (SAML)": [
|
||||
"Server Joplin (SAML)"
|
||||
],
|
||||
"Joplin Server Business": [
|
||||
"Server Joplin Business"
|
||||
],
|
||||
@@ -2043,7 +2055,7 @@
|
||||
"Autentificare SSO Joplin"
|
||||
],
|
||||
"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.": [
|
||||
""
|
||||
"Joplin suportă salvarea locației în care notițele sînt salvate sau create. Dorești să o activezi? Acest lucru poate fi modificat oricînd din setări."
|
||||
],
|
||||
"Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.": [
|
||||
"Joplin Web Clipper permite salvarea paginilor web și a capturilor de ecran din navigatorul tău web în Joplin."
|
||||
@@ -2076,7 +2088,7 @@
|
||||
"Chei care trebuie actualizate"
|
||||
],
|
||||
"Label": [
|
||||
""
|
||||
"Etichetă"
|
||||
],
|
||||
"Landscape": [
|
||||
"Peisaj"
|
||||
@@ -2235,6 +2247,9 @@
|
||||
"Markdown editor": [
|
||||
"Editor Markdown"
|
||||
],
|
||||
"Markdown editor: Highlight active line": [
|
||||
"Editor Markdown: Evidențiază linia activă"
|
||||
],
|
||||
"Markdown editor: Render images": [
|
||||
"Editor Markdown: Redă imaginile"
|
||||
],
|
||||
@@ -2496,7 +2511,7 @@
|
||||
"Nu acum"
|
||||
],
|
||||
"Not useful": [
|
||||
""
|
||||
"Nu este utilă"
|
||||
],
|
||||
"note": [
|
||||
"notiță"
|
||||
@@ -2546,6 +2561,9 @@
|
||||
"Note list style": [
|
||||
"Stilul listei de notițe"
|
||||
],
|
||||
"Note not published: %s": [
|
||||
"Notița nu a fost publicată: %s"
|
||||
],
|
||||
"Note preview": [
|
||||
"Previzualizare notiță"
|
||||
],
|
||||
@@ -2706,7 +2724,7 @@
|
||||
"Listă ordonată"
|
||||
],
|
||||
"Other": [
|
||||
""
|
||||
"Alta"
|
||||
],
|
||||
"Other applications...": [
|
||||
"Alte aplicații…"
|
||||
@@ -2959,6 +2977,9 @@
|
||||
"Publish Note": [
|
||||
"Publică notița"
|
||||
],
|
||||
"Publish note \"%s\" (in notebook \"%s\")?": [
|
||||
"Publici notița „%s” (în caietul „%s”)?"
|
||||
],
|
||||
"Publish note...": [
|
||||
"Publică notița…"
|
||||
],
|
||||
@@ -2971,8 +2992,11 @@
|
||||
"Publish/unpublish": [
|
||||
"Publică/Retrage"
|
||||
],
|
||||
"Published at URL: %s": [
|
||||
"Publicat la URL: %s"
|
||||
],
|
||||
"Publishes a note to Joplin Server or Joplin Cloud": [
|
||||
""
|
||||
"Publică o notiță pe un server Joplin sau în Cloud-ul Joplin"
|
||||
],
|
||||
"QR Code": [
|
||||
"Cod QR"
|
||||
@@ -3019,6 +3043,9 @@
|
||||
"Recipients:": [
|
||||
"Destinatari:"
|
||||
],
|
||||
"Recognise text:": [
|
||||
"Recunoaște textul:"
|
||||
],
|
||||
"Recognize handwritten image": [
|
||||
"Recunoaște imagine cu text scris de mînă"
|
||||
],
|
||||
@@ -3247,6 +3274,9 @@
|
||||
"Save geo-location with notes": [
|
||||
"Salvează geo-locația în notițe"
|
||||
],
|
||||
"Save geolocation?": [
|
||||
"Salvezi geo-locația?"
|
||||
],
|
||||
"Scan notebook": [
|
||||
"Scanează un caiet"
|
||||
],
|
||||
@@ -3317,7 +3347,7 @@
|
||||
"Selectează caiet"
|
||||
],
|
||||
"Select one of the other supported sync targets.": [
|
||||
""
|
||||
"Selectează una din celelalte destinații de sincronizare suportate."
|
||||
],
|
||||
"Select parent notebook": [
|
||||
"Selectează caietul părinte"
|
||||
@@ -3656,7 +3686,7 @@
|
||||
"Comută la [notebook] - toate operațiunile ulterioare vor avea loc în acest caiet."
|
||||
],
|
||||
"Sync": [
|
||||
""
|
||||
"Sincronizează"
|
||||
],
|
||||
"Sync as many devices as you want": [
|
||||
"Sincronizează cît de multe dispozitive dorești"
|
||||
@@ -3736,6 +3766,9 @@
|
||||
"Take photo": [
|
||||
"Fotografiază"
|
||||
],
|
||||
"Take survey": [
|
||||
"Completează chestionarul"
|
||||
],
|
||||
"Task \"%s\" failed with error: %s": [
|
||||
"Sarcina „%s” a eșuat cu eroarea: %s"
|
||||
],
|
||||
@@ -3755,7 +3788,7 @@
|
||||
"Comandă editor de text"
|
||||
],
|
||||
"Thank you for the feedback!\nDo you have time to complete a short survey?": [
|
||||
""
|
||||
"Mulțumim pentru feedback!\nAi timp să completezi un scurt chestionar?"
|
||||
],
|
||||
"The active profile cannot be deleted. Switch to a different profile and try again.": [
|
||||
"Profilul activ nu poate fi șters. Comută la un alt profil și încearcă din nou."
|
||||
@@ -4158,7 +4191,10 @@
|
||||
"Nu se poate edita resursa de tip %s"
|
||||
],
|
||||
"Unable to share log data. Reason: %s": [
|
||||
"Imposibilitatea de a partaja datele din jurnal. Motivul: %s"
|
||||
"Imposibil de a partaja datele din jurnal. Motivul: %s"
|
||||
],
|
||||
"Unable to share note data. Reason: %s": [
|
||||
"Imposibil de a partaja datele din notiță. Motivul: %s"
|
||||
],
|
||||
"Unchecked": [
|
||||
"Debifat"
|
||||
@@ -4305,7 +4341,7 @@
|
||||
"Se utilizează atunci cînd este necesar un font cu lățime fixă pentru a prezenta textul în mod lizibil (de exemplu, tabele, căsuțe de selectare, coduri). Dacă nu se găsește, se utilizează un font generic monospace (lățime fixă)."
|
||||
],
|
||||
"Useful": [
|
||||
""
|
||||
"Utilă"
|
||||
],
|
||||
"User deletions": [
|
||||
"Ștergeri de utilizatori"
|
||||
@@ -4413,7 +4449,7 @@
|
||||
"Cînd este creată o nouă listă de făcut:"
|
||||
],
|
||||
"When enabled, requests that the images in the note be transcribed with a higher-quality on-server transcription service. Requires sync with a copy of the desktop app.": [
|
||||
""
|
||||
"Cînd este activat, solicită ca imaginile din notiță să fie transcrise cu un serviciu de calitate mai bună de pe server. Necesită sincronizare cu aplicația desktop."
|
||||
],
|
||||
"When enabled, the application will scan your attachments and extract the text from it. This will allow you to search for text in these attachments.": [
|
||||
"Atunci cînd este activată, aplicația va scana atașamentele și va extrage textul din ele. Acest lucru îți va permite să cauți text în atașamente."
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
"[niciuna]"
|
||||
],
|
||||
"A brief description of the image:": [
|
||||
""
|
||||
"O descriere sumară a imaginii:"
|
||||
],
|
||||
"A comma-separated list of words. May be used for uncommon words, to help voice typing spell them correctly.": [
|
||||
"O listă de cuvinte separate de virgule. Poate fi folosită pentru cuvinte mai puțin comune, pentru a ajuta transcrierea prin dictare să le scrie corect."
|
||||
@@ -248,6 +248,9 @@
|
||||
"Add body": [
|
||||
"Adaugă corp"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Adaugă un nou"
|
||||
],
|
||||
@@ -345,7 +348,7 @@
|
||||
"A fost găsit un desen salvat automat. Atașezi o copie a acestuia la notiță?"
|
||||
],
|
||||
"An error occurred while sending the response. This can happen if the app is offline or cannot connect to the server.\nError: %s": [
|
||||
""
|
||||
"A apărut o eroare la trimiterea răspunsului. Acest lucru se poate întâmpla dacă aplicația este offline sau nu se poate conecta la server.\nEroare: %s"
|
||||
],
|
||||
"An error occurred: %s": [
|
||||
"S-a produs o eroare: %s"
|
||||
@@ -686,6 +689,9 @@
|
||||
"Collapse all notebooks": [
|
||||
"Restrânge toate caietele"
|
||||
],
|
||||
"Collapse title": [
|
||||
"Restrânge titlul"
|
||||
],
|
||||
"Collapsed": [
|
||||
"Restrâns"
|
||||
],
|
||||
@@ -1186,7 +1192,7 @@
|
||||
"Nu pierde parola, deoarece din motive de securitate, aceasta este *unica* modalitate de decriptare a datelor! Pentru a activa criptarea, te rog să introduci parola mai jos."
|
||||
],
|
||||
"Do you find the Joplin web app useful?": [
|
||||
""
|
||||
"Consideri că aplicația web Joplin este utilă?"
|
||||
],
|
||||
"Document scanner: Title template": [
|
||||
"Scanner documente: Șablon titlu"
|
||||
@@ -1545,6 +1551,9 @@
|
||||
"Expand all notebooks": [
|
||||
"Extinde toate caietele"
|
||||
],
|
||||
"Expand title": [
|
||||
"Extinde titlul"
|
||||
],
|
||||
"Expanded": [
|
||||
"Extins"
|
||||
],
|
||||
@@ -1609,7 +1618,7 @@
|
||||
"Feature Flags"
|
||||
],
|
||||
"Feedback": [
|
||||
""
|
||||
"Feedback"
|
||||
],
|
||||
"Fetched items: %d/%d.": [
|
||||
"Elemente preluate: %d/%d."
|
||||
@@ -2024,6 +2033,9 @@
|
||||
"Joplin Server": [
|
||||
"Server Joplin"
|
||||
],
|
||||
"Joplin Server (SAML)": [
|
||||
"Server Joplin (SAML)"
|
||||
],
|
||||
"Joplin Server Business": [
|
||||
"Server Joplin Business"
|
||||
],
|
||||
@@ -2043,7 +2055,7 @@
|
||||
"Autentificare SSO Joplin"
|
||||
],
|
||||
"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.": [
|
||||
""
|
||||
"Joplin suportă salvarea locației în care notițele sunt salvate sau create. Dorești să o activezi? Acest lucru poate fi modificat oricând din setări."
|
||||
],
|
||||
"Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.": [
|
||||
"Joplin Web Clipper permite salvarea paginilor web și a capturilor de ecran din navigatorul tău web în Joplin."
|
||||
@@ -2076,7 +2088,7 @@
|
||||
"Chei care trebuie actualizate"
|
||||
],
|
||||
"Label": [
|
||||
""
|
||||
"Etichetă"
|
||||
],
|
||||
"Landscape": [
|
||||
"Peisaj"
|
||||
@@ -2235,6 +2247,9 @@
|
||||
"Markdown editor": [
|
||||
"Editor Markdown"
|
||||
],
|
||||
"Markdown editor: Highlight active line": [
|
||||
"Editor Markdown: Evidențiază linia activă"
|
||||
],
|
||||
"Markdown editor: Render images": [
|
||||
"Editor Markdown: Redă imaginile"
|
||||
],
|
||||
@@ -2496,7 +2511,7 @@
|
||||
"Nu acum"
|
||||
],
|
||||
"Not useful": [
|
||||
""
|
||||
"Nu este utilă"
|
||||
],
|
||||
"note": [
|
||||
"notiță"
|
||||
@@ -2546,6 +2561,9 @@
|
||||
"Note list style": [
|
||||
"Stilul listei de notițe"
|
||||
],
|
||||
"Note not published: %s": [
|
||||
"Notița nu a fost publicată: %s"
|
||||
],
|
||||
"Note preview": [
|
||||
"Previzualizare notiță"
|
||||
],
|
||||
@@ -2706,7 +2724,7 @@
|
||||
"Listă ordonată"
|
||||
],
|
||||
"Other": [
|
||||
""
|
||||
"Alta"
|
||||
],
|
||||
"Other applications...": [
|
||||
"Alte aplicații…"
|
||||
@@ -2959,6 +2977,9 @@
|
||||
"Publish Note": [
|
||||
"Publică notița"
|
||||
],
|
||||
"Publish note \"%s\" (in notebook \"%s\")?": [
|
||||
"Publici notița „%s” (în caietul „%s”)?"
|
||||
],
|
||||
"Publish note...": [
|
||||
"Publică notița…"
|
||||
],
|
||||
@@ -2971,8 +2992,11 @@
|
||||
"Publish/unpublish": [
|
||||
"Publică/Retrage"
|
||||
],
|
||||
"Published at URL: %s": [
|
||||
"Publicat la URL: %s"
|
||||
],
|
||||
"Publishes a note to Joplin Server or Joplin Cloud": [
|
||||
""
|
||||
"Publică o notiță pe un server Joplin sau în Cloud-ul Joplin"
|
||||
],
|
||||
"QR Code": [
|
||||
"Cod QR"
|
||||
@@ -3019,6 +3043,9 @@
|
||||
"Recipients:": [
|
||||
"Destinatari:"
|
||||
],
|
||||
"Recognise text:": [
|
||||
"Recunoaște textul:"
|
||||
],
|
||||
"Recognize handwritten image": [
|
||||
"Recunoaște imagine cu text scris de mână"
|
||||
],
|
||||
@@ -3247,6 +3274,9 @@
|
||||
"Save geo-location with notes": [
|
||||
"Salvează geo-locația în notițe"
|
||||
],
|
||||
"Save geolocation?": [
|
||||
"Salvezi geo-locația?"
|
||||
],
|
||||
"Scan notebook": [
|
||||
"Scanează un caiet"
|
||||
],
|
||||
@@ -3317,7 +3347,7 @@
|
||||
"Selectează caiet"
|
||||
],
|
||||
"Select one of the other supported sync targets.": [
|
||||
""
|
||||
"Selectează una din celelalte destinații de sincronizare suportate."
|
||||
],
|
||||
"Select parent notebook": [
|
||||
"Selectează caietul părinte"
|
||||
@@ -3656,7 +3686,7 @@
|
||||
"Comută la [notebook] - toate operațiunile ulterioare vor avea loc în acest caiet."
|
||||
],
|
||||
"Sync": [
|
||||
""
|
||||
"Sincronizează"
|
||||
],
|
||||
"Sync as many devices as you want": [
|
||||
"Sincronizează cât de multe dispozitive dorești"
|
||||
@@ -3736,6 +3766,9 @@
|
||||
"Take photo": [
|
||||
"Fotografiază"
|
||||
],
|
||||
"Take survey": [
|
||||
"Completează chestionarul"
|
||||
],
|
||||
"Task \"%s\" failed with error: %s": [
|
||||
"Sarcina „%s” a eșuat cu eroarea: %s"
|
||||
],
|
||||
@@ -3755,7 +3788,7 @@
|
||||
"Comandă editor de text"
|
||||
],
|
||||
"Thank you for the feedback!\nDo you have time to complete a short survey?": [
|
||||
""
|
||||
"Mulțumim pentru feedback!\nAi timp să completezi un scurt chestionar?"
|
||||
],
|
||||
"The active profile cannot be deleted. Switch to a different profile and try again.": [
|
||||
"Profilul activ nu poate fi șters. Comută la un alt profil și încearcă din nou."
|
||||
@@ -4158,7 +4191,10 @@
|
||||
"Nu se poate edita resursa de tip %s"
|
||||
],
|
||||
"Unable to share log data. Reason: %s": [
|
||||
"Imposibilitatea de a partaja datele din jurnal. Motivul: %s"
|
||||
"Imposibil de a partaja datele din jurnal. Motivul: %s"
|
||||
],
|
||||
"Unable to share note data. Reason: %s": [
|
||||
"Imposibil de a partaja datele din notiță. Motivul: %s"
|
||||
],
|
||||
"Unchecked": [
|
||||
"Debifat"
|
||||
@@ -4305,7 +4341,7 @@
|
||||
"Se utilizează atunci când este necesar un font cu lățime fixă pentru a prezenta textul în mod lizibil (de exemplu, tabele, căsuțe de selectare, coduri). Dacă nu se găsește, se utilizează un font generic monospace (lățime fixă)."
|
||||
],
|
||||
"Useful": [
|
||||
""
|
||||
"Utilă"
|
||||
],
|
||||
"User deletions": [
|
||||
"Ștergeri de utilizatori"
|
||||
@@ -4413,7 +4449,7 @@
|
||||
"Când este creată o nouă listă de făcut:"
|
||||
],
|
||||
"When enabled, requests that the images in the note be transcribed with a higher-quality on-server transcription service. Requires sync with a copy of the desktop app.": [
|
||||
""
|
||||
"Când este activat, solicită ca imaginile din notiță să fie transcrise cu un serviciu de calitate mai bună de pe server. Necesită sincronizare cu aplicația desktop."
|
||||
],
|
||||
"When enabled, the application will scan your attachments and extract the text from it. This will allow you to search for text in these attachments.": [
|
||||
"Atunci când este activată, aplicația va scana atașamentele și va extrage textul din ele. Acest lucru îți va permite să cauți text în atașamente."
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -174,7 +174,7 @@
|
||||
"[Žiadny]"
|
||||
],
|
||||
"A brief description of the image:": [
|
||||
""
|
||||
"Stručný popis obrázku:"
|
||||
],
|
||||
"A comma-separated list of words. May be used for uncommon words, to help voice typing spell them correctly.": [
|
||||
"Zoznam slov oddelených čiarkami. Môže sa použiť pre nezvyčajné slová, aby sa pomohlo pri ich správnom hlasovom zadávaní."
|
||||
@@ -248,6 +248,9 @@
|
||||
"Add body": [
|
||||
"Pridať telo"
|
||||
],
|
||||
"Add column": [
|
||||
"Pridať stĺpec"
|
||||
],
|
||||
"Add new": [
|
||||
"Pridať novú"
|
||||
],
|
||||
@@ -257,6 +260,9 @@
|
||||
"Add recipient:": [
|
||||
"Pridať príjemcu:"
|
||||
],
|
||||
"Add row": [
|
||||
"Pridať riadok"
|
||||
],
|
||||
"Add tags:": [
|
||||
"Pridať štítky:"
|
||||
],
|
||||
@@ -345,7 +351,7 @@
|
||||
"Bol nájdený automaticky uložený náčrt. Pripojiť jeho kópiu k poznámke?"
|
||||
],
|
||||
"An error occurred while sending the response. This can happen if the app is offline or cannot connect to the server.\nError: %s": [
|
||||
""
|
||||
"Pri odosielaní odpovede došlo k chybe. K tejto chybe môže dôjsť, ak je aplikácia offline alebo sa nemôže pripojiť k serveru.\nChyba: %s"
|
||||
],
|
||||
"An error occurred: %s": [
|
||||
"Vyskytla sa chyba: %s"
|
||||
@@ -686,6 +692,9 @@
|
||||
"Collapse all notebooks": [
|
||||
"Zbaliť všetky zápisníky"
|
||||
],
|
||||
"Collapse title": [
|
||||
"Zbaliť nadpis"
|
||||
],
|
||||
"Collapsed": [
|
||||
"Zbalené"
|
||||
],
|
||||
@@ -1026,6 +1035,9 @@
|
||||
"Delete attachment \"%s\"?": [
|
||||
"Vymazať prílohu \"%s\"?"
|
||||
],
|
||||
"Delete column": [
|
||||
"Vymazať stĺpec"
|
||||
],
|
||||
"Delete expired sessions": [
|
||||
"Vymazať relácie s uplynutou platnosťou"
|
||||
],
|
||||
@@ -1053,6 +1065,9 @@
|
||||
"Delete profile \"%s\"": [
|
||||
"Vymazať profil \"%s\""
|
||||
],
|
||||
"Delete row": [
|
||||
"Vymazať riadok"
|
||||
],
|
||||
"Delete selected notes": [
|
||||
"Vymazať vybrané poznámky"
|
||||
],
|
||||
@@ -1186,7 +1201,7 @@
|
||||
"Nestraťte heslo, pretože z bezpečnostných dôvodov je to *jediný* spôsob, ako dešifrovať údaje! Ak chcete zapnúť šifrovanie, zadajte heslo nižšie."
|
||||
],
|
||||
"Do you find the Joplin web app useful?": [
|
||||
""
|
||||
"Považujete webovú aplikáciu Joplin za užitočnú?"
|
||||
],
|
||||
"Document scanner: Title template": [
|
||||
"Skener dokumentov: Šablóna názvu"
|
||||
@@ -1545,6 +1560,9 @@
|
||||
"Expand all notebooks": [
|
||||
"Rozbaliť všetky zápisníky"
|
||||
],
|
||||
"Expand title": [
|
||||
"Rozbaliť nadpis"
|
||||
],
|
||||
"Expanded": [
|
||||
"Rozbalené"
|
||||
],
|
||||
@@ -1609,7 +1627,7 @@
|
||||
"Príznaky funkcií"
|
||||
],
|
||||
"Feedback": [
|
||||
""
|
||||
"Spätná väzba"
|
||||
],
|
||||
"Fetched items: %d/%d.": [
|
||||
"Načítané položky: %d/%d."
|
||||
@@ -2024,6 +2042,9 @@
|
||||
"Joplin Server": [
|
||||
"Joplin Server"
|
||||
],
|
||||
"Joplin Server (SAML)": [
|
||||
"Joplin Server (SAML)"
|
||||
],
|
||||
"Joplin Server Business": [
|
||||
"Joplin Server Business"
|
||||
],
|
||||
@@ -2043,7 +2064,7 @@
|
||||
"Joplin SSO overenie"
|
||||
],
|
||||
"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.": [
|
||||
""
|
||||
"Joplin podporuje ukladanie polohy, pri ktorej sa poznámky ukladajú alebo vytvárajú. Chcete túto funkciu povoliť? Túto voľbu môžete kedykoľvek zmeniť v nastaveniach."
|
||||
],
|
||||
"Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.": [
|
||||
"Joplin Web Clipper umožňuje ukladanie webových stránok a snímok obrazovky z vášho prehliadača do aplikácie Joplin."
|
||||
@@ -2076,7 +2097,7 @@
|
||||
"Kľúče, ktoré si vyžadujú aktualizáciu"
|
||||
],
|
||||
"Label": [
|
||||
""
|
||||
"Označenie"
|
||||
],
|
||||
"Landscape": [
|
||||
"Na šírku"
|
||||
@@ -2235,6 +2256,9 @@
|
||||
"Markdown editor": [
|
||||
"Editor Markdown"
|
||||
],
|
||||
"Markdown editor: Highlight active line": [
|
||||
"Editor Markdown: Zvýrazniť aktívny riadok"
|
||||
],
|
||||
"Markdown editor: Render images": [
|
||||
"Editor Markdown: Vykresliť obrázky"
|
||||
],
|
||||
@@ -2496,7 +2520,7 @@
|
||||
"Teraz nie"
|
||||
],
|
||||
"Not useful": [
|
||||
""
|
||||
"Nie je užitočné"
|
||||
],
|
||||
"note": [
|
||||
"pozn"
|
||||
@@ -2546,6 +2570,9 @@
|
||||
"Note list style": [
|
||||
"Štýl zoznamu poznámok"
|
||||
],
|
||||
"Note not published: %s": [
|
||||
"Poznámka neuverejnená: %s"
|
||||
],
|
||||
"Note preview": [
|
||||
"Náhľad poznámky"
|
||||
],
|
||||
@@ -2706,7 +2733,7 @@
|
||||
"Usporiadaný zoznam"
|
||||
],
|
||||
"Other": [
|
||||
""
|
||||
"Iné"
|
||||
],
|
||||
"Other applications...": [
|
||||
"Iné aplikácie..."
|
||||
@@ -2959,6 +2986,9 @@
|
||||
"Publish Note": [
|
||||
"Zverejniť poznámku"
|
||||
],
|
||||
"Publish note \"%s\" (in notebook \"%s\")?": [
|
||||
"Zverejniť poznámku „%s“ (v zápisníku „%s“)?"
|
||||
],
|
||||
"Publish note...": [
|
||||
"Zverejniť poznámku..."
|
||||
],
|
||||
@@ -2971,8 +3001,11 @@
|
||||
"Publish/unpublish": [
|
||||
"Zverejniť/Zrušiť zverejnenie"
|
||||
],
|
||||
"Published at URL: %s": [
|
||||
"Uverejnené na URL adrese: %s"
|
||||
],
|
||||
"Publishes a note to Joplin Server or Joplin Cloud": [
|
||||
""
|
||||
"Uverejní poznámku na serveri Joplin alebo v cloude Joplin"
|
||||
],
|
||||
"QR Code": [
|
||||
"QR kód"
|
||||
@@ -3019,6 +3052,9 @@
|
||||
"Recipients:": [
|
||||
"Príjemcovia:"
|
||||
],
|
||||
"Recognise text:": [
|
||||
"Rozpoznať text:"
|
||||
],
|
||||
"Recognize handwritten image": [
|
||||
"Rozpoznať obrázok rukopisu"
|
||||
],
|
||||
@@ -3247,6 +3283,9 @@
|
||||
"Save geo-location with notes": [
|
||||
"Uložiť geografickú polohu s poznámkami"
|
||||
],
|
||||
"Save geolocation?": [
|
||||
"Uložiť geografickú polohu?"
|
||||
],
|
||||
"Scan notebook": [
|
||||
"Skenovať zápisník"
|
||||
],
|
||||
@@ -3317,7 +3356,7 @@
|
||||
"Vybrať zápisník"
|
||||
],
|
||||
"Select one of the other supported sync targets.": [
|
||||
""
|
||||
"Vyberte jeden z ďalších podporovaných cieľov synchronizácie."
|
||||
],
|
||||
"Select parent notebook": [
|
||||
"Vybrať nadradený zápisník"
|
||||
@@ -3656,7 +3695,7 @@
|
||||
"Prepne na [zápisník] – všetky ďalšie operácie budú prebiehať v tomto zápisníku."
|
||||
],
|
||||
"Sync": [
|
||||
""
|
||||
"Synchronizovať"
|
||||
],
|
||||
"Sync as many devices as you want": [
|
||||
"Synchronizujte toľko zariadení, koľko len chcete"
|
||||
@@ -3736,6 +3775,9 @@
|
||||
"Take photo": [
|
||||
"Odfotiť"
|
||||
],
|
||||
"Take survey": [
|
||||
"Zúčastniť sa prieskumu"
|
||||
],
|
||||
"Task \"%s\" failed with error: %s": [
|
||||
"Úloha \"%s\" zlyhala s chybou: %s"
|
||||
],
|
||||
@@ -3755,7 +3797,7 @@
|
||||
"Príkaz textového editora"
|
||||
],
|
||||
"Thank you for the feedback!\nDo you have time to complete a short survey?": [
|
||||
""
|
||||
"Ďakujeme za spätnú väzbu!\nMáte čas vyplniť krátky dotazník?"
|
||||
],
|
||||
"The active profile cannot be deleted. Switch to a different profile and try again.": [
|
||||
"Aktívny profil nie je možné odstrániť. Prepnite na iný profil a skúste to znova."
|
||||
@@ -4160,6 +4202,9 @@
|
||||
"Unable to share log data. Reason: %s": [
|
||||
"Nie je možné zdieľať údaje záznamu. Dôvod: %s"
|
||||
],
|
||||
"Unable to share note data. Reason: %s": [
|
||||
"Nie je možné zdieľať údaje poznámky. Dôvod: %s"
|
||||
],
|
||||
"Unchecked": [
|
||||
"Neoznačené"
|
||||
],
|
||||
@@ -4305,7 +4350,7 @@
|
||||
"Používa sa v prípadoch, keď je na čitateľné rozvrhnutie textu potrebná pevná šírka písma (napr. tabuľky, označovacie políčka, kód). Ak sa nenájde, použije sa všeobecné monospace písmo (s pevnou šírkou)."
|
||||
],
|
||||
"Useful": [
|
||||
""
|
||||
"Užitočné"
|
||||
],
|
||||
"User deletions": [
|
||||
"Vymazania používateľa"
|
||||
@@ -4413,7 +4458,7 @@
|
||||
"Pri vytváraní novej úlohy:"
|
||||
],
|
||||
"When enabled, requests that the images in the note be transcribed with a higher-quality on-server transcription service. Requires sync with a copy of the desktop app.": [
|
||||
""
|
||||
"Ak je táto funkcia povolená, požiada o prepis obrázkov v poznámke pomocou kvalitnejšej služby prepisovania na serveri. Vyžaduje synchronizáciu s kópiou aplikácie pre počítače."
|
||||
],
|
||||
"When enabled, the application will scan your attachments and extract the text from it. This will allow you to search for text in these attachments.": [
|
||||
"Ak je táto funkcia zapnutá, aplikácia bude skenovať prílohy a extrahovať z nich text. To vám umožní vyhľadávať text v týchto prílohách."
|
||||
|
||||
@@ -182,6 +182,9 @@
|
||||
"Add body": [
|
||||
"Dodaj telo"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add or remove tags:": [
|
||||
"Dodaj ali odstrani oznako:"
|
||||
],
|
||||
|
||||
@@ -176,6 +176,9 @@
|
||||
"Add body": [
|
||||
"Додај тело"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add or remove tags:": [
|
||||
"Додај или уклони ознаке:"
|
||||
],
|
||||
|
||||
@@ -242,6 +242,9 @@
|
||||
"Add body": [
|
||||
"Lägg till brödtext"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Lägg till ny"
|
||||
],
|
||||
|
||||
@@ -170,6 +170,9 @@
|
||||
"Add body": [
|
||||
"เพิ่มเนื้อความ"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add or remove tags:": [
|
||||
"เพิ่มหรือลบแท็ก:"
|
||||
],
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
"%s pek çok dosyanın senkronu için optimize bir değil, bu sebeple ilk senkronizasyon zaman alabilir."
|
||||
],
|
||||
"%s must be a valid whole number": [
|
||||
"%s geçerli bir telefon numarası olmalıdır"
|
||||
"%s geçerli bir tamsayı olmalıdır"
|
||||
],
|
||||
"%s tab opened": [
|
||||
"%s sekme açık"
|
||||
@@ -150,7 +150,7 @@
|
||||
"(Ayarlardan bu istemi kapatabilirsiniz)"
|
||||
],
|
||||
"- Camera: to allow taking a picture and attaching it to a note.": [
|
||||
"-Kamera: fotoğraf çekimi ve fotoğrafın nota eklenebilmesi için."
|
||||
"- Kamera: fotoğraf çekimi ve fotoğrafın nota eklenebilmesi için."
|
||||
],
|
||||
"- Location: to allow attaching geo-location information to a note.": [
|
||||
"- Konum: coğrafi konum bilgilerinin bir nota eklenmesine izin vermek için."
|
||||
@@ -168,7 +168,7 @@
|
||||
"[None]"
|
||||
],
|
||||
"A brief description of the image:": [
|
||||
""
|
||||
"Resmin kısa bir açıklaması:"
|
||||
],
|
||||
"A comma-separated list of words. May be used for uncommon words, to help voice typing spell them correctly.": [
|
||||
"Virgülle ayrılmış bir kelime listesi. Sesli yazımın nadir kelimeleri doğru şekilde hecelemesine yardımcı olmak için kullanılabilir."
|
||||
@@ -242,6 +242,9 @@
|
||||
"Add body": [
|
||||
"Gövde Ekle"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Yeni bir şey ekle"
|
||||
],
|
||||
@@ -339,7 +342,7 @@
|
||||
"Otomatik kaydedilmiş bir çizim bulundu. Not'a bunun kopyasını iliştirmek ister misiniz?"
|
||||
],
|
||||
"An error occurred while sending the response. This can happen if the app is offline or cannot connect to the server.\nError: %s": [
|
||||
""
|
||||
"Cevap gönderilirken bir hata oluştu. Bu durum uygulama çevrimdışıyken, veya sunucuya bağlanılamadığı zamanlarda olabilir\nHata: %s"
|
||||
],
|
||||
"An error occurred: %s": [
|
||||
"Bir hata oluştu: %s"
|
||||
@@ -381,7 +384,7 @@
|
||||
"Aritim Dark"
|
||||
],
|
||||
"Associated tags:": [
|
||||
"Ekli etiketler"
|
||||
"Tanımlı etiketler:"
|
||||
],
|
||||
"At present, Joplin Web can only be open in one tab at a time. Please close the other instance of Joplin.": [
|
||||
"Joplin Web şimdilik sadece aynı anda bir tane sekmede açık durabilir. Lütfen Joplin'in açık diğer sekmelerini kapatın."
|
||||
@@ -680,6 +683,9 @@
|
||||
"Collapse all notebooks": [
|
||||
"Tüm not defterlerini küçült"
|
||||
],
|
||||
"Collapse title": [
|
||||
"Başlığı daralt"
|
||||
],
|
||||
"Collapsed": [
|
||||
"Daraltılmış"
|
||||
],
|
||||
@@ -1178,7 +1184,7 @@
|
||||
"Şifrenizi kaybetmeyin, verilerinizin şifresini çözmenin \"*tek yolu* bu şifreyi girmek olacaktır. Şifrelemeyi etkinleştirmek için lütfen parolanızı aşağıya girin."
|
||||
],
|
||||
"Do you find the Joplin web app useful?": [
|
||||
""
|
||||
"Joplin web uygulamasını faydalı buldunuz mu?"
|
||||
],
|
||||
"Document scanner: Title template": [
|
||||
"Belge tarayıcı: Başlık şablonu"
|
||||
@@ -1241,7 +1247,7 @@
|
||||
"Dropbox Girişi"
|
||||
],
|
||||
"Due": [
|
||||
"Şu tarihe kadar: "
|
||||
"Şu tarihe kadar:"
|
||||
],
|
||||
"due date": [
|
||||
"şu tarihe kadar"
|
||||
@@ -1537,6 +1543,9 @@
|
||||
"Expand all notebooks": [
|
||||
"Tüm not defterini genişlet"
|
||||
],
|
||||
"Expand title": [
|
||||
"Başlığı genişlet"
|
||||
],
|
||||
"Expanded": [
|
||||
"Genişletilmiş"
|
||||
],
|
||||
@@ -1601,7 +1610,7 @@
|
||||
"Özellik bilgileri"
|
||||
],
|
||||
"Feedback": [
|
||||
""
|
||||
"Geribildirim"
|
||||
],
|
||||
"Fetched items: %d/%d.": [
|
||||
"Alınan öğeler: %d/%d."
|
||||
@@ -2015,6 +2024,9 @@
|
||||
"Joplin Server": [
|
||||
"Joplin Sunucusu"
|
||||
],
|
||||
"Joplin Server (SAML)": [
|
||||
"Joplin Server (SAML)"
|
||||
],
|
||||
"Joplin Server Business": [
|
||||
"Joplin Sunucusu Business"
|
||||
],
|
||||
@@ -2034,7 +2046,7 @@
|
||||
"Joplin SSO Kimlik Doğrulaması"
|
||||
],
|
||||
"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.": [
|
||||
""
|
||||
"Joplin notların oluşturulduğu veya güncellemelerin kaydedildiği konumları saklayabilme özelliğine sahip. Bu özelliği aktifleştirmek ister misiniz? Bu özelliği istediğiniz zaman ayarlardan açıp kapatabilirsiniz."
|
||||
],
|
||||
"Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.": [
|
||||
"Joplin Web Alıntılama, tarayıcınızdaki web sayfalarını ve ekran görüntülerini Joplin'e kaydetmenizi sağlar."
|
||||
@@ -2067,7 +2079,7 @@
|
||||
"Güncellenmesi gereken anahtarlar"
|
||||
],
|
||||
"Label": [
|
||||
""
|
||||
"Etiket"
|
||||
],
|
||||
"Landscape": [
|
||||
"Yatay"
|
||||
@@ -2224,6 +2236,9 @@
|
||||
"Markdown editor": [
|
||||
"Markdown düzenleyici"
|
||||
],
|
||||
"Markdown editor: Highlight active line": [
|
||||
"Markdown düzenleyici: Mevcut satırı rengini değiştirerek vurgula"
|
||||
],
|
||||
"Markdown editor: Render images": [
|
||||
"Markdown düzenleyici: Resimleri görüntüle"
|
||||
],
|
||||
@@ -2373,7 +2388,7 @@
|
||||
"Yeni not defteri \"%s\" oluşturulacak ve \"%s\" dosyası onun içine aktarılacak"
|
||||
],
|
||||
"New notebook title": [
|
||||
"Yeni not defteri başlığı:"
|
||||
"Yeni not defteri başlığı"
|
||||
],
|
||||
"New photo": [
|
||||
"Yeni fotoğraf"
|
||||
@@ -2484,7 +2499,7 @@
|
||||
"Şimdi değil"
|
||||
],
|
||||
"Not useful": [
|
||||
""
|
||||
"Kullanışsız"
|
||||
],
|
||||
"note": [
|
||||
"not"
|
||||
@@ -2534,6 +2549,9 @@
|
||||
"Note list style": [
|
||||
"Not listeleme biçimi"
|
||||
],
|
||||
"Note not published: %s": [
|
||||
"Not paylaşılamadı: %s"
|
||||
],
|
||||
"Note preview": [
|
||||
"Not önizleme"
|
||||
],
|
||||
@@ -2694,7 +2712,7 @@
|
||||
"Dizilmiş liste"
|
||||
],
|
||||
"Other": [
|
||||
""
|
||||
"Diğer"
|
||||
],
|
||||
"Other applications...": [
|
||||
"Diğer uygulamalar..."
|
||||
@@ -2946,6 +2964,9 @@
|
||||
"Publish Note": [
|
||||
"Notu Yayımla"
|
||||
],
|
||||
"Publish note \"%s\" (in notebook \"%s\")?": [
|
||||
"“%s” notu (“%s” not defterinde) yayımlansın mı?"
|
||||
],
|
||||
"Publish note...": [
|
||||
"Notu yayımla…"
|
||||
],
|
||||
@@ -2958,8 +2979,11 @@
|
||||
"Publish/unpublish": [
|
||||
"Yayımla/yayından kaldır"
|
||||
],
|
||||
"Published at URL: %s": [
|
||||
"Şu URL’de yayımlandı: %s"
|
||||
],
|
||||
"Publishes a note to Joplin Server or Joplin Cloud": [
|
||||
""
|
||||
"Notu Joplin Server veya Joplin Cloud’a yayımlar"
|
||||
],
|
||||
"QR Code": [
|
||||
"Kare Kod"
|
||||
@@ -3006,6 +3030,9 @@
|
||||
"Recipients:": [
|
||||
"Alıcılar:"
|
||||
],
|
||||
"Recognise text:": [
|
||||
"Metni algıla:"
|
||||
],
|
||||
"Recognize handwritten image": [
|
||||
"El yazısı olan resimleri algıla"
|
||||
],
|
||||
@@ -3234,6 +3261,9 @@
|
||||
"Save geo-location with notes": [
|
||||
"Coğrafi konumu notlarla kaydedin"
|
||||
],
|
||||
"Save geolocation?": [
|
||||
"Coğrafi konumu kaydedilsin mi?"
|
||||
],
|
||||
"Scan notebook": [
|
||||
"Not defterini tara"
|
||||
],
|
||||
@@ -3304,7 +3334,7 @@
|
||||
"Not defterini seç"
|
||||
],
|
||||
"Select one of the other supported sync targets.": [
|
||||
""
|
||||
"Destekleyen diğer senkronizasyon hedeflerinden birini seçin."
|
||||
],
|
||||
"Select parent notebook": [
|
||||
"Ana not defterini seç"
|
||||
@@ -3643,7 +3673,7 @@
|
||||
"[notebook] not defterine geçer - daha sonraki tüm işlemler bu not defterinde gerçekleşecektir."
|
||||
],
|
||||
"Sync": [
|
||||
""
|
||||
"Senkronizasyon"
|
||||
],
|
||||
"Sync as many devices as you want": [
|
||||
"İstediğin kadar cihazı senkronize et"
|
||||
@@ -3723,6 +3753,9 @@
|
||||
"Take photo": [
|
||||
"Fotoğraf çek"
|
||||
],
|
||||
"Take survey": [
|
||||
"Ankete katıl"
|
||||
],
|
||||
"Task \"%s\" failed with error: %s": [
|
||||
"\"%s\" görevi başarısız. Hata mesajı: %s"
|
||||
],
|
||||
@@ -3742,7 +3775,7 @@
|
||||
"Metin editörü komutu"
|
||||
],
|
||||
"Thank you for the feedback!\nDo you have time to complete a short survey?": [
|
||||
""
|
||||
"Geribildirim için teşekkür ederiz!\nKısa bir anketimiz var, bunu doldurmaya vaktiniz var mı?"
|
||||
],
|
||||
"The active profile cannot be deleted. Switch to a different profile and try again.": [
|
||||
"Profil aktifken silinemez. Bir başka profile geçip yeniden deneyin."
|
||||
@@ -3818,7 +3851,7 @@
|
||||
"“%s” notu başarılı bir şekilde “%s” not defterine geri yüklendi."
|
||||
],
|
||||
"The note has been converted to Markdown and the original note has been moved to the trash": [
|
||||
"Not Markdown'a dönüştürüldü ve orijinal not çöp kutusuna taşındı."
|
||||
"Not Markdown'a dönüştürüldü ve orijinal not çöp kutusuna taşındı"
|
||||
],
|
||||
"The note was successfully moved to the trash.": [
|
||||
"Not başarıyla çöp kutusuna taşındı.",
|
||||
@@ -4142,6 +4175,9 @@
|
||||
"Unable to share log data. Reason: %s": [
|
||||
"Loglar dışarı aktarılamadı. Sebep: %s"
|
||||
],
|
||||
"Unable to share note data. Reason: %s": [
|
||||
"Not verisi paylaşılamadı. Sebep: %s"
|
||||
],
|
||||
"Unchecked": [
|
||||
"İşaretlenmemiş"
|
||||
],
|
||||
@@ -4287,7 +4323,7 @@
|
||||
"Sabit bir metin uzunluğu gerektiren yerlerde (örn: tablolar, doğrulama kutucukları, kod vs.) kullanılır. Eğer bulunamazsa jenerik ve monospace olan bir font kullanılacak."
|
||||
],
|
||||
"Useful": [
|
||||
""
|
||||
"Kullanuşlı"
|
||||
],
|
||||
"User deletions": [
|
||||
"Kullancı silmeleri"
|
||||
@@ -4395,7 +4431,7 @@
|
||||
"Yeni bir yapılacak oluşturulurken:"
|
||||
],
|
||||
"When enabled, requests that the images in the note be transcribed with a higher-quality on-server transcription service. Requires sync with a copy of the desktop app.": [
|
||||
""
|
||||
"Etkinleştirildiğinde, nottaki resimlerin sunucu tabanlı daha yüksek kaliteli transkripsiyon servisi ile çözümlenmesini ister. Masaüstü uygulamasının bir kopyasıyla senkronizasyon gerektirir."
|
||||
],
|
||||
"When enabled, the application will scan your attachments and extract the text from it. This will allow you to search for text in these attachments.": [
|
||||
"Eğer bu özellik aktifleştirilirse, uygulama iliştirilen ek dosyaları tarayarak içindeki metinleri ayıklayacak. Bu sayede bu metinler üzerinden de arama yapabileceksiniz."
|
||||
|
||||
@@ -203,6 +203,9 @@
|
||||
"Add body": [
|
||||
"Додати тіло"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"Додати нове"
|
||||
],
|
||||
|
||||
@@ -182,6 +182,9 @@
|
||||
"Add body": [
|
||||
"Điền phần thân"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add or remove tags:": [
|
||||
"Gắn hoặc gỡ nhãn:"
|
||||
],
|
||||
|
||||
@@ -236,6 +236,9 @@
|
||||
"Add body": [
|
||||
"添加内容"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"新增"
|
||||
],
|
||||
|
||||
@@ -200,6 +200,9 @@
|
||||
"Add body": [
|
||||
"新增內文"
|
||||
],
|
||||
"Add column": [
|
||||
""
|
||||
],
|
||||
"Add new": [
|
||||
"新增"
|
||||
],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Second } from '@joplin/utils/time';
|
||||
import { afterAllCleanUp, setupDatabaseAndSynchronizer, switchClient, syncTargetId, synchronizerStart, msleep } from '../testing/test-utils';
|
||||
import BaseItem from './BaseItem';
|
||||
import Folder from './Folder';
|
||||
@@ -42,6 +43,19 @@ describe('BaseItem', () => {
|
||||
expect(unserialized2.title).toBe(folder2.title);
|
||||
});
|
||||
|
||||
it.each([
|
||||
'',
|
||||
'\n\na\nb\nc\nç\nTest!\n Testing. \n',
|
||||
'Test! ☺',
|
||||
'Test! ☺\n\n\n',
|
||||
])('should not modify body when unserializing (body: %j)', async (body) => {
|
||||
const note = await Note.save({ title: 'note1', body });
|
||||
|
||||
expect(await Note.unserialize(await Note.serialize(note))).toMatchObject({
|
||||
body,
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly unserialize note timestamps', async () => {
|
||||
const folder = await Folder.save({ title: 'folder' });
|
||||
const note = await Note.save({ title: 'note', parent_id: folder.id });
|
||||
@@ -55,6 +69,22 @@ describe('BaseItem', () => {
|
||||
expect(unserialized.user_updated_time).toEqual(note.user_updated_time);
|
||||
});
|
||||
|
||||
it('should unserialize a very large note quickly', async () => {
|
||||
const folder = await Folder.save({ title: 'folder' });
|
||||
const note = await Note.save({ title: 'note', parent_id: folder.id });
|
||||
|
||||
const serialized = await Note.serialize({
|
||||
...note,
|
||||
// 2 MiB
|
||||
body: '\n.'.repeat(1 * 1024 * 1024),
|
||||
});
|
||||
|
||||
const start = performance.now();
|
||||
await Note.unserialize(serialized);
|
||||
// Locally, this passes in in < 2s, so 30s should be a safe upper bound.
|
||||
expect(performance.now() - start).toBeLessThan(30 * Second);
|
||||
});
|
||||
|
||||
it('should serialize geolocation fields', async () => {
|
||||
const folder = await Folder.save({ title: 'folder' });
|
||||
let note = await Note.save({ title: 'note', parent_id: folder.id });
|
||||
|
||||
@@ -578,28 +578,24 @@ export default class BaseItem extends BaseModel {
|
||||
const lines = content.split('\n');
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
let output: any = {};
|
||||
let state = 'readingProps';
|
||||
const body: string[] = [];
|
||||
let body: string[] = [];
|
||||
|
||||
for (let i = lines.length - 1; i >= 0; i--) {
|
||||
let line = lines[i];
|
||||
|
||||
if (state === 'readingProps') {
|
||||
line = line.trim();
|
||||
line = line.trim();
|
||||
|
||||
if (line === '') {
|
||||
state = 'readingBody';
|
||||
continue;
|
||||
}
|
||||
|
||||
const p = line.indexOf(':');
|
||||
if (p < 0) throw new Error(`Invalid property format: ${line}: ${content}`);
|
||||
const key = line.substr(0, p).trim();
|
||||
const value = line.substr(p + 1).trim();
|
||||
output[key] = value;
|
||||
} else if (state === 'readingBody') {
|
||||
body.splice(0, 0, line);
|
||||
// Props are separated from the body by a single blank line
|
||||
if (line === '') {
|
||||
body = lines.slice(0, i);
|
||||
break;
|
||||
}
|
||||
|
||||
const p = line.indexOf(':');
|
||||
if (p < 0) throw new Error(`Invalid property format: ${line}: ${content}`);
|
||||
const key = line.substr(0, p).trim();
|
||||
const value = line.substr(p + 1).trim();
|
||||
output[key] = value;
|
||||
}
|
||||
|
||||
if (!output.type_) throw new Error(`Missing required property: type_: ${content}`);
|
||||
@@ -1007,7 +1003,7 @@ export default class BaseItem extends BaseModel {
|
||||
|
||||
const isNew = this.isNew(o, options);
|
||||
|
||||
if (needsShareReadOnlyChecks(this.modelType(), options.changeSource, this.syncShareCache)) {
|
||||
if (needsShareReadOnlyChecks(this.modelType(), options.changeSource, this.syncShareCache, options.disableReadOnlyCheck)) {
|
||||
if (!isNew) {
|
||||
const previousItem = await this.loadItemByTypeAndId(this.modelType(), o.id, { fields: ['id', 'share_id'] });
|
||||
checkIfItemCanBeChanged(this.modelType(), options.changeSource, previousItem, this.syncShareCache);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { setupDatabaseAndSynchronizer, switchClient, createFolderTree, supportDir, msleep, resourceService } from '../testing/test-utils';
|
||||
import { setupDatabaseAndSynchronizer, switchClient, createFolderTree, supportDir, msleep, resourceService, simulateReadOnlyShareEnv } from '../testing/test-utils';
|
||||
import Folder from '../models/Folder';
|
||||
import { allNotesFolders } from '../testing/test-utils-synchronizer';
|
||||
import Note from '../models/Note';
|
||||
@@ -181,6 +181,21 @@ describe('models/Folder.sharing', () => {
|
||||
}
|
||||
}));
|
||||
|
||||
it('should not fail to update share IDs when an outdated share ID is contained in a read-only folder', async () => {
|
||||
const shareId = 'abcd1234';
|
||||
const root = await Folder.save({ title: 'read-only', share_id: shareId });
|
||||
// Save a child with a different share ID
|
||||
const child = await Note.save({ title: 'Test', parent_id: root.id, share_id: `${shareId}-different` });
|
||||
|
||||
const reset = simulateReadOnlyShareEnv([shareId]);
|
||||
try {
|
||||
await Folder.updateAllShareIds(resourceService(), []);
|
||||
expect(await Note.load(child.id)).toMatchObject({ share_id: shareId });
|
||||
} finally {
|
||||
reset();
|
||||
}
|
||||
});
|
||||
|
||||
it('should unshare a subfolder of a shared folder when it is moved to the root', (async () => {
|
||||
let folder1 = await createFolderTree('', [
|
||||
{
|
||||
|
||||
@@ -556,7 +556,10 @@ export default class Folder extends BaseItem {
|
||||
share_id: row.share_id || '',
|
||||
parent_id: row.parent_id,
|
||||
updated_time: Date.now(),
|
||||
}, { autoTimestamp: false });
|
||||
}, {
|
||||
autoTimestamp: false,
|
||||
disableReadOnlyCheck: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"fast-xml-parser": "3.21.1",
|
||||
"file-type": "16.5.4",
|
||||
"follow-redirects": "1.15.6",
|
||||
"follow-redirects": "1.15.11",
|
||||
"form-data": "4.0.4",
|
||||
"fs-extra": "11.2.0",
|
||||
"hpagent": "1.2.0",
|
||||
|
||||
@@ -125,7 +125,7 @@ describe('services_KeymapService', () => {
|
||||
expect(keymapService.getAccelerator('textBold')).toEqual('Ctrl+B');
|
||||
});
|
||||
|
||||
if ('should throw when an invalid command is requested', () => {
|
||||
it('should throw when an invalid command is requested', () => {
|
||||
expect(() => keymapService.getAccelerator('totallyNonExistentCommand')).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,6 +39,7 @@ describe('services/RevisionService', () => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
Setting.setValue('revisionService.intervalBetweenRevisions', 0);
|
||||
Setting.setValue('revisionService.oldNoteInterval', 1000 * 60 * 60 * 24 * 7);
|
||||
|
||||
jest.useFakeTimers({ advanceTimers: true });
|
||||
});
|
||||
@@ -619,6 +620,7 @@ describe('services/RevisionService', () => {
|
||||
jest.advanceTimersByTime(100);
|
||||
|
||||
await Note.save({ id: note.id, title: 'test', body: 'StartA' });
|
||||
await ItemChange.waitForAllSaved();
|
||||
await Note.save({ id: note.id, title: 'test', body: 'StartAB' });
|
||||
await Note.save({ id: note.id, title: 'test', body: 'StartABC' }); // REV 2
|
||||
await revisionService().collectRevisions(); // Create revisions for old and new content
|
||||
@@ -726,8 +728,9 @@ describe('services/RevisionService', () => {
|
||||
Setting.setValue('revisionService.oldNoteInterval', 50);
|
||||
|
||||
await Note.save({ id: note.id, title: 'test', body: 'ABCDEF' });
|
||||
await ItemChange.waitForAllSaved();
|
||||
await Note.save({ id: note.id, title: 'test', body: 'ABCDEFG' }); // REV 2
|
||||
await revisionService().collectRevisions();
|
||||
await revisionService().collectRevisions(); // Create revisions for old and new content
|
||||
|
||||
const revisions = await Revision.allByType(BaseModel.TYPE_NOTE, note.id);
|
||||
expect(revisions.length).toBe(3);
|
||||
@@ -763,8 +766,9 @@ describe('services/RevisionService', () => {
|
||||
Setting.setValue('revisionService.oldNoteInterval', 50);
|
||||
|
||||
await Note.save({ id: note.id, title: 'test', body: 'ABCDEF' });
|
||||
await ItemChange.waitForAllSaved();
|
||||
await Note.save({ id: note.id, title: 'test', body: 'ABCDE' }); // REV 1
|
||||
await revisionService().collectRevisions(); // Content is the same, create just one revision instead of two
|
||||
await revisionService().collectRevisions(); // Content is the same for old and new content, so create just one revision instead of two
|
||||
|
||||
const revisions = await Revision.allByType(BaseModel.TYPE_NOTE, note.id);
|
||||
expect(revisions.length).toBe(2);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user