mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-20 06:21:25 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a75d41cbb | ||
|
|
5252cc0372 | ||
|
|
5022155317 | ||
|
|
d36f925c65 | ||
|
|
3ae33e0500 | ||
|
|
13e442a0c7 | ||
|
|
6288716966 | ||
|
|
47d2cf9733 | ||
|
|
ae6a9ecee4 |
@@ -12,10 +12,10 @@ max_document_size = 50000000
|
|||||||
|
|
||||||
[chttpd]
|
[chttpd]
|
||||||
require_valid_user = true
|
require_valid_user = true
|
||||||
|
max_http_request_size = 4294967296
|
||||||
|
|
||||||
[chttpd_auth]
|
[chttpd_auth]
|
||||||
require_valid_user = true
|
require_valid_user = true
|
||||||
max_http_request_size = 4294967296
|
|
||||||
authentication_redirect = /_utils/session.html
|
authentication_redirect = /_utils/session.html
|
||||||
|
|
||||||
[httpd]
|
[httpd]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.19.8",
|
"version": "0.19.12",
|
||||||
"minAppVersion": "0.9.12",
|
"minAppVersion": "0.9.12",
|
||||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"author": "vorotamoroz",
|
"author": "vorotamoroz",
|
||||||
|
|||||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -1,17 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.19.8",
|
"version": "0.19.12",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.19.8",
|
"version": "0.19.12",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
"idb": "^7.1.1",
|
"idb": "^7.1.1",
|
||||||
"xxhash-wasm": "^0.4.2"
|
"xxhash-wasm": "^0.4.2",
|
||||||
|
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tsconfig/svelte": "^4.0.1",
|
"@tsconfig/svelte": "^4.0.1",
|
||||||
@@ -4076,6 +4077,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz",
|
||||||
"integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA=="
|
"integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/xxhash-wasm-102": {
|
||||||
|
"name": "xxhash-wasm",
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A=="
|
||||||
|
},
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
@@ -7032,6 +7039,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz",
|
||||||
"integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA=="
|
"integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA=="
|
||||||
},
|
},
|
||||||
|
"xxhash-wasm-102": {
|
||||||
|
"version": "npm:xxhash-wasm@1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A=="
|
||||||
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.19.8",
|
"version": "0.19.12",
|
||||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -47,6 +47,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
"idb": "^7.1.1",
|
"idb": "^7.1.1",
|
||||||
"xxhash-wasm": "^0.4.2"
|
"xxhash-wasm": "^0.4.2",
|
||||||
|
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import { ICXHeader, PERIODIC_PLUGIN_SWEEP, } from "./types";
|
|||||||
import { Parallels, delay, getDocData } from "./lib/src/utils";
|
import { Parallels, delay, getDocData } from "./lib/src/utils";
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger } from "./lib/src/logger";
|
||||||
import { WrappedNotice } from "./lib/src/wrapper";
|
import { WrappedNotice } from "./lib/src/wrapper";
|
||||||
import { base64ToArrayBuffer, arrayBufferToBase64, readString, uint8ArrayToHexString } from "./lib/src/strbin";
|
import { base64ToArrayBuffer, arrayBufferToBase64, readString, crc32CKHash } from "./lib/src/strbin";
|
||||||
import { runWithLock } from "./lib/src/lock";
|
import { runWithLock } from "./lib/src/lock";
|
||||||
import { LiveSyncCommands } from "./LiveSyncCommands";
|
import { LiveSyncCommands } from "./LiveSyncCommands";
|
||||||
import { stripAllPrefixes } from "./lib/src/path";
|
import { stripAllPrefixes } from "./lib/src/path";
|
||||||
import { PeriodicProcessor, askYesNo, disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask } from "./utils";
|
import { PeriodicProcessor, askYesNo, disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask } from "./utils";
|
||||||
import { Semaphore } from "./lib/src/semaphore";
|
|
||||||
import { PluginDialogModal } from "./dialogs";
|
import { PluginDialogModal } from "./dialogs";
|
||||||
import { JsonResolveModal } from "./JsonResolveModal";
|
import { JsonResolveModal } from "./JsonResolveModal";
|
||||||
|
import { pipeGeneratorToGenerator, processAllGeneratorTasksWithConcurrencyLimit } from './lib/src/task';
|
||||||
|
|
||||||
|
|
||||||
function serialize<T>(obj: T): string {
|
function serialize<T>(obj: T): string {
|
||||||
@@ -36,14 +36,6 @@ function deserialize<T>(str: string, def: T) {
|
|||||||
export const pluginList = writable([] as PluginDataExDisplay[]);
|
export const pluginList = writable([] as PluginDataExDisplay[]);
|
||||||
export const pluginIsEnumerating = writable(false);
|
export const pluginIsEnumerating = writable(false);
|
||||||
|
|
||||||
const encoder = new TextEncoder();
|
|
||||||
const hashString = (async (key: string) => {
|
|
||||||
// const buff = writeString(key);
|
|
||||||
const buff = encoder.encode(key);
|
|
||||||
const digest = await crypto.subtle.digest('SHA-256', buff);
|
|
||||||
return uint8ArrayToHexString(new Uint8Array(digest));
|
|
||||||
})
|
|
||||||
|
|
||||||
export type PluginDataExFile = {
|
export type PluginDataExFile = {
|
||||||
filename: string,
|
filename: string,
|
||||||
data?: string[],
|
data?: string[],
|
||||||
@@ -180,34 +172,25 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
pluginList.set(this.pluginList)
|
pluginList.set(this.pluginList)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
scheduleTask("update-plugin-list-task", 200, async () => {
|
||||||
await runWithLock("update-plugin-list", false, async () => {
|
await runWithLock("update-plugin-list", false, async () => {
|
||||||
// if (updatedDocumentPath != "") pluginList.update(e => e.filter(ee => ee.documentPath != updatedDocumentPath));
|
try {
|
||||||
// const work: Record<string, Record<string, Record<string, Record<string, PluginDataEntryEx>>>> = {};
|
const updatedDocumentId = updatedDocumentPath ? await this.path2id(updatedDocumentPath) : "";
|
||||||
const entries = [] as PluginDataExDisplay[]
|
const plugins = updatedDocumentPath ?
|
||||||
const plugins = this.localDatabase.findEntries(ICXHeader + "", `${ICXHeader}\u{10ffff}`, { include_docs: true });
|
this.localDatabase.findEntries(updatedDocumentId, updatedDocumentId + "\u{10ffff}", { include_docs: true, key: updatedDocumentId, limit: 1 }) :
|
||||||
const semaphore = Semaphore(4);
|
this.localDatabase.findEntries(ICXHeader + "", `${ICXHeader}\u{10ffff}`, { include_docs: true });
|
||||||
const para = Parallels();
|
let count = 0;
|
||||||
let count = 0;
|
pluginIsEnumerating.set(true);
|
||||||
pluginIsEnumerating.set(true);
|
for await (const v of processAllGeneratorTasksWithConcurrencyLimit(20, pipeGeneratorToGenerator(plugins, async plugin => {
|
||||||
let processed = false;
|
const path = plugin.path || this.getPath(plugin);
|
||||||
try {
|
if (updatedDocumentPath && updatedDocumentPath != path) {
|
||||||
for await (const plugin of plugins) {
|
return false;
|
||||||
const path = plugin.path || this.getPath(plugin);
|
}
|
||||||
if (updatedDocumentPath && updatedDocumentPath != path) {
|
const oldEntry = (this.pluginList.find(e => e.documentPath == path));
|
||||||
continue;
|
if (oldEntry && oldEntry.mtime == plugin.mtime) return false;
|
||||||
}
|
|
||||||
processed = true;
|
|
||||||
const oldEntry = (this.pluginList.find(e => e.documentPath == path));
|
|
||||||
if (oldEntry && oldEntry.mtime == plugin.mtime) continue;
|
|
||||||
await para.wait(5);
|
|
||||||
para.add((async (v) => {
|
|
||||||
|
|
||||||
const release = await semaphore.acquire(1);
|
|
||||||
try {
|
try {
|
||||||
count++;
|
count++;
|
||||||
if (count % 10 == 0) Logger(`Enumerating files... ${count}`, logLevel, "get-plugins");
|
if (count % 10 == 0) Logger(`Enumerating files... ${count}`, logLevel, "get-plugins");
|
||||||
|
|
||||||
Logger(`plugin-${path}`, LOG_LEVEL.VERBOSE);
|
Logger(`plugin-${path}`, LOG_LEVEL.VERBOSE);
|
||||||
const wx = await this.localDatabase.getDBEntry(path, null, false, false);
|
const wx = await this.localDatabase.getDBEntry(path, null, false, false);
|
||||||
if (wx) {
|
if (wx) {
|
||||||
@@ -216,41 +199,40 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
for (const file of data.files) {
|
for (const file of data.files) {
|
||||||
const work = { ...file };
|
const work = { ...file };
|
||||||
const tempStr = getDocData(work.data);
|
const tempStr = getDocData(work.data);
|
||||||
work.data = [await hashString(tempStr)];
|
work.data = [crc32CKHash(tempStr)];
|
||||||
xFiles.push(work);
|
xFiles.push(work);
|
||||||
}
|
}
|
||||||
entries.push({
|
return ({
|
||||||
...data,
|
...data,
|
||||||
documentPath: this.getPath(wx),
|
documentPath: this.getPath(wx),
|
||||||
files: xFiles
|
files: xFiles
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// return entries;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
//TODO
|
//TODO
|
||||||
Logger(`Something happened at enumerating customization :${v.path}`, LOG_LEVEL.NOTICE);
|
Logger(`Something happened at enumerating customization :${path}`, LOG_LEVEL.NOTICE);
|
||||||
console.warn(ex);
|
console.warn(ex);
|
||||||
} finally {
|
}
|
||||||
release();
|
return false;
|
||||||
|
}))) {
|
||||||
|
if ("ok" in v) {
|
||||||
|
if (v.ok != false) {
|
||||||
|
let newList = [...this.pluginList];
|
||||||
|
const item = v.ok;
|
||||||
|
newList = newList.filter(x => x.documentPath != item.documentPath);
|
||||||
|
newList.push(item)
|
||||||
|
if (updatedDocumentPath != "") newList = newList.filter(e => e.documentPath != updatedDocumentPath);
|
||||||
|
this.pluginList = newList;
|
||||||
|
pluginList.set(newList);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)(plugin));
|
Logger(`All files enumerated`, logLevel, "get-plugins");
|
||||||
|
} finally {
|
||||||
|
pluginIsEnumerating.set(false);
|
||||||
}
|
}
|
||||||
await para.all();
|
});
|
||||||
let newList = [...this.pluginList];
|
|
||||||
for (const item of entries) {
|
|
||||||
newList = newList.filter(x => x.documentPath != item.documentPath);
|
|
||||||
newList.push(item)
|
|
||||||
}
|
|
||||||
if (updatedDocumentPath != "" && !processed) newList = newList.filter(e => e.documentPath != updatedDocumentPath);
|
|
||||||
|
|
||||||
this.pluginList = newList;
|
|
||||||
pluginList.set(newList);
|
|
||||||
|
|
||||||
|
|
||||||
Logger(`All files enumerated`, logLevel, "get-plugins");
|
|
||||||
} finally {
|
|
||||||
pluginIsEnumerating.set(false);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// return entries;
|
// return entries;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Notice, normalizePath, PluginManifest } from "./deps";
|
import { Notice, normalizePath, type PluginManifest } from "./deps";
|
||||||
import { EntryDoc, LoadedEntry, LOG_LEVEL, InternalFileEntry, FilePathWithPrefix, FilePath } from "./lib/src/types";
|
import { type EntryDoc, type LoadedEntry, LOG_LEVEL, type InternalFileEntry, type FilePathWithPrefix, type FilePath } from "./lib/src/types";
|
||||||
import { InternalFileInfo, ICHeader, ICHeaderEnd } from "./types";
|
import { type InternalFileInfo, ICHeader, ICHeaderEnd } from "./types";
|
||||||
import { Parallels, delay, isDocContentSame } from "./lib/src/utils";
|
import { Parallels, delay, isDocContentSame } from "./lib/src/utils";
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger } from "./lib/src/logger";
|
||||||
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { TFile, Modal, App } from "./deps";
|
import { TFile, Modal, App, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "./deps";
|
||||||
import { getPathFromTFile, isValidPath } from "./utils";
|
import { getPathFromTFile, isValidPath } from "./utils";
|
||||||
import { base64ToArrayBuffer, base64ToString, escapeStringToHTML } from "./lib/src/strbin";
|
import { base64ToArrayBuffer, base64ToString, escapeStringToHTML } from "./lib/src/strbin";
|
||||||
import ObsidianLiveSyncPlugin from "./main";
|
import ObsidianLiveSyncPlugin from "./main";
|
||||||
import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
|
|
||||||
import { type DocumentID, type FilePathWithPrefix, type LoadedEntry, LOG_LEVEL } from "./lib/src/types";
|
import { type DocumentID, type FilePathWithPrefix, type LoadedEntry, LOG_LEVEL } from "./lib/src/types";
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger } from "./lib/src/logger";
|
||||||
import { isErrorOfMissingDoc } from "./lib/src/utils_couchdb";
|
import { isErrorOfMissingDoc } from "./lib/src/utils_couchdb";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { onDestroy, onMount } from "svelte";
|
import { onDestroy, onMount } from "svelte";
|
||||||
import type { AnyEntry, FilePathWithPrefix } from "./lib/src/types";
|
import type { AnyEntry, FilePathWithPrefix } from "./lib/src/types";
|
||||||
import { getDocData, isDocContentSame } from "./lib/src/utils";
|
import { getDocData, isDocContentSame } from "./lib/src/utils";
|
||||||
import { diff_match_patch } from "diff-match-patch";
|
import { diff_match_patch } from "./deps";
|
||||||
import { DocumentHistoryModal } from "./DocumentHistoryModal";
|
import { DocumentHistoryModal } from "./DocumentHistoryModal";
|
||||||
import { isPlainText, stripAllPrefixes } from "./lib/src/path";
|
import { isPlainText, stripAllPrefixes } from "./lib/src/path";
|
||||||
import { TFile } from "./deps";
|
import { TFile } from "./deps";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { type Diff, DIFF_DELETE, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
|
import { type Diff, DIFF_DELETE, DIFF_INSERT, diff_match_patch } from "./deps";
|
||||||
import type { FilePath, LoadedEntry } from "./lib/src/types";
|
import type { FilePath, LoadedEntry } from "./lib/src/types";
|
||||||
import { base64ToString } from "./lib/src/strbin";
|
import { base64ToString } from "./lib/src/strbin";
|
||||||
import { getDocData } from "./lib/src/utils";
|
import { getDocData } from "./lib/src/utils";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { App, PluginSettingTab, Setting, sanitizeHTMLToDom, TextAreaComponent, MarkdownRenderer, stringifyYaml } from "./deps";
|
import { App, PluginSettingTab, Setting, sanitizeHTMLToDom, TextAreaComponent, MarkdownRenderer, stringifyYaml } from "./deps";
|
||||||
import { DEFAULT_SETTINGS, LOG_LEVEL, type ObsidianLiveSyncSettings, type ConfigPassphraseStore, type RemoteDBSettings } from "./lib/src/types";
|
import { DEFAULT_SETTINGS, LOG_LEVEL, type ObsidianLiveSyncSettings, type ConfigPassphraseStore, type RemoteDBSettings, type FilePathWithPrefix, type HashAlgorithm, type DocumentID } from "./lib/src/types";
|
||||||
import { delay } from "./lib/src/utils";
|
import { delay } from "./lib/src/utils";
|
||||||
import { Semaphore } from "./lib/src/semaphore";
|
import { Semaphore } from "./lib/src/semaphore";
|
||||||
import { versionNumberString2Number } from "./lib/src/strbin";
|
import { versionNumberString2Number } from "./lib/src/strbin";
|
||||||
@@ -111,7 +111,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MarkdownRenderer.renderMarkdown(updateInformation, informationDivEl, "/", null);
|
MarkdownRenderer.renderMarkdown(updateInformation, informationDivEl, "/", this.plugin);
|
||||||
|
|
||||||
|
|
||||||
addScreenElement("100", containerInformationEl);
|
addScreenElement("100", containerInformationEl);
|
||||||
@@ -1536,7 +1536,73 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
Logger("done", LOG_LEVEL.NOTICE, "verify");
|
Logger("done", LOG_LEVEL.NOTICE, "verify");
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
new Setting(containerHatchEl)
|
||||||
|
.setName("Check and convert non-path-obfuscated files")
|
||||||
|
.setDesc("")
|
||||||
|
.addButton((button) =>
|
||||||
|
button
|
||||||
|
.setButtonText("Perform")
|
||||||
|
.setDisabled(false)
|
||||||
|
.setWarning()
|
||||||
|
.onClick(async () => {
|
||||||
|
for await (const docName of this.plugin.localDatabase.findAllDocNames()) {
|
||||||
|
if (!docName.startsWith("f:")) {
|
||||||
|
const idEncoded = await this.plugin.path2id(docName as FilePathWithPrefix);
|
||||||
|
const doc = await this.plugin.localDatabase.getRaw(docName as DocumentID);
|
||||||
|
if (!doc) continue;
|
||||||
|
if (doc.type != "newnote" && doc.type != "plain") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (doc?.deleted ?? false) continue;
|
||||||
|
const newDoc = { ...doc };
|
||||||
|
//Prepare converted data
|
||||||
|
newDoc._id = idEncoded;
|
||||||
|
newDoc.path = docName as FilePathWithPrefix;
|
||||||
|
delete newDoc._rev;
|
||||||
|
try {
|
||||||
|
const obfuscatedDoc = await this.plugin.localDatabase.getRaw(idEncoded, { revs_info: true });
|
||||||
|
// Unfortunately we have to delete one of them.
|
||||||
|
// Just now, save it as a conflicted document.
|
||||||
|
obfuscatedDoc._revs_info?.shift(); // Drop latest revision.
|
||||||
|
const previousRev = obfuscatedDoc._revs_info?.shift(); // Use second revision.
|
||||||
|
if (previousRev) {
|
||||||
|
newDoc._rev = previousRev.rev;
|
||||||
|
} else {
|
||||||
|
//If there are no revisions, set the possibly unique one
|
||||||
|
newDoc._rev = "1-" + (`00000000000000000000000000000000${~~(Math.random() * 1e9)}${~~(Math.random() * 1e9)}${~~(Math.random() * 1e9)}${~~(Math.random() * 1e9)}`.slice(-32));
|
||||||
|
}
|
||||||
|
const ret = await this.plugin.localDatabase.putRaw(newDoc, { force: true });
|
||||||
|
if (ret.ok) {
|
||||||
|
Logger(`${docName} has been converted as conflicted document`, LOG_LEVEL.NOTICE);
|
||||||
|
doc._deleted = true;
|
||||||
|
if ((await this.plugin.localDatabase.putRaw(doc)).ok) {
|
||||||
|
Logger(`Old ${docName} has been deleted`, LOG_LEVEL.NOTICE);
|
||||||
|
}
|
||||||
|
await this.plugin.showIfConflicted(docName as FilePathWithPrefix);
|
||||||
|
} else {
|
||||||
|
Logger(`Converting ${docName} Failed!`, LOG_LEVEL.NOTICE);
|
||||||
|
Logger(ret, LOG_LEVEL.VERBOSE);
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex?.status == 404) {
|
||||||
|
// We can perform this safely
|
||||||
|
if ((await this.plugin.localDatabase.putRaw(newDoc)).ok) {
|
||||||
|
Logger(`${docName} has been converted`, LOG_LEVEL.NOTICE);
|
||||||
|
doc._deleted = true;
|
||||||
|
if ((await this.plugin.localDatabase.putRaw(doc)).ok) {
|
||||||
|
Logger(`Old ${docName} has been deleted`, LOG_LEVEL.NOTICE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger(`Something went wrong on converting ${docName}`, LOG_LEVEL.NOTICE);
|
||||||
|
Logger(ex, LOG_LEVEL.VERBOSE);
|
||||||
|
// Something wrong.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logger(`Converting finished`, LOG_LEVEL.NOTICE);
|
||||||
|
}));
|
||||||
new Setting(containerHatchEl)
|
new Setting(containerHatchEl)
|
||||||
.setName("Suspend file watching")
|
.setName("Suspend file watching")
|
||||||
.setDesc("Stop watching for file change.")
|
.setDesc("Stop watching for file change.")
|
||||||
@@ -1649,6 +1715,22 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
await this.plugin.initializeDatabase();
|
await this.plugin.initializeDatabase();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
new Setting(containerHatchEl)
|
||||||
|
.setName("The Hash algorithm for chunk IDs")
|
||||||
|
.setDesc("xxhash64 is the current default.")
|
||||||
|
.setClass("wizardHidden")
|
||||||
|
.addDropdown((dropdown) =>
|
||||||
|
dropdown
|
||||||
|
.addOptions({ "": "Old Algorithm", "xxhash32": "xxhash32 (Fast)", "xxhash64": "xxhash64 (Fastest)" } as Record<HashAlgorithm, string>)
|
||||||
|
.setValue(this.plugin.settings.hashAlg)
|
||||||
|
.onChange(async (value: HashAlgorithm) => {
|
||||||
|
this.plugin.settings.hashAlg = value;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.setClass("wizardHidden");
|
||||||
|
|
||||||
addScreenElement("50", containerHatchEl);
|
addScreenElement("50", containerHatchEl);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ import {
|
|||||||
} from "obsidian";
|
} from "obsidian";
|
||||||
const normalizePath = normalizePath_ as <T extends string | FilePath>(from: T) => T;
|
const normalizePath = normalizePath_ as <T extends string | FilePath>(from: T) => T;
|
||||||
export { normalizePath }
|
export { normalizePath }
|
||||||
|
export { type Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
|
||||||
2
src/lib
2
src/lib
Submodule src/lib updated: 63fa0074fe...aacf942453
71
src/main.ts
71
src/main.ts
@@ -1,11 +1,11 @@
|
|||||||
const isDebug = false;
|
const isDebug = false;
|
||||||
|
|
||||||
import { type Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
|
import { type Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "./deps";
|
||||||
import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, type RequestUrlParam, type RequestUrlResponse, requestUrl } from "./deps";
|
import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, type RequestUrlParam, type RequestUrlResponse, requestUrl } from "./deps";
|
||||||
import { type EntryDoc, type LoadedEntry, type ObsidianLiveSyncSettings, type diff_check_result, type diff_result_leaf, type EntryBody, LOG_LEVEL, VER, DEFAULT_SETTINGS, type diff_result, FLAGMD_REDFLAG, SYNCINFO_ID, SALT_OF_PASSPHRASE, type ConfigPassphraseStore, type CouchDBConnection, FLAGMD_REDFLAG2, FLAGMD_REDFLAG3, PREFIXMD_LOGFILE, type DatabaseConnectingStatus, type EntryHasPath, type DocumentID, type FilePathWithPrefix, type FilePath, type AnyEntry } from "./lib/src/types";
|
import { type EntryDoc, type LoadedEntry, type ObsidianLiveSyncSettings, type diff_check_result, type diff_result_leaf, type EntryBody, LOG_LEVEL, VER, DEFAULT_SETTINGS, type diff_result, FLAGMD_REDFLAG, SYNCINFO_ID, SALT_OF_PASSPHRASE, type ConfigPassphraseStore, type CouchDBConnection, FLAGMD_REDFLAG2, FLAGMD_REDFLAG3, PREFIXMD_LOGFILE, type DatabaseConnectingStatus, type EntryHasPath, type DocumentID, type FilePathWithPrefix, type FilePath, type AnyEntry } from "./lib/src/types";
|
||||||
import { type InternalFileInfo, type queueItem, type CacheData, type FileEventItem, FileWatchEventQueueMax } from "./types";
|
import { type InternalFileInfo, type queueItem, type CacheData, type FileEventItem, FileWatchEventQueueMax } from "./types";
|
||||||
import { getDocData, isDocContentSame, Parallels } from "./lib/src/utils";
|
import { arrayToChunkedArray, getDocData, isDocContentSame } from "./lib/src/utils";
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger, setGlobalLogFunction } from "./lib/src/logger";
|
||||||
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
||||||
import { ConflictResolveModal } from "./ConflictResolveModal";
|
import { ConflictResolveModal } from "./ConflictResolveModal";
|
||||||
import { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab";
|
import { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab";
|
||||||
@@ -14,7 +14,7 @@ import { applyPatch, cancelAllPeriodicTask, cancelAllTasks, cancelTask, generate
|
|||||||
import { encrypt, tryDecrypt } from "./lib/src/e2ee_v2";
|
import { encrypt, tryDecrypt } from "./lib/src/e2ee_v2";
|
||||||
import { enableEncryption, isCloudantURI, isErrorOfMissingDoc, isValidRemoteCouchDBURI } from "./lib/src/utils_couchdb";
|
import { enableEncryption, isCloudantURI, isErrorOfMissingDoc, isValidRemoteCouchDBURI } from "./lib/src/utils_couchdb";
|
||||||
import { getGlobalStore, ObservableStore, observeStores } from "./lib/src/store";
|
import { getGlobalStore, ObservableStore, observeStores } from "./lib/src/store";
|
||||||
import { lockStore, logMessageStore, logStore } from "./lib/src/stores";
|
import { lockStore, logMessageStore, logStore, type LogEntry } from "./lib/src/stores";
|
||||||
import { setNoticeClass } from "./lib/src/wrapper";
|
import { setNoticeClass } from "./lib/src/wrapper";
|
||||||
import { base64ToString, versionNumberString2Number, base64ToArrayBuffer, arrayBufferToBase64 } from "./lib/src/strbin";
|
import { base64ToString, versionNumberString2Number, base64ToArrayBuffer, arrayBufferToBase64 } from "./lib/src/strbin";
|
||||||
import { addPrefix, isPlainText, shouldBeIgnored, stripAllPrefixes } from "./lib/src/path";
|
import { addPrefix, isPlainText, shouldBeIgnored, stripAllPrefixes } from "./lib/src/path";
|
||||||
@@ -31,9 +31,17 @@ import { ConfigSync } from "./CmdConfigSync";
|
|||||||
import { confirmWithMessage } from "./dialogs";
|
import { confirmWithMessage } from "./dialogs";
|
||||||
import { GlobalHistoryView, VIEW_TYPE_GLOBAL_HISTORY } from "./GlobalHistoryView";
|
import { GlobalHistoryView, VIEW_TYPE_GLOBAL_HISTORY } from "./GlobalHistoryView";
|
||||||
import { LogPaneView, VIEW_TYPE_LOG } from "./LogPaneView";
|
import { LogPaneView, VIEW_TYPE_LOG } from "./LogPaneView";
|
||||||
|
import { mapAllTasksWithConcurrencyLimit, processAllTasksWithConcurrencyLimit } from "./lib/src/task";
|
||||||
|
|
||||||
setNoticeClass(Notice);
|
setNoticeClass(Notice);
|
||||||
|
|
||||||
|
// DI the log again.
|
||||||
|
setGlobalLogFunction((message: any, level?: LOG_LEVEL, key?: string) => {
|
||||||
|
const entry = { message, level, key } as LogEntry;
|
||||||
|
logStore.push(entry);
|
||||||
|
});
|
||||||
|
logStore.intercept(e => e.slice(Math.min(e.length - 200, 0)));
|
||||||
|
|
||||||
export default class ObsidianLiveSyncPlugin extends Plugin
|
export default class ObsidianLiveSyncPlugin extends Plugin
|
||||||
implements LiveSyncLocalDBEnv, LiveSyncReplicatorEnv {
|
implements LiveSyncLocalDBEnv, LiveSyncReplicatorEnv {
|
||||||
|
|
||||||
@@ -1710,21 +1718,27 @@ Or if you are sure know what had been happened, we can unlock the database from
|
|||||||
const runAll = async<T>(procedureName: string, objects: T[], callback: (arg: T) => Promise<void>) => {
|
const runAll = async<T>(procedureName: string, objects: T[], callback: (arg: T) => Promise<void>) => {
|
||||||
Logger(procedureName);
|
Logger(procedureName);
|
||||||
if (!this.localDatabase.isReady) throw Error("Database is not ready!");
|
if (!this.localDatabase.isReady) throw Error("Database is not ready!");
|
||||||
const para = Parallels();
|
const procs = objects.map(e => async () => {
|
||||||
for (const v of objects) {
|
try {
|
||||||
await para.wait(10);
|
await callback(e);
|
||||||
para.add((async (v) => {
|
return true;
|
||||||
try {
|
} catch (ex) {
|
||||||
await callback(v);
|
Logger(`Error while ${procedureName}`, LOG_LEVEL.NOTICE);
|
||||||
} catch (ex) {
|
Logger(ex, LOG_LEVEL.VERBOSE);
|
||||||
Logger(`Error while ${procedureName}`, LOG_LEVEL.NOTICE);
|
return false;
|
||||||
Logger(ex);
|
}
|
||||||
}
|
|
||||||
})(v));
|
|
||||||
|
|
||||||
|
});
|
||||||
|
let success = 0;
|
||||||
|
let failed = 0;
|
||||||
|
for await (const v of processAllTasksWithConcurrencyLimit(10, procs)) {
|
||||||
|
if ("ok" in v && v.ok) {
|
||||||
|
success++;
|
||||||
|
} else {
|
||||||
|
failed++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await para.all();
|
Logger(`${procedureName}: PASS:${success}, FAILED:${failed}`);
|
||||||
Logger(`${procedureName} done.`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await runAll("UPDATE DATABASE", onlyInStorage, async (e) => {
|
await runAll("UPDATE DATABASE", onlyInStorage, async (e) => {
|
||||||
@@ -1748,22 +1762,19 @@ Or if you are sure know what had been happened, we can unlock the database from
|
|||||||
if (!initialScan) {
|
if (!initialScan) {
|
||||||
let caches: { [key: string]: { storageMtime: number; docMtime: number } } = {};
|
let caches: { [key: string]: { storageMtime: number; docMtime: number } } = {};
|
||||||
caches = await this.kvDB.get<{ [key: string]: { storageMtime: number; docMtime: number } }>("diff-caches") || {};
|
caches = await this.kvDB.get<{ [key: string]: { storageMtime: number; docMtime: number } }>("diff-caches") || {};
|
||||||
const docsCount = syncFiles.length;
|
|
||||||
do {
|
|
||||||
const syncFilesXSrc = syncFiles.splice(0, 100);
|
|
||||||
const syncFilesX = [] as { file: TFile, id: DocumentID }[];
|
|
||||||
for (const file of syncFilesXSrc) {
|
|
||||||
const id = await this.path2id(getPathFromTFile(file));
|
|
||||||
syncFilesX.push({ file: file, id: id });
|
|
||||||
}
|
|
||||||
const docs = await this.localDatabase.allDocsRaw<EntryDoc>({ keys: syncFilesX.map(e => e.id), include_docs: true })
|
|
||||||
const docsMap = docs.rows.reduce((p, c) => ({ ...p, [c.id]: c.doc }), {} as Record<DocumentID, EntryDoc>)
|
|
||||||
|
|
||||||
const syncFilesToSync = syncFilesX.map((e) => ({ file: e.file, doc: docsMap[e.id] as LoadedEntry }));
|
const syncFilesBatch = [...arrayToChunkedArray(syncFiles, 100)];
|
||||||
await runAll(`CHECK FILE STATUS:${syncFiles.length}/${docsCount}`, syncFilesToSync, async (e) => {
|
const processes = syncFilesBatch.map((files, idx, total) => async () => {
|
||||||
|
const dbEntries = await mapAllTasksWithConcurrencyLimit(10, files.map(file => async () => ({ file: file, id: await this.path2id(getPathFromTFile(file)) })));
|
||||||
|
const dbEntriesOk = dbEntries.map(e => "ok" in e ? e.ok : undefined).filter(e => e);
|
||||||
|
const docs = await this.localDatabase.allDocsRaw<EntryDoc>({ keys: dbEntriesOk.map(e => e.id), include_docs: true });
|
||||||
|
const docsMap = docs.rows.reduce((p, c) => ({ ...p, [c.id]: c.doc }), {} as Record<DocumentID, EntryDoc>);
|
||||||
|
const syncFilesToSync = dbEntriesOk.map((e) => ({ file: e.file, doc: docsMap[e.id] as LoadedEntry }));
|
||||||
|
await runAll(`CHECK FILE STATUS:${idx + 1}/${total.length}`, syncFilesToSync, async (e) => {
|
||||||
caches = await this.syncFileBetweenDBandStorage(e.file, e.doc, initialScan, caches);
|
caches = await this.syncFileBetweenDBandStorage(e.file, e.doc, initialScan, caches);
|
||||||
});
|
});
|
||||||
} while (syncFiles.length > 0);
|
})
|
||||||
|
await mapAllTasksWithConcurrencyLimit(2, processes);
|
||||||
await this.kvDB.set("diff-caches", caches);
|
await this.kvDB.set("diff-caches", caches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,8 @@
|
|||||||
"ES5",
|
"ES5",
|
||||||
"ES6",
|
"ES6",
|
||||||
"ES7",
|
"ES7",
|
||||||
"es2019.array"
|
"es2019.array",
|
||||||
|
"ES2020.BigInt",
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
|||||||
59
updates.md
59
updates.md
@@ -14,50 +14,7 @@ I hope you will give it a try.
|
|||||||
|
|
||||||
#### Minors
|
#### Minors
|
||||||
|
|
||||||
- 0.19.1
|
- 0.19.1 to 0.19.6 has been moved into the updates_old.md
|
||||||
- Fixed: Fixed hidden file handling on Linux
|
|
||||||
- Improved: Now customization sync works more smoothly.
|
|
||||||
- 0.19.2
|
|
||||||
- Fixed:
|
|
||||||
- Fixed garbage collection error while unreferenced chunks exist many.
|
|
||||||
- Fixed filename validation on Linux.
|
|
||||||
- Improved:
|
|
||||||
- Showing status is now thinned for performance.
|
|
||||||
- Enhance caching while collecting chunks.
|
|
||||||
- 0.19.3
|
|
||||||
- Improved:
|
|
||||||
- Now replication will be paced by collecting chunks. If synchronisation has been deadlocked, please enable `Do not pace synchronization` once.
|
|
||||||
- 0.19.4
|
|
||||||
- Improved:
|
|
||||||
- Reduced remote database checking to improve speed and reduce bandwidth.
|
|
||||||
- Fixed:
|
|
||||||
- Chunks which previously misinterpreted are now interpreted correctly.
|
|
||||||
- No more missing chunks which not be found forever, except if it has been actually missing.
|
|
||||||
- Deleted file detection on hidden file synchronising now works fine.
|
|
||||||
- Now the Customisation sync is surely quiet while it has been disabled.
|
|
||||||
- 0.19.5
|
|
||||||
- Fixed:
|
|
||||||
- Now hidden file synchronisation would not be hanged, even if so many files exist.
|
|
||||||
- Improved:
|
|
||||||
- Customisation sync works more smoothly.
|
|
||||||
- Note: Concurrent processing has been rollbacked into the original implementation. As a result, the total number of processes is no longer shown next to the hourglass icon. However, only the processes that are running concurrently are shown.
|
|
||||||
- 0.19.6
|
|
||||||
- Fixed:
|
|
||||||
- Logging has been tweaked.
|
|
||||||
- No more too many planes and rockets.
|
|
||||||
- The batch database update now surely only works in non-live mode.
|
|
||||||
- Internal things:
|
|
||||||
- Some frameworks has been upgraded.
|
|
||||||
- Import declaration has been fixed.
|
|
||||||
- Improved:
|
|
||||||
- The plug-in now asks to enable a new adaptor, when rebuilding, if it is not enabled yet.
|
|
||||||
- The setting dialogue refined.
|
|
||||||
- Configurations for compatibilities have been moved under the hatch.
|
|
||||||
- Made it clear that disabled is the default.
|
|
||||||
- Ambiguous names configuration have been renamed.
|
|
||||||
- Items that have no meaning in the settings are no longer displayed.
|
|
||||||
- Some items have been reordered for clarity.
|
|
||||||
- Each configuration has been grouped.
|
|
||||||
- 0.19.7
|
- 0.19.7
|
||||||
- Fixed:
|
- Fixed:
|
||||||
- The initial pane of Setting dialogue is now changed to General Settings.
|
- The initial pane of Setting dialogue is now changed to General Settings.
|
||||||
@@ -70,5 +27,19 @@ I hope you will give it a try.
|
|||||||
- Log dialogue is now shown as one of tabs.
|
- Log dialogue is now shown as one of tabs.
|
||||||
- Fixed:
|
- Fixed:
|
||||||
- Some minor issues has been fixed.
|
- Some minor issues has been fixed.
|
||||||
|
- 0.19.9
|
||||||
|
- New feature (For fixing a problem):
|
||||||
|
- We can fix the database obfuscated and plain paths that have been mixed up.
|
||||||
|
- Improvements
|
||||||
|
- Customisation Sync performance has been improved.
|
||||||
|
- 0.19.10
|
||||||
|
- Fixed
|
||||||
|
- Fixed the issue about fixing the database.
|
||||||
|
- 0.19.11
|
||||||
|
- Improvements:
|
||||||
|
- Hashing ChunkID has been improved.
|
||||||
|
- Logging keeps 400 lines now.
|
||||||
|
- Refactored:
|
||||||
|
- Import statement has been fixed about types.
|
||||||
|
|
||||||
... To continue on to `updates_old.md`.
|
... To continue on to `updates_old.md`.
|
||||||
|
|||||||
@@ -1,3 +1,61 @@
|
|||||||
|
### 0.19.0
|
||||||
|
|
||||||
|
#### Customization sync
|
||||||
|
|
||||||
|
Since `Plugin and their settings` have been broken, so I tried to fix it, not just fix it, but fix it the way it should be.
|
||||||
|
|
||||||
|
Now, we have `Customization sync`.
|
||||||
|
|
||||||
|
It is a real shame that the compatibility between these features has been broken. However, this new feature is surely useful and I believe that worth getting over the pain.
|
||||||
|
We can use the new feature with the same configuration. Only the menu on the command palette has been changed. The dialog can be opened by `Show customization sync dialog`.
|
||||||
|
|
||||||
|
I hope you will give it a try.
|
||||||
|
|
||||||
|
#### Minors
|
||||||
|
- 0.19.1
|
||||||
|
- Fixed: Fixed hidden file handling on Linux
|
||||||
|
- Improved: Now customization sync works more smoothly.
|
||||||
|
- 0.19.2
|
||||||
|
- Fixed:
|
||||||
|
- Fixed garbage collection error while unreferenced chunks exist many.
|
||||||
|
- Fixed filename validation on Linux.
|
||||||
|
- Improved:
|
||||||
|
- Showing status is now thinned for performance.
|
||||||
|
- Enhance caching while collecting chunks.
|
||||||
|
- 0.19.3
|
||||||
|
- Improved:
|
||||||
|
- Now replication will be paced by collecting chunks. If synchronisation has been deadlocked, please enable `Do not pace synchronization` once.
|
||||||
|
- 0.19.4
|
||||||
|
- Improved:
|
||||||
|
- Reduced remote database checking to improve speed and reduce bandwidth.
|
||||||
|
- Fixed:
|
||||||
|
- Chunks which previously misinterpreted are now interpreted correctly.
|
||||||
|
- No more missing chunks which not be found forever, except if it has been actually missing.
|
||||||
|
- Deleted file detection on hidden file synchronising now works fine.
|
||||||
|
- Now the Customisation sync is surely quiet while it has been disabled.
|
||||||
|
- 0.19.5
|
||||||
|
- Fixed:
|
||||||
|
- Now hidden file synchronisation would not be hanged, even if so many files exist.
|
||||||
|
- Improved:
|
||||||
|
- Customisation sync works more smoothly.
|
||||||
|
- Note: Concurrent processing has been rollbacked into the original implementation. As a result, the total number of processes is no longer shown next to the hourglass icon. However, only the processes that are running concurrently are shown.
|
||||||
|
- 0.19.6
|
||||||
|
- Fixed:
|
||||||
|
- Logging has been tweaked.
|
||||||
|
- No more too many planes and rockets.
|
||||||
|
- The batch database update now surely only works in non-live mode.
|
||||||
|
- Internal things:
|
||||||
|
- Some frameworks has been upgraded.
|
||||||
|
- Import declaration has been fixed.
|
||||||
|
- Improved:
|
||||||
|
- The plug-in now asks to enable a new adaptor, when rebuilding, if it is not enabled yet.
|
||||||
|
- The setting dialogue refined.
|
||||||
|
- Configurations for compatibilities have been moved under the hatch.
|
||||||
|
- Made it clear that disabled is the default.
|
||||||
|
- Ambiguous names configuration have been renamed.
|
||||||
|
- Items that have no meaning in the settings are no longer displayed.
|
||||||
|
- Some items have been reordered for clarity.
|
||||||
|
- Each configuration has been grouped.
|
||||||
|
|
||||||
### 0.18.0
|
### 0.18.0
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user