1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-02-01 19:15:01 +02:00

Merge branch 'release-1.8' into dev

This commit is contained in:
Laurent Cozic 2021-05-10 12:23:39 +02:00
commit ea536dbf87
87 changed files with 434 additions and 159 deletions

77
DEPLOY.md Normal file
View File

@ -0,0 +1,77 @@
# Deploying Joplin apps and scripts
Various scripts are provided to deploy the Joplin applications, scripts and tools.
## Setting up version numbers
Before new releases are created, all version numbers must be updated. This is done using the `setupNewRelease` script and passing it the new major.minor version number. For example:
npm run setupNewRelease -- 1.8
Patch numbers are going to be incremented automatically when releasing each individual package.
## Desktop application
The desktop application is built for Windows, macOS and Linux via continuous integration, by pushing a version tag to GitHub. The process is automated using:
npm run releaseDesktop
## Android application
The app is built and upload to GitHub using:
npm run releaseAndroid -- --type=prerelease
The "type" parameter can be either "release" or "prerelease"
## iOS application
It must be built and released manually using XCode.
## CLI application
Unlike the mobile or desktop application, the CLI app doesn't bundle its dependencies and is always installed from source. For that reason, all its `@joplin` dependencies must be deployed publicly first. This is done using:
npm run publishAll
This is going to publish all the Joplin libraries, such as `@joplin/lib`, `@joplin/tools`, etc.
Then in `app-cli/package.json`, all `@joplin` dependencies and devdependencies must be set to the last major/minor version. For example:
```json
"dependencies": {
"@joplin/lib": "1.8",
"@joplin/renderer": "1.8",
"...": "..."
},
"devDependencies": {
"@joplin/tools": "1.8",
"...": "..."
}
```
Finally, to release the actual app, run:
npm run releaseCli
## Joplin Server
Run:
npm run releaseServer
## Web clipper
Run:
npm run releaseClipper
## Plugin generator
First the types should generally be updated, using `./updateTypes.sh`. Then run:
npm run releasePluginGenerator
## Plugin Repo Cli
Since it has dependencies to the `@joplin` packages, it is released when running `npm run publishAll`

View File

