mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-27 10:32:58 +02:00
Merge branch 'master' of github.com:laurent22/joplin
This commit is contained in:
commit
cdbb7c4b0d
@ -15,10 +15,6 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.state = {
|
|
||||||
settings: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
shared.init(this);
|
shared.init(this);
|
||||||
|
|
||||||
this.checkSyncConfig_ = async () => {
|
this.checkSyncConfig_ = async () => {
|
||||||
@ -68,9 +64,7 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateSettingValue = (key, value) => {
|
const updateSettingValue = (key, value) => {
|
||||||
const settings = Object.assign({}, this.state.settings);
|
return shared.updateSettingValue(this, key, value);
|
||||||
settings[key] = Setting.formatValue(key, value);
|
|
||||||
this.setState({ settings: settings });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Component key needs to be key+value otherwise it doesn't update when the settings change.
|
// Component key needs to be key+value otherwise it doesn't update when the settings change.
|
||||||
@ -142,10 +136,7 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSaveClick() {
|
onSaveClick() {
|
||||||
for (let n in this.state.settings) {
|
shared.saveSettings(this);
|
||||||
if (!this.state.settings.hasOwnProperty(n)) continue;
|
|
||||||
Setting.setValue(n, this.state.settings[n]);
|
|
||||||
}
|
|
||||||
this.props.dispatch({ type: 'NAV_BACK' });
|
this.props.dispatch({ type: 'NAV_BACK' });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,24 +158,11 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const buttonStyle = {
|
const buttonStyle = {
|
||||||
display: this.state.settings === this.props.settings ? 'none' : 'inline-block',
|
display: this.state.changedSettingKeys.length ? 'inline-block' : 'none',
|
||||||
marginRight: 10,
|
marginRight: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
let settingComps = [];
|
const settingComps = shared.settingsToComponents(this, 'desktop', settings);
|
||||||
let keys = Setting.keys(true, 'desktop');
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
const key = keys[i];
|
|
||||||
if (!(key in settings)) {
|
|
||||||
console.warn('Missing setting: ' + key);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const md = Setting.settingMetadata(key);
|
|
||||||
if (md.show && !md.show(settings)) continue;
|
|
||||||
const comp = this.settingToComponent(key, settings[key]);
|
|
||||||
if (!comp) continue;
|
|
||||||
settingComps.push(comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
const syncTargetMd = SyncTargetRegistry.idToMetadata(settings['sync.target']);
|
const syncTargetMd = SyncTargetRegistry.idToMetadata(settings['sync.target']);
|
||||||
|
|
||||||
|
2
ElectronClient/app/package-lock.json
generated
2
ElectronClient/app/package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Joplin",
|
"name": "Joplin",
|
||||||
"version": "1.0.62",
|
"version": "1.0.63",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Joplin",
|
"name": "Joplin",
|
||||||
"version": "1.0.62",
|
"version": "1.0.63",
|
||||||
"description": "Joplin for Desktop",
|
"description": "Joplin for Desktop",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -110,6 +110,7 @@ Select the "WebDAV" synchronisation target and follow the same instructions as f
|
|||||||
|
|
||||||
Known compatible services that use WebDAV:
|
Known compatible services that use WebDAV:
|
||||||
|
|
||||||
|
- [Box.com](https://www.box.com/)
|
||||||
- [DriveHQ](https://www.drivehq.com)
|
- [DriveHQ](https://www.drivehq.com)
|
||||||
- [Zimbra](https://www.zimbra.com/)
|
- [Zimbra](https://www.zimbra.com/)
|
||||||
|
|
||||||
|
@ -244,7 +244,7 @@ The following commands are available in [command-line mode](#command-line-mode):
|
|||||||
Possible values: HH:mm (20:30), h:mm A (8:30 PM).
|
Possible values: HH:mm (20:30), h:mm A (8:30 PM).
|
||||||
Default: "HH:mm"
|
Default: "HH:mm"
|
||||||
|
|
||||||
uncompletedTodosOnTop Show uncompleted todos on top of the lists.
|
uncompletedTodosOnTop Show uncompleted to-dos on top of the lists.
|
||||||
Type: bool.
|
Type: bool.
|
||||||
Default: true
|
Default: true
|
||||||
|
|
||||||
|
@ -133,6 +133,42 @@ class WebDavApi {
|
|||||||
return this.valueFromJson(json, keys, 'array');
|
return this.valueFromJson(json, keys, 'array');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourcePropByName(resource, outputType, propName) {
|
||||||
|
const propStats = resource['d:propstat'];
|
||||||
|
let output = null;
|
||||||
|
if (!Array.isArray(propStats)) throw new Error('Missing d:propstat property');
|
||||||
|
for (let i = 0; i < propStats.length; i++) {
|
||||||
|
const props = propStats[i]['d:prop'];
|
||||||
|
if (!Array.isArray(props) || !props.length) continue;
|
||||||
|
const prop = props[0];
|
||||||
|
if (Array.isArray(prop[propName])) {
|
||||||
|
output = prop[propName];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputType === 'string') {
|
||||||
|
// If the XML has not attribute the value is directly a string
|
||||||
|
// If the XML node has attributes, the value is under "_".
|
||||||
|
// Eg for this XML, the string will be under {"_":"Thu, 01 Feb 2018 17:24:05 GMT"}:
|
||||||
|
// <a:getlastmodified b:dt="dateTime.rfc1123">Thu, 01 Feb 2018 17:24:05 GMT</a:getlastmodified>
|
||||||
|
// For this XML, the value will be "Thu, 01 Feb 2018 17:24:05 GMT"
|
||||||
|
// <a:getlastmodified>Thu, 01 Feb 2018 17:24:05 GMT</a:getlastmodified>
|
||||||
|
|
||||||
|
output = output[0];
|
||||||
|
|
||||||
|
if (typeof output === 'object' && '_' in output) output = output['_'];
|
||||||
|
if (typeof output !== 'string') return null;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputType === 'array') {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Invalid output type: ' + outputType);
|
||||||
|
}
|
||||||
|
|
||||||
async execPropFind(path, depth, fields = null, options = null) {
|
async execPropFind(path, depth, fields = null, options = null) {
|
||||||
if (fields === null) fields = ['d:getlastmodified'];
|
if (fields === null) fields = ['d:getlastmodified'];
|
||||||
|
|
||||||
|
@ -20,11 +20,6 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
super();
|
super();
|
||||||
this.styles_ = {};
|
this.styles_ = {};
|
||||||
|
|
||||||
this.state = {
|
|
||||||
settings: {},
|
|
||||||
settingsChanged: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
shared.init(this);
|
shared.init(this);
|
||||||
|
|
||||||
this.checkSyncConfig_ = async () => {
|
this.checkSyncConfig_ = async () => {
|
||||||
@ -32,11 +27,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.saveButton_press = () => {
|
this.saveButton_press = () => {
|
||||||
for (let n in this.state.settings) {
|
return shared.saveSettings(this);
|
||||||
if (!this.state.settings.hasOwnProperty(n)) continue;
|
|
||||||
Setting.setValue(n, this.state.settings[n]);
|
|
||||||
}
|
|
||||||
this.setState({settingsChanged:false});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,6 +65,11 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
fontSize: theme.fontSize,
|
fontSize: theme.fontSize,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
|
descriptionText: {
|
||||||
|
color: theme.color,
|
||||||
|
fontSize: theme.fontSize,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
settingControl: {
|
settingControl: {
|
||||||
color: theme.color,
|
color: theme.color,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@ -113,12 +109,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
let output = null;
|
let output = null;
|
||||||
|
|
||||||
const updateSettingValue = (key, value) => {
|
const updateSettingValue = (key, value) => {
|
||||||
const settings = Object.assign({}, this.state.settings);
|
return shared.updateSettingValue(this, key, value);
|
||||||
settings[key] = Setting.formatValue(key, value);
|
|
||||||
this.setState({
|
|
||||||
settings: settings,
|
|
||||||
settingsChanged: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const md = Setting.settingMetadata(key);
|
const md = Setting.settingMetadata(key);
|
||||||
@ -187,20 +178,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
render() {
|
render() {
|
||||||
const settings = this.state.settings;
|
const settings = this.state.settings;
|
||||||
|
|
||||||
const keys = Setting.keys(true, 'mobile');
|
const settingComps = shared.settingsToComponents(this, 'mobile', settings);
|
||||||
let settingComps = [];
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
const key = keys[i];
|
|
||||||
//if (key == 'sync.target' && !settings.showAdvancedOptions) continue;
|
|
||||||
if (!Setting.isPublic(key)) continue;
|
|
||||||
|
|
||||||
const md = Setting.settingMetadata(key);
|
|
||||||
if (md.show && !md.show(settings)) continue;
|
|
||||||
|
|
||||||
const comp = this.settingToComponent(key, settings[key]);
|
|
||||||
if (!comp) continue;
|
|
||||||
settingComps.push(comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
const syncTargetMd = SyncTargetRegistry.idToMetadata(settings['sync.target']);
|
const syncTargetMd = SyncTargetRegistry.idToMetadata(settings['sync.target']);
|
||||||
|
|
||||||
@ -208,8 +186,8 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
const messages = shared.checkSyncConfigMessages(this);
|
const messages = shared.checkSyncConfigMessages(this);
|
||||||
const statusComp = !messages.length ? null : (
|
const statusComp = !messages.length ? null : (
|
||||||
<View style={{flex:1, marginTop: 10}}>
|
<View style={{flex:1, marginTop: 10}}>
|
||||||
<Text>{messages[0]}</Text>
|
<Text style={this.styles().descriptionText}>{messages[0]}</Text>
|
||||||
{messages.length >= 1 ? (<Text style={{marginTop:10}}>{messages[1]}</Text>) : null}
|
{messages.length >= 1 ? (<View style={{marginTop:10}}><Text style={this.styles().descriptionText}>{messages[1]}</Text></View>) : null}
|
||||||
</View>);
|
</View>);
|
||||||
|
|
||||||
settingComps.push(
|
settingComps.push(
|
||||||
@ -244,7 +222,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
|||||||
<ScreenHeader
|
<ScreenHeader
|
||||||
title={_('Configuration')}
|
title={_('Configuration')}
|
||||||
showSaveButton={true}
|
showSaveButton={true}
|
||||||
saveButtonDisabled={!this.state.settingsChanged}
|
saveButtonDisabled={!this.state.changedSettingKeys.length}
|
||||||
onSaveButtonPress={this.saveButton_press}
|
onSaveButtonPress={this.saveButton_press}
|
||||||
/>
|
/>
|
||||||
<ScrollView >
|
<ScrollView >
|
||||||
|
@ -7,6 +7,8 @@ const shared = {}
|
|||||||
shared.init = function(comp) {
|
shared.init = function(comp) {
|
||||||
if (!comp.state) comp.state = {};
|
if (!comp.state) comp.state = {};
|
||||||
comp.state.checkSyncConfigResult = null;
|
comp.state.checkSyncConfigResult = null;
|
||||||
|
comp.state.settings = {};
|
||||||
|
comp.state.changedSettingKeys = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
shared.checkSyncConfig = async function(comp, settings) {
|
shared.checkSyncConfig = async function(comp, settings) {
|
||||||
@ -34,4 +36,46 @@ shared.checkSyncConfigMessages = function(comp) {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shared.updateSettingValue = function(comp, key, value) {
|
||||||
|
const settings = Object.assign({}, comp.state.settings);
|
||||||
|
const changedSettingKeys = comp.state.changedSettingKeys.slice();
|
||||||
|
settings[key] = Setting.formatValue(key, value);
|
||||||
|
if (changedSettingKeys.indexOf(key) < 0) changedSettingKeys.push(key);
|
||||||
|
|
||||||
|
comp.setState({
|
||||||
|
settings: settings,
|
||||||
|
changedSettingKeys: changedSettingKeys,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
shared.saveSettings = function(comp) {
|
||||||
|
for (let key in comp.state.settings) {
|
||||||
|
if (!comp.state.settings.hasOwnProperty(key)) continue;
|
||||||
|
if (comp.state.changedSettingKeys.indexOf(key) < 0) continue;
|
||||||
|
console.info("Saving", key, comp.state.settings[key]);
|
||||||
|
Setting.setValue(key, comp.state.settings[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
comp.setState({ changedSettingKeys: [] });
|
||||||
|
}
|
||||||
|
|
||||||
|
shared.settingsToComponents = function(comp, device, settings) {
|
||||||
|
const keys = Setting.keys(true, device);
|
||||||
|
const settingComps = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
const key = keys[i];
|
||||||
|
if (!Setting.isPublic(key)) continue;
|
||||||
|
|
||||||
|
const md = Setting.settingMetadata(key);
|
||||||
|
if (md.show && !md.show(settings)) continue;
|
||||||
|
|
||||||
|
const settingComp = comp.settingToComponent(key, settings[key]);
|
||||||
|
if (!settingComp) continue;
|
||||||
|
settingComps.push(settingComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return settingComps
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = shared;
|
module.exports = shared;
|
@ -27,7 +27,7 @@ class FileApiDriverWebDav {
|
|||||||
const result = await this.api().execPropFind(path, 0, [
|
const result = await this.api().execPropFind(path, 0, [
|
||||||
'd:getlastmodified',
|
'd:getlastmodified',
|
||||||
'd:resourcetype',
|
'd:resourcetype',
|
||||||
'd:getcontentlength', // Remove this once PUT call issue is sorted out
|
// 'd:getcontentlength', // Remove this once PUT call issue is sorted out
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const resource = this.api().objectFromJson(result, ['d:multistatus', 'd:response', 0]);
|
const resource = this.api().objectFromJson(result, ['d:multistatus', 'd:response', 0]);
|
||||||
@ -39,22 +39,40 @@ class FileApiDriverWebDav {
|
|||||||
}
|
}
|
||||||
|
|
||||||
statFromResource_(resource, path) {
|
statFromResource_(resource, path) {
|
||||||
const isCollection = this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:resourcetype', 0, 'd:collection', 0]);
|
// WebDAV implementations are always slighly different from one server to another but, at the minimum,
|
||||||
const lastModifiedString = this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:getlastmodified', 0]);
|
// a resource should have a propstat key - if not it's probably an error.
|
||||||
|
const propStat = this.api().arrayFromJson(resource, ['d:propstat']);
|
||||||
|
if (!Array.isArray(propStat)) throw new Error('Invalid WebDAV resource format: ' + JSON.stringify(resource));
|
||||||
|
|
||||||
|
const resourceTypes = this.api().resourcePropByName(resource, 'array', 'd:resourcetype');
|
||||||
|
let isDir = false;
|
||||||
|
if (Array.isArray(resourceTypes)) {
|
||||||
|
for (let i = 0; i < resourceTypes.length; i++) {
|
||||||
|
const t = resourceTypes[i];
|
||||||
|
if (typeof t === 'object' && 'd:collection' in t) {
|
||||||
|
isDir = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastModifiedString = this.api().resourcePropByName(resource, 'string', 'd:getlastmodified');
|
||||||
|
|
||||||
// const sizeDONOTUSE = Number(this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:getcontentlength', 0]));
|
// const sizeDONOTUSE = Number(this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:getcontentlength', 0]));
|
||||||
// if (isNaN(sizeDONOTUSE)) throw new Error('Cannot get content size: ' + JSON.stringify(resource));
|
// if (isNaN(sizeDONOTUSE)) throw new Error('Cannot get content size: ' + JSON.stringify(resource));
|
||||||
|
|
||||||
if (!lastModifiedString) throw new Error('Could not get lastModified date: ' + JSON.stringify(resource));
|
|
||||||
|
|
||||||
const lastModifiedDate = new Date(lastModifiedString);
|
// Note: Not all WebDAV servers return a getlastmodified date (eg. Seafile, which doesn't return the
|
||||||
|
// property for folders) so we can only throw an error if it's a file.
|
||||||
|
if (!lastModifiedString && !isDir) throw new Error('Could not get lastModified date for resource: ' + JSON.stringify(resource));
|
||||||
|
const lastModifiedDate = lastModifiedString ? new Date(lastModifiedString) : new Date();
|
||||||
if (isNaN(lastModifiedDate.getTime())) throw new Error('Invalid date: ' + lastModifiedString);
|
if (isNaN(lastModifiedDate.getTime())) throw new Error('Invalid date: ' + lastModifiedString);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
path: path,
|
path: path,
|
||||||
// created_time: lastModifiedDate.getTime(),
|
// created_time: lastModifiedDate.getTime(),
|
||||||
updated_time: lastModifiedDate.getTime(),
|
updated_time: lastModifiedDate.getTime(),
|
||||||
isDir: isCollection === '',
|
isDir: isDir,
|
||||||
// sizeDONOTUSE: sizeDONOTUSE, // This property is used only for the WebDAV PUT hack (see below) so mark it as such so that it can be removed with the hack later on.
|
// sizeDONOTUSE: sizeDONOTUSE, // This property is used only for the WebDAV PUT hack (see below) so mark it as such so that it can be removed with the hack later on.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -260,7 +278,7 @@ class FileApiDriverWebDav {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const resources = this.api().arrayFromJson(result, ['d:multistatus', 'd:response']);
|
const resources = this.api().arrayFromJson(result, ['d:multistatus', 'd:response']);
|
||||||
const stats = this.statsFromResources_(resources)
|
const stats = this.statsFromResources_(resources);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: stats,
|
items: stats,
|
||||||
|
@ -58,7 +58,7 @@ class Setting extends BaseModel {
|
|||||||
// recent: _('Non-completed and recently completed ones'),
|
// recent: _('Non-completed and recently completed ones'),
|
||||||
// nonCompleted: _('Non-completed ones only'),
|
// nonCompleted: _('Non-completed ones only'),
|
||||||
// })},
|
// })},
|
||||||
'uncompletedTodosOnTop': { value: true, type: Setting.TYPE_BOOL, public: true, label: () => _('Show uncompleted todos on top of the lists') },
|
'uncompletedTodosOnTop': { value: true, type: Setting.TYPE_BOOL, public: true, label: () => _('Show uncompleted to-dos on top of the lists') },
|
||||||
'trackLocation': { value: true, type: Setting.TYPE_BOOL, public: true, label: () => _('Save geo-location with notes') },
|
'trackLocation': { value: true, type: Setting.TYPE_BOOL, public: true, label: () => _('Save geo-location with notes') },
|
||||||
'newTodoFocus': { value: 'title', type: Setting.TYPE_STRING, isEnum: true, public: true, appTypes: ['desktop'], label: () => _('When creating a new to-do:'), options: () => {
|
'newTodoFocus': { value: 'title', type: Setting.TYPE_STRING, isEnum: true, public: true, appTypes: ['desktop'], label: () => _('When creating a new to-do:'), options: () => {
|
||||||
return {
|
return {
|
||||||
|
@ -310,6 +310,7 @@ sudo ln -s ~/.joplin-bin/bin/joplin /usr/bin/joplin
|
|||||||
<p>Select the "WebDAV" synchronisation target and follow the same instructions as for Nextcloud above.</p>
|
<p>Select the "WebDAV" synchronisation target and follow the same instructions as for Nextcloud above.</p>
|
||||||
<p>Known compatible services that use WebDAV:</p>
|
<p>Known compatible services that use WebDAV:</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li><a href="https://www.box.com/">Box.com</a></li>
|
||||||
<li><a href="https://www.drivehq.com">DriveHQ</a></li>
|
<li><a href="https://www.drivehq.com">DriveHQ</a></li>
|
||||||
<li><a href="https://www.zimbra.com/">Zimbra</a></li>
|
<li><a href="https://www.zimbra.com/">Zimbra</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -390,14 +391,14 @@ $$
|
|||||||
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/hr.png" alt=""></td>
|
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/hr.png" alt=""></td>
|
||||||
<td>Croatian</td>
|
<td>Croatian</td>
|
||||||
<td>hr_HR</td>
|
<td>hr_HR</td>
|
||||||
<td>Hrvoje Mandić <a href="mailto:trbuhom@net.hr">trbuhom@net.hr</a></td>
|
<td>Hrvoje Mandić <a href="mailto:trbuhom@net.hr">trbuhom@net.hr</a></td>
|
||||||
<td>72%</td>
|
<td>72%</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/de.png" alt=""></td>
|
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/de.png" alt=""></td>
|
||||||
<td>Deutsch</td>
|
<td>Deutsch</td>
|
||||||
<td>de_DE</td>
|
<td>de_DE</td>
|
||||||
<td>Tobias Strobel <a href="mailto:git@strobeltobias.de">git@strobeltobias.de</a></td>
|
<td>Tobias Strobel <a href="mailto:git@strobeltobias.de">git@strobeltobias.de</a></td>
|
||||||
<td>91%</td>
|
<td>91%</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -453,14 +454,14 @@ $$
|
|||||||
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/ru.png" alt=""></td>
|
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/ru.png" alt=""></td>
|
||||||
<td>Русский</td>
|
<td>Русский</td>
|
||||||
<td>ru_RU</td>
|
<td>ru_RU</td>
|
||||||
<td>Artyom Karlov <a href="mailto:artyom.karlov@gmail.com">artyom.karlov@gmail.com</a></td>
|
<td>Artyom Karlov <a href="mailto:artyom.karlov@gmail.com">artyom.karlov@gmail.com</a></td>
|
||||||
<td>94%</td>
|
<td>94%</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/cn.png" alt=""></td>
|
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/cn.png" alt=""></td>
|
||||||
<td>中文 (简体)</td>
|
<td>中文 (简体)</td>
|
||||||
<td>zh_CN</td>
|
<td>zh_CN</td>
|
||||||
<td>RCJacH <a href="mailto:RCJacH@outlook.com">RCJacH@outlook.com</a></td>
|
<td>RCJacH <a href="mailto:RCJacH@outlook.com">RCJacH@outlook.com</a></td>
|
||||||
<td>75%</td>
|
<td>75%</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -395,7 +395,7 @@ Possible keys/values:
|
|||||||
Possible values: HH:mm (20:30), h:mm A (8:30 PM).
|
Possible values: HH:mm (20:30), h:mm A (8:30 PM).
|
||||||
Default: "HH:mm"
|
Default: "HH:mm"
|
||||||
|
|
||||||
uncompletedTodosOnTop Show uncompleted todos on top of the lists.
|
uncompletedTodosOnTop Show uncompleted to-dos on top of the lists.
|
||||||
Type: bool.
|
Type: bool.
|
||||||
Default: true
|
Default: true
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
"*.min.js",
|
"*.min.js",
|
||||||
"ElectronClient/app/gui/note-viewer/highlight/*.pack.js",
|
"ElectronClient/app/gui/note-viewer/highlight/*.pack.js",
|
||||||
"ElectronClient/app/css/font-awesome.min.css",
|
"ElectronClient/app/css/font-awesome.min.css",
|
||||||
|
"docs/*.html",
|
||||||
|
"docs/*.svg",
|
||||||
],
|
],
|
||||||
"folder_exclude_patterns":
|
"folder_exclude_patterns":
|
||||||
[
|
[
|
||||||
|
Loading…
Reference in New Issue
Block a user