@ -1,6 +1,6 @@
{
"name": "joplin",
"version": "1.8.0",
"version": "1.8.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -31,7 +31,7 @@
],
"owner": "Laurent Cozic"
},
"version": "1.8.0",
"version": "1.8.1",
"bin": {
"joplin": "./main.js"
},
@ -39,8 +39,8 @@
"node": ">=10.0.0"
},
"dependencies": {
"@joplin/lib": "^1.7.3",
"@joplin/renderer": "^1.7.3",
"@joplin/lib": "1.8",
"@joplin/renderer": "1.8",
"aws-sdk": "^2.588.0",
"chalk": "^4.1.0",
"clean-html": "^1.5.0",
@ -65,7 +65,7 @@
"yargs-parser": "^7.0.0"
},
"devDependencies": {
"@joplin/tools": "^1.0.9",
"@joplin/tools": "1.8",
"@types/fs-extra": "^9.0.6",
"@types/jest": "^26.0.15",
"@types/node": "^14.14.6",

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -21,7 +21,7 @@ import { Command } from './types';
* and look at the `execute()` command.
*/
export default class JoplinCommands {
/**
/**
* <span class="platform-desktop">desktop</span> Executes the given
* command.
*
@ -40,8 +40,8 @@ export default class JoplinCommands {
* await joplin.commands.execute('newFolder', "SOME_FOLDER_ID");
* ```
*/
execute(commandName: string, ...args: any[]): Promise<any | void>;
/**
execute(commandName: string, ...args: any[]): Promise<any | void>;
/**
* <span class="platform-desktop">desktop</span> Registers a new command.
*
* ```typescript
@ -57,5 +57,5 @@ export default class JoplinCommands {
* });
* ```
*/
register(command: Command): Promise<void>;
register(command: Command): Promise<void>;
}

View File

@ -5,6 +5,6 @@
* so for now disable filters.
*/
export default class JoplinFilters {
on(name: string, callback: Function): Promise<void>;
off(name: string, callback: Function): Promise<void>;
on(name: string, callback: Function): Promise<void>;
off(name: string, callback: Function): Promise<void>;
}

View File

@ -12,6 +12,6 @@ import { ExportModule, ImportModule } from './types';
* You may also want to refer to the Joplin API documentation to see the list of properties for each item (note, notebook, etc.) - https://joplinapp.org/api/references/rest_api/
*/
export default class JoplinInterop {
registerExportModule(module: ExportModule): Promise<void>;
registerImportModule(module: ImportModule): Promise<void>;
registerExportModule(module: ExportModule): Promise<void>;
registerImportModule(module: ImportModule): Promise<void>;
}

View File

@ -6,7 +6,7 @@ export interface ChangeEvent {
*/
keys: string[];
}
export declare type ChangeHandler = (event: ChangeEvent)=> void;
export declare type ChangeHandler = (event: ChangeEvent) => void;
/**
* This API allows registering new settings and setting sections, as well as getting and setting settings. Once a setting has been registered it will appear in the config screen and be editable by the user.
*
@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -3,7 +3,7 @@ import { Disposable } from './types';
declare enum ItemChangeEventType {
Create = 1,
Update = 2,
Delete = 3,
Delete = 3
}
interface ItemChangeEvent {
id: string;
@ -12,8 +12,8 @@ interface ItemChangeEvent {
interface SyncStartEvent {
withErrors: boolean;
}
declare type ItemChangeHandler = (event: ItemChangeEvent)=> void;
declare type SyncStartHandler = (event: SyncStartEvent)=> void;
declare type ItemChangeHandler = (event: ItemChangeEvent) => void;
declare type SyncStartHandler = (event: SyncStartEvent) => void;
/**
* The workspace service provides access to all the parts of Joplin that
* are being worked on - i.e. the currently selected notes or notebooks as
@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -330,16 +330,57 @@ export enum SettingItemType {
export interface SettingItem {
value: any;
type: SettingItemType;
public: boolean;
label: string;
label: string;
description?: string;
isEnum?: boolean;
/**
* A public setting will appear in the Configuration screen and will be
* modifiable by the user. A private setting however will not appear there,
* and can only be changed programmatically. You may use this to store some
* values that you do not want to directly expose.
*/
public: boolean;
/**
* You would usually set this to a section you would have created
* specifically for the plugin.
*/
section?: string;
options?: any;
/**
* To create a setting with multiple options, set this property to `true`.
* That setting will render as a dropdown list in the configuration screen.
*/
isEnum?: boolean;
/**
* This property is required when `isEnum` is `true`. In which case, it
* should contain a map of value => label.
*/
options?: Record<any, any>;
/**
* Reserved property. Not used at the moment.
*/
appTypes?: string[];
/**
* Set this to `true` to store secure data, such as passwords. Any such
* setting will be stored in the system keychain if one is available.
*/
secure?: boolean;
/**
* An advanced setting will be moved under the "Advanced" button in the
* config screen.
*/
advanced?: boolean;
/**
* Set the min, max and step values if you want to restrict an int setting
* to a particular range.
*/
minimum?: number;
maximum?: number;
step?: number;

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -24,4 +24,4 @@
"webpack-cli": "^3.3.11",
"yargs": "^16.2.0"
}
}
}

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -12,6 +12,7 @@ const { clipboard } = require('electron');
const mimeUtils = require('@joplin/lib/mime-utils.js').mime;
const md5 = require('md5');
const path = require('path');
const uri2path = require('file-uri-to-path');
const logger = Logger.create('resourceHandling');
@ -142,21 +143,21 @@ export async function processPastedHtml(html: string) {
if (!mappedResources[imageSrc]) {
try {
if (imageSrc.startsWith('file')) {
const imageFilePath = path.normalize(unescape(imageSrc).substr(7));
const imageFilePath = path.normalize(uri2path(imageSrc));
const resourceDirPath = path.normalize(Setting.value('resourceDir'));
if (imageFilePath.startsWith(resourceDirPath)) {
mappedResources[imageSrc] = imageSrc;
} else {
const createdResource = await shim.createResourceFromPath(imageFilePath);
mappedResources[imageSrc] = `file://${escape(Resource.fullPath(createdResource))}`;
mappedResources[imageSrc] = `file://${encodeURI(Resource.fullPath(createdResource))}`;
}
} else {
const filePath = `${Setting.value('tempDir')}/${md5(Date.now() + Math.random())}`;
await shim.fetchBlob(imageSrc, { path: filePath });
const createdResource = await shim.createResourceFromPath(filePath);
await shim.fsDriver().remove(filePath);
mappedResources[imageSrc] = `file://${escape(Resource.fullPath(createdResource))}`;
mappedResources[imageSrc] = `file://${encodeURI(Resource.fullPath(createdResource))}`;
}
} catch (error) {
logger.warn(`Error creating a resource for ${imageSrc}.`, error);

View File

@ -3199,7 +3199,8 @@
},
"ini": {
"version": "1.3.5",
"resolved": "",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true,
"optional": true
},
@ -3260,7 +3261,8 @@
},
"mkdirp": {
"version": "0.5.1",
"resolved": "",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"optional": true,
"requires": {
@ -3425,7 +3427,8 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": "",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true,
"optional": true
}
@ -4218,6 +4221,15 @@
"optional": true,
"requires": {
"file-uri-to-path": "1.0.0"
},
"dependencies": {
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"dev": true,
"optional": true
}
}
},
"bl": {
@ -6964,11 +6976,9 @@
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"dev": true,
"optional": true
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz",
"integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg=="
},
"filelist": {
"version": "1.0.2",
@ -7649,7 +7659,8 @@
},
"ini": {
"version": "1.3.5",
"resolved": "",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true,
"optional": true
},
@ -7893,7 +7904,8 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": "",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true,
"optional": true
}
@ -8354,7 +8366,8 @@
},
"y18n": {
"version": "3.2.1",
"resolved": "",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
"dev": true
},
"yargs": {

View File

@ -133,6 +133,7 @@
"electron-context-menu": "^0.15.0",
"electron-is-dev": "^0.3.0",
"electron-window-state": "^4.1.1",
"file-uri-to-path": "^2.0.0",
"formatcoords": "^1.1.3",
"fs-extra": "^5.0.0",
"highlight.js": "^10.2.1",

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/fork-htmlparser2",
"version": "4.1.23",
"version": "4.1.24",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,7 +1,7 @@
{
"name": "@joplin/fork-htmlparser2",
"description": "Fast & forgiving HTML/XML/RSS parser",
"version": "4.1.23",
"version": "4.1.24",
"author": "Felix Boehm <me@feedic.com>",
"publishConfig": {
"access": "public"

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/fork-sax",
"version": "1.2.27",
"version": "1.2.28",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -2,7 +2,7 @@
"name": "@joplin/fork-sax",
"description": "An evented streaming XML parser in JavaScript",
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
"version": "1.2.27",
"version": "1.2.28",
"main": "lib/sax.js",
"publishConfig": {
"access": "public"

View File

@ -1,14 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
private requireWhiteList_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
private requireWhiteList;
require(filePath: string): any;
get process(): any;
}

View File

@ -22,11 +22,18 @@ export default class JoplinSettings {
private get keyPrefix();
private namespacedKey;
/**
* Registers a new setting. Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.

View File

@ -35,7 +35,7 @@ export default class JoplinWorkspace {
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of a note changes.
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**

View File

@ -1,6 +1,6 @@
{
"name": "generator-joplin",
"version": "1.8.0",
"version": "1.8.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "generator-joplin",
"version": "1.8.0",
"version": "1.8.1",
"description": "Scaffolds out a new Joplin plugin",
"homepage": "https://github.com/laurent22/joplin/tree/dev/packages/generator-joplin",
"author": {
@ -34,4 +34,4 @@
"repository": "https://github.com/laurent22/generator-joplin",
"license": "MIT",
"private": true
}
}

View File

@ -3,7 +3,7 @@ import paginationToSql from './models/utils/paginationToSql';
import Database from './database';
import uuid from './uuid';
import time from './time';
import JoplinDatabase from './JoplinDatabase';
import JoplinDatabase, { TableField } from './JoplinDatabase';
const Mutex = require('async-mutex').Mutex;
// New code should make use of this enum
@ -193,7 +193,7 @@ class BaseModel {
throw new Error(`Unknown field: ${name}`);
}
static fields() {
static fields(): TableField[] {
return this.db().tableFields(this.tableName());
}

View File

@ -119,7 +119,7 @@ CREATE TABLE version (
INSERT INTO version (version) VALUES (1);
`;
interface TableField {
export interface TableField {
name: string;
type: number;
default: any;

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/lib",
"version": "1.8.1",
"version": "1.8.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/lib",
"version": "1.8.1",
"version": "1.8.2",
"description": "Joplin Core library",
"author": "Laurent Cozic",
"homepage": "",
@ -22,11 +22,11 @@
"typescript": "^4.0.5"
},
"dependencies": {
"@joplin/fork-htmlparser2": "^4.1.23",
"@joplin/fork-sax": "^1.2.27",
"@joplin/renderer": "^1.8.1",
"@joplin/turndown": "^4.0.45",
"@joplin/turndown-plugin-gfm": "^1.0.27",
"@joplin/fork-htmlparser2": "^4.1.24",
"@joplin/fork-sax": "^1.2.28",
"@joplin/renderer": "^1.8.2",
"@joplin/turndown": "^4.0.46",
"@joplin/turndown-plugin-gfm": "^1.0.28",
"async-mutex": "^0.1.3",
"aws-sdk": "^2.588.0",
"base-64": "^0.1.0",

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/plugin-repo-cli",
"version": "1.8.1",
"version": "1.8.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/plugin-repo-cli",
"version": "1.8.1",
"version": "1.8.2",
"description": "",
"main": "index.js",
"bin": {
@ -18,8 +18,8 @@
"author": "",
"license": "MIT",
"dependencies": {
"@joplin/lib": "^1.8.1",
"@joplin/tools": "^1.8.1",
"@joplin/lib": "^1.8.2",
"@joplin/tools": "^1.8.2",
"fs-extra": "^9.0.1",
"yargs": "^16.0.3"
},

View File

@ -133,7 +133,7 @@ class HtmlUtils {
return output.join('').replace(/\s+/g, ' ');
}
sanitizeHtml(html: string, options: any = null) {
public sanitizeHtml(html: string, options: any = null) {
options = Object.assign({}, {
// If true, adds a "jop-noMdConv" class to all the tags.
// It can be used afterwards to restore HTML tags in Markdown.
@ -158,7 +158,7 @@ class HtmlUtils {
// "link" can be used to escape the parser and inject JavaScript.
// Adding "meta" too for the same reason as it shouldn't be used in
// notes anyway.
const disallowedTags = ['script', 'iframe', 'frameset', 'frame', 'object', 'base', 'embed', 'link', 'meta'];
const disallowedTags = ['script', 'iframe', 'frameset', 'frame', 'object', 'base', 'embed', 'link', 'meta', 'noscript'];
const parser = new htmlparser2.Parser({

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/renderer",
"version": "1.8.1",
"version": "1.8.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/renderer",
"version": "1.8.1",
"version": "1.8.2",
"description": "The Joplin note renderer, used the mobile and desktop application",
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/renderer",
"main": "index.js",
@ -24,7 +24,7 @@
"typescript": "^4.0.5"
},
"dependencies": {
"@joplin/fork-htmlparser2": "^4.1.23",
"@joplin/fork-htmlparser2": "^4.1.24",
"font-awesome-filetypes": "^2.1.0",
"fs-extra": "^8.1.0",
"highlight.js": "^10.2.1",

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/tools",
"version": "1.8.1",
"version": "1.8.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/tools",
"version": "1.8.1",
"version": "1.8.2",
"description": "Various tools for Joplin",
"main": "index.js",
"author": "Laurent Cozic",
@ -18,7 +18,7 @@
},
"license": "MIT",
"dependencies": {
"@joplin/lib": "^1.8.1",
"@joplin/lib": "^1.8.2",
"execa": "^4.1.0",
"fs-extra": "^4.0.3",
"gettext-parser": "^1.3.0",

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/turndown-plugin-gfm",
"version": "1.0.27",
"version": "1.0.28",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -4,7 +4,7 @@
"publishConfig": {
"access": "public"
},
"version": "1.0.27",
"version": "1.0.28",
"author": "Dom Christie",
"main": "lib/turndown-plugin-gfm.cjs.js",
"module": "lib/turndown-plugin-gfm.es.js",

View File

@ -1,6 +1,6 @@
{
"name": "@joplin/turndown",
"version": "4.0.45",
"version": "4.0.46",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,7 +1,7 @@
{
"name": "@joplin/turndown",
"description": "A library that converts HTML to Markdown",
"version": "4.0.45",
"version": "4.0.46",
"author": "Dom Christie",
"main": "lib/turndown.cjs.js",
"module": "lib/turndown.es.js",

View File

@ -1,5 +1,21 @@
# Joplin terminal app changelog
## [cli-v1.8.1](https://github.com/laurent22/joplin/releases/tag/cli-v1.8.1) - 2021-05-10T09:38:05Z
- New: Add "id" and "due" search filters (#4898 by [@JackGruber](https://github.com/JackGruber))
- New: Add support for "batch" command (eef86d6)
- Improved: Also duplicate the tags when the note is duplicated (#4876) (#3157 by [@JackGruber](https://github.com/JackGruber))
- Improved: Bump KaTeX to 0.13.3 (#4902 by Roman Musin)
- Improved: Filter "notebook" can now be negated (#4651 by Naveen M V)
- Improved: Improved error handling when importing ENEX (257cde4)
- Improved: Save user settings to JSON file (71f976f)
- Improved: Some imported ENEX files incorrectly had invisible sections (f7a457f)
- Fixed: Disable WebDAV response caching (#4887) (#4706 by Roman Musin)
- Fixed: Fixed issue when getting version info (54884d6)
- Fixed: Fixed rendering of note and resource links (61399ce)
- Fixed: Regression: Fixed network request repeat mechanism (ede6004)
- Security: Apply npm audit security fixes (0b67446)
## [cli-v1.6.4](https://github.com/laurent22/joplin/releases/tag/cli-v1.6.4) - 2021-01-21T10:01:15Z
- Fixed: Fixed infinite sync issue with OneDrive (#4305)