mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-02-21 19:48:48 +00:00
chore(format): no intentional behaviour change - runs pretty
This commit is contained in:
@@ -2,7 +2,14 @@ import { TFile, Modal, App, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_pat
|
||||
import { getPathFromTFile, isValidPath } from "../../../common/utils.ts";
|
||||
import { decodeBinary, escapeStringToHTML, readString } from "../../../lib/src/string_and_binary/convert.ts";
|
||||
import ObsidianLiveSyncPlugin from "../../../main.ts";
|
||||
import { type DocumentID, type FilePathWithPrefix, type LoadedEntry, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "../../../lib/src/common/types.ts";
|
||||
import {
|
||||
type DocumentID,
|
||||
type FilePathWithPrefix,
|
||||
type LoadedEntry,
|
||||
LOG_LEVEL_INFO,
|
||||
LOG_LEVEL_NOTICE,
|
||||
LOG_LEVEL_VERBOSE,
|
||||
} from "../../../lib/src/common/types.ts";
|
||||
import { Logger } from "../../../lib/src/common/logger.ts";
|
||||
import { isErrorOfMissingDoc } from "../../../lib/src/pouchdb/utils_couchdb.ts";
|
||||
import { fireAndForget, getDocData, readContent } from "../../../lib/src/common/utils.ts";
|
||||
@@ -19,7 +26,7 @@ function isComparableText(path: string) {
|
||||
}
|
||||
function isComparableTextDecode(path: string) {
|
||||
const ext = path.split(".").splice(-1)[0].toLowerCase();
|
||||
return ["json"].includes(ext)
|
||||
return ["json"].includes(ext);
|
||||
}
|
||||
function readDocument(w: LoadedEntry) {
|
||||
if (w.data.length == 0) return "";
|
||||
@@ -54,10 +61,16 @@ export class DocumentHistoryModal extends Modal {
|
||||
currentDeleted = false;
|
||||
initialRev?: string;
|
||||
|
||||
constructor(app: App, plugin: ObsidianLiveSyncPlugin, file: TFile | FilePathWithPrefix, id?: DocumentID, revision?: string) {
|
||||
constructor(
|
||||
app: App,
|
||||
plugin: ObsidianLiveSyncPlugin,
|
||||
file: TFile | FilePathWithPrefix,
|
||||
id?: DocumentID,
|
||||
revision?: string
|
||||
) {
|
||||
super(app);
|
||||
this.plugin = plugin;
|
||||
this.file = (file instanceof TFile) ? getPathFromTFile(file) : file;
|
||||
this.file = file instanceof TFile ? getPathFromTFile(file) : file;
|
||||
this.id = id;
|
||||
this.initialRev = revision;
|
||||
if (!file && id) {
|
||||
@@ -95,7 +108,7 @@ export class DocumentHistoryModal extends Modal {
|
||||
async loadRevs(initialRev?: string) {
|
||||
if (this.revs_info.length == 0) return;
|
||||
if (initialRev) {
|
||||
const rIndex = this.revs_info.findIndex(e => e.rev == initialRev);
|
||||
const rIndex = this.revs_info.findIndex((e) => e.rev == initialRev);
|
||||
if (rIndex >= 0) {
|
||||
this.range.value = `${this.revs_info.length - 1 - rIndex}`;
|
||||
}
|
||||
@@ -163,8 +176,7 @@ export class DocumentHistoryModal extends Modal {
|
||||
} else if (isImage(this.file)) {
|
||||
const src = this.generateBlobURL("base", w1data);
|
||||
const overlay = this.generateBlobURL("overlay", readDocument(w2) as Uint8Array);
|
||||
result =
|
||||
`<div class='ls-imgdiff-wrap'>
|
||||
result = `<div class='ls-imgdiff-wrap'>
|
||||
<div class='overlay'>
|
||||
<img class='img-base' src="${src}">
|
||||
<img class='img-overlay' src='${overlay}'>
|
||||
@@ -174,14 +186,12 @@ export class DocumentHistoryModal extends Modal {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (result == undefined) {
|
||||
if (typeof w1data != "string") {
|
||||
if (isImage(this.file)) {
|
||||
const src = this.generateBlobURL("base", w1data);
|
||||
result =
|
||||
`<div class='ls-imgdiff-wrap'>
|
||||
result = `<div class='ls-imgdiff-wrap'>
|
||||
<div class='overlay'>
|
||||
<img class='img-base' src="${src}">
|
||||
</div>
|
||||
@@ -193,7 +203,8 @@ export class DocumentHistoryModal extends Modal {
|
||||
}
|
||||
}
|
||||
if (result == undefined) result = typeof w1data == "string" ? escapeStringToHTML(w1data) : "Binary file";
|
||||
this.contentView.innerHTML = (this.currentDeleted ? "(At this revision, the file has been deleted)\n" : "") + result;
|
||||
this.contentView.innerHTML =
|
||||
(this.currentDeleted ? "(At this revision, the file has been deleted)\n" : "") + result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,9 +268,9 @@ export class DocumentHistoryModal extends Modal {
|
||||
const leaf = this.plugin.app.workspace.getLeaf(false);
|
||||
await leaf.openFile(targetFile);
|
||||
} else {
|
||||
Logger("The file could not view on the editor", LOG_LEVEL_NOTICE)
|
||||
Logger("The file could not view on the editor", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
}
|
||||
};
|
||||
buttons.createEl("button", { text: "Back to this revision" }, (e) => {
|
||||
e.addClass("mod-cta");
|
||||
e.addEventListener("click", () => {
|
||||
@@ -285,9 +296,9 @@ export class DocumentHistoryModal extends Modal {
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
this.BlobURLs.forEach(value => {
|
||||
this.BlobURLs.forEach((value) => {
|
||||
console.log(value);
|
||||
if (value) URL.revokeObjectURL(value);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import {
|
||||
ItemView,
|
||||
WorkspaceLeaf
|
||||
} from "../../../deps.ts";
|
||||
import { ItemView, WorkspaceLeaf } from "../../../deps.ts";
|
||||
import GlobalHistoryComponent from "./GlobalHistory.svelte";
|
||||
import type ObsidianLiveSyncPlugin from "../../../main.ts";
|
||||
|
||||
export const VIEW_TYPE_GLOBAL_HISTORY = "global-history";
|
||||
export class GlobalHistoryView extends ItemView {
|
||||
|
||||
component?: GlobalHistoryComponent;
|
||||
plugin: ObsidianLiveSyncPlugin;
|
||||
icon = "clock";
|
||||
@@ -23,7 +19,6 @@ export class GlobalHistoryView extends ItemView {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
|
||||
getViewType() {
|
||||
return VIEW_TYPE_GLOBAL_HISTORY;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export class ConflictResolveModal extends Modal {
|
||||
if (this.pluginPickMode) {
|
||||
this.title = "Pick a version";
|
||||
this.remoteName = `Use ${remoteName || "Remote"}`;
|
||||
this.localName = "Use Local"
|
||||
this.localName = "Use Local";
|
||||
}
|
||||
// Send cancel signal for the previous merge dialogue
|
||||
// if not there, simply be ignored.
|
||||
@@ -48,7 +48,7 @@ export class ConflictResolveModal extends Modal {
|
||||
this.sendResponse(CANCELLED);
|
||||
}
|
||||
});
|
||||
}, 10)
|
||||
}, 10);
|
||||
// sendValue("close-resolve-conflict:" + this.filename, false);
|
||||
this.titleEl.setText(this.title);
|
||||
contentEl.empty();
|
||||
@@ -60,28 +60,47 @@ export class ConflictResolveModal extends Modal {
|
||||
const x1 = v[0];
|
||||
const x2 = v[1];
|
||||
if (x1 == DIFF_DELETE) {
|
||||
diff += "<span class='deleted'>" + escapeStringToHTML(x2).replace(/\n/g, "<span class='ls-mark-cr'></span>\n") + "</span>";
|
||||
diff +=
|
||||
"<span class='deleted'>" +
|
||||
escapeStringToHTML(x2).replace(/\n/g, "<span class='ls-mark-cr'></span>\n") +
|
||||
"</span>";
|
||||
} else if (x1 == DIFF_EQUAL) {
|
||||
diff += "<span class='normal'>" + escapeStringToHTML(x2).replace(/\n/g, "<span class='ls-mark-cr'></span>\n") + "</span>";
|
||||
diff +=
|
||||
"<span class='normal'>" +
|
||||
escapeStringToHTML(x2).replace(/\n/g, "<span class='ls-mark-cr'></span>\n") +
|
||||
"</span>";
|
||||
} else if (x1 == DIFF_INSERT) {
|
||||
diff += "<span class='added'>" + escapeStringToHTML(x2).replace(/\n/g, "<span class='ls-mark-cr'></span>\n") + "</span>";
|
||||
diff +=
|
||||
"<span class='added'>" +
|
||||
escapeStringToHTML(x2).replace(/\n/g, "<span class='ls-mark-cr'></span>\n") +
|
||||
"</span>";
|
||||
}
|
||||
}
|
||||
|
||||
diff = diff.replace(/\n/g, "<br>");
|
||||
div.innerHTML = diff;
|
||||
const div2 = contentEl.createDiv("");
|
||||
const date1 = new Date(this.result.left.mtime).toLocaleString() + (this.result.left.deleted ? " (Deleted)" : "");
|
||||
const date2 = new Date(this.result.right.mtime).toLocaleString() + (this.result.right.deleted ? " (Deleted)" : "");
|
||||
const date1 =
|
||||
new Date(this.result.left.mtime).toLocaleString() + (this.result.left.deleted ? " (Deleted)" : "");
|
||||
const date2 =
|
||||
new Date(this.result.right.mtime).toLocaleString() + (this.result.right.deleted ? " (Deleted)" : "");
|
||||
div2.innerHTML = `
|
||||
<span class='deleted'>A:${date1}</span><br /><span class='added'>B:${date2}</span><br>
|
||||
`;
|
||||
contentEl.createEl("button", { text: this.localName }, (e) => e.addEventListener("click", () => this.sendResponse(this.result.right.rev))).style.marginRight = "4px";
|
||||
contentEl.createEl("button", { text: this.remoteName }, (e) => e.addEventListener("click", () => this.sendResponse(this.result.left.rev))).style.marginRight = "4px";
|
||||
contentEl.createEl("button", { text: this.localName }, (e) =>
|
||||
e.addEventListener("click", () => this.sendResponse(this.result.right.rev))
|
||||
).style.marginRight = "4px";
|
||||
contentEl.createEl("button", { text: this.remoteName }, (e) =>
|
||||
e.addEventListener("click", () => this.sendResponse(this.result.left.rev))
|
||||
).style.marginRight = "4px";
|
||||
if (!this.pluginPickMode) {
|
||||
contentEl.createEl("button", { text: "Concat both" }, (e) => e.addEventListener("click", () => this.sendResponse(LEAVE_TO_SUBSEQUENT))).style.marginRight = "4px";
|
||||
contentEl.createEl("button", { text: "Concat both" }, (e) =>
|
||||
e.addEventListener("click", () => this.sendResponse(LEAVE_TO_SUBSEQUENT))
|
||||
).style.marginRight = "4px";
|
||||
}
|
||||
contentEl.createEl("button", { text: !this.pluginPickMode ? "Not now" : "Cancel" }, (e) => e.addEventListener("click", () => this.sendResponse(CANCELLED))).style.marginRight = "4px";
|
||||
contentEl.createEl("button", { text: !this.pluginPickMode ? "Not now" : "Cancel" }, (e) =>
|
||||
e.addEventListener("click", () => this.sendResponse(CANCELLED))
|
||||
).style.marginRight = "4px";
|
||||
}
|
||||
|
||||
sendResponse(result: MergeDialogResult) {
|
||||
@@ -106,4 +125,4 @@ export class ConflictResolveModal extends Modal {
|
||||
if (r === RESULT_TIMED_OUT) return CANCELLED;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import {
|
||||
ItemView,
|
||||
WorkspaceLeaf
|
||||
} from "obsidian";
|
||||
import { ItemView, WorkspaceLeaf } from "obsidian";
|
||||
import LogPaneComponent from "./LogPane.svelte";
|
||||
import type ObsidianLiveSyncPlugin from "../../../main.ts";
|
||||
export const VIEW_TYPE_LOG = "log-log";
|
||||
//Log view
|
||||
export class LogPaneView extends ItemView {
|
||||
|
||||
component?: LogPaneComponent;
|
||||
plugin: ObsidianLiveSyncPlugin;
|
||||
icon = "view-log";
|
||||
@@ -23,7 +19,6 @@ export class LogPaneView extends ItemView {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
|
||||
getViewType() {
|
||||
return VIEW_TYPE_LOG;
|
||||
}
|
||||
@@ -35,8 +30,7 @@ export class LogPaneView extends ItemView {
|
||||
async onOpen() {
|
||||
this.component = new LogPaneComponent({
|
||||
target: this.contentEl,
|
||||
props: {
|
||||
},
|
||||
props: {},
|
||||
});
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { VIEW_TYPE_GLOBAL_HISTORY, GlobalHistoryView } from "./GlobalHistory/GlobalHistoryView.ts";
|
||||
|
||||
|
||||
export class ModuleObsidianGlobalHistory extends AbstractObsidianModule implements IObsidianModule {
|
||||
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
|
||||
this.addCommand({
|
||||
id: "livesync-global-history",
|
||||
name: "Show vault history",
|
||||
callback: () => {
|
||||
this.showGlobalHistory()
|
||||
}
|
||||
})
|
||||
this.showGlobalHistory();
|
||||
},
|
||||
});
|
||||
|
||||
this.registerView(
|
||||
VIEW_TYPE_GLOBAL_HISTORY,
|
||||
(leaf) => new GlobalHistoryView(leaf, this.plugin)
|
||||
);
|
||||
this.registerView(VIEW_TYPE_GLOBAL_HISTORY, (leaf) => new GlobalHistoryView(leaf, this.plugin));
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
@@ -25,5 +19,4 @@ export class ModuleObsidianGlobalHistory extends AbstractObsidianModule implemen
|
||||
showGlobalHistory() {
|
||||
void this.core.$$showView(VIEW_TYPE_GLOBAL_HISTORY);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import { CANCELLED, LEAVE_TO_SUBSEQUENT, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, MISSING_OR_ERROR, type DocumentID, type FilePathWithPrefix, type diff_result } from "../../lib/src/common/types.ts";
|
||||
import {
|
||||
CANCELLED,
|
||||
LEAVE_TO_SUBSEQUENT,
|
||||
LOG_LEVEL_INFO,
|
||||
LOG_LEVEL_NOTICE,
|
||||
LOG_LEVEL_VERBOSE,
|
||||
MISSING_OR_ERROR,
|
||||
type DocumentID,
|
||||
type FilePathWithPrefix,
|
||||
type diff_result,
|
||||
} from "../../lib/src/common/types.ts";
|
||||
import { ConflictResolveModal } from "./InteractiveConflictResolving/ConflictResolveModal.ts";
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { displayRev, getPath, getPathWithoutPrefix } from "../../common/utils.ts";
|
||||
import { fireAndForget } from "octagonal-wheels/promises";
|
||||
|
||||
export class ModuleInteractiveConflictResolver extends AbstractObsidianModule implements IObsidianModule {
|
||||
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
this.addCommand({
|
||||
id: "livesync-conflictcheck",
|
||||
@@ -13,14 +22,14 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
callback: async () => {
|
||||
await this.pickFileForResolve();
|
||||
},
|
||||
})
|
||||
});
|
||||
this.addCommand({
|
||||
id: "livesync-all-conflictcheck",
|
||||
name: "Resolve all conflicted files",
|
||||
callback: async () => {
|
||||
await this.allConflictCheck();
|
||||
},
|
||||
})
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
@@ -50,18 +59,26 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
// Create a new file by concatenating both conflicted revisions.
|
||||
const p = conflictCheckResult.diff.map((e) => e[1]).join("");
|
||||
const delRev = testDoc._conflicts[0];
|
||||
if (!await this.core.databaseFileAccess.storeContent(filename, p)) {
|
||||
if (!(await this.core.databaseFileAccess.storeContent(filename, p))) {
|
||||
this._log(`Concatenated content cannot be stored:${filename}`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
// 2. As usual, delete the conflicted revision and if there are no conflicts, write the resolved content to the storage.
|
||||
if (await this.core.$$resolveConflictByDeletingRev(filename, delRev, "UI Concatenated") == MISSING_OR_ERROR) {
|
||||
this._log(`Concatenated saved, but cannot delete conflicted revisions: ${filename}, (${displayRev(delRev)})`, LOG_LEVEL_NOTICE);
|
||||
if (
|
||||
(await this.core.$$resolveConflictByDeletingRev(filename, delRev, "UI Concatenated")) ==
|
||||
MISSING_OR_ERROR
|
||||
) {
|
||||
this._log(
|
||||
`Concatenated saved, but cannot delete conflicted revisions: ${filename}, (${displayRev(delRev)})`,
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
return false;
|
||||
}
|
||||
} else if (typeof toDelete === "string") {
|
||||
// Select one of the conflicted revision to delete.
|
||||
if (await this.core.$$resolveConflictByDeletingRev(filename, toDelete, "UI Selected") == MISSING_OR_ERROR) {
|
||||
if (
|
||||
(await this.core.$$resolveConflictByDeletingRev(filename, toDelete, "UI Selected")) == MISSING_OR_ERROR
|
||||
) {
|
||||
this._log(`Merge: Something went wrong: ${filename}, (${toDelete})`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
@@ -83,22 +100,21 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
while (await this.pickFileForResolve());
|
||||
}
|
||||
|
||||
|
||||
async pickFileForResolve() {
|
||||
const notes: { id: DocumentID, path: FilePathWithPrefix, dispPath: string, mtime: number }[] = [];
|
||||
const notes: { id: DocumentID; path: FilePathWithPrefix; dispPath: string; mtime: number }[] = [];
|
||||
for await (const doc of this.localDatabase.findAllDocs({ conflicts: true })) {
|
||||
if (!("_conflicts" in doc)) continue;
|
||||
notes.push({ id: doc._id, path: getPath(doc), dispPath: getPathWithoutPrefix(doc), mtime: doc.mtime });
|
||||
}
|
||||
notes.sort((a, b) => b.mtime - a.mtime);
|
||||
const notesList = notes.map(e => e.dispPath);
|
||||
const notesList = notes.map((e) => e.dispPath);
|
||||
if (notesList.length == 0) {
|
||||
this._log("There are no conflicted documents", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
const target = await this.core.confirm.askSelectString("File to resolve conflict", notesList);
|
||||
if (target) {
|
||||
const targetItem = notes.find(e => e.dispPath == target)!;
|
||||
const targetItem = notes.find((e) => e.dispPath == target)!;
|
||||
await this.core.$$queueConflictCheck(targetItem.path);
|
||||
await this.core.$$waitForAllConflictProcessed();
|
||||
return true;
|
||||
@@ -107,21 +123,27 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
}
|
||||
|
||||
async $allScanStat(): Promise<boolean> {
|
||||
const notes: { path: string, mtime: number }[] = [];
|
||||
const notes: { path: string; mtime: number }[] = [];
|
||||
this._log(`Checking conflicted files`, LOG_LEVEL_VERBOSE);
|
||||
for await (const doc of this.localDatabase.findAllDocs({ conflicts: true })) {
|
||||
if (!("_conflicts" in doc)) continue;
|
||||
notes.push({ path: getPath(doc), mtime: doc.mtime });
|
||||
}
|
||||
if (notes.length > 0) {
|
||||
this.core.confirm.askInPopup(`conflicting-detected-on-safety`, `Some files have been left conflicted! Press {HERE} to resolve them, or you can do it later by "Pick a file to resolve conflict`, (anchor) => {
|
||||
anchor.text = "HERE";
|
||||
anchor.addEventListener("click", () => {
|
||||
fireAndForget(() => this.allConflictCheck())
|
||||
});
|
||||
}
|
||||
this.core.confirm.askInPopup(
|
||||
`conflicting-detected-on-safety`,
|
||||
`Some files have been left conflicted! Press {HERE} to resolve them, or you can do it later by "Pick a file to resolve conflict`,
|
||||
(anchor) => {
|
||||
anchor.text = "HERE";
|
||||
anchor.addEventListener("click", () => {
|
||||
fireAndForget(() => this.allConflictCheck());
|
||||
});
|
||||
}
|
||||
);
|
||||
this._log(
|
||||
`Some files have been left conflicted! Please resolve them by "Pick a file to resolve conflict". The list is written in the log.`,
|
||||
LOG_LEVEL_VERBOSE
|
||||
);
|
||||
this._log(`Some files have been left conflicted! Please resolve them by "Pick a file to resolve conflict". The list is written in the log.`, LOG_LEVEL_VERBOSE);
|
||||
for (const note of notes) {
|
||||
this._log(`Conflicted: ${note.path}`);
|
||||
}
|
||||
@@ -130,5 +152,4 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
import { computed, reactive, reactiveSource, type ReactiveValue } from "octagonal-wheels/dataobject/reactive";
|
||||
import { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_VERBOSE, PREFIXMD_LOGFILE, type DatabaseConnectingStatus, type LOG_LEVEL } from "../../lib/src/common/types.ts";
|
||||
import {
|
||||
LOG_LEVEL_DEBUG,
|
||||
LOG_LEVEL_INFO,
|
||||
LOG_LEVEL_VERBOSE,
|
||||
PREFIXMD_LOGFILE,
|
||||
type DatabaseConnectingStatus,
|
||||
type LOG_LEVEL,
|
||||
} from "../../lib/src/common/types.ts";
|
||||
import { cancelTask, scheduleTask } from "octagonal-wheels/concurrency/task";
|
||||
import { fireAndForget, isDirty, throttle } from "../../lib/src/common/utils.ts";
|
||||
import { collectingChunks, pluginScanningCount, hiddenFilesEventCount, hiddenFilesProcessingCount, type LogEntry, logStore, logMessages } from "../../lib/src/mock_and_interop/stores.ts";
|
||||
import {
|
||||
collectingChunks,
|
||||
pluginScanningCount,
|
||||
hiddenFilesEventCount,
|
||||
hiddenFilesProcessingCount,
|
||||
type LogEntry,
|
||||
logStore,
|
||||
logMessages,
|
||||
} from "../../lib/src/mock_and_interop/stores.ts";
|
||||
import { eventHub } from "../../lib/src/hub/hub.ts";
|
||||
import { EVENT_FILE_RENAMED, EVENT_LAYOUT_READY, EVENT_LEAF_ACTIVE_CHANGED } from "../../common/events.ts";
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
@@ -22,15 +37,16 @@ setGlobalLogFunction((message: any, level?: number, key?: string) => {
|
||||
let recentLogs = [] as string[];
|
||||
|
||||
// Recent log splicer
|
||||
const recentLogProcessor = new QueueProcessor((logs: string[]) => {
|
||||
recentLogs = [...recentLogs, ...logs].splice(-200);
|
||||
logMessages.value = recentLogs;
|
||||
}, { batchSize: 25, delay: 10, suspended: false, concurrentLimit: 1 }).resumePipeLine();
|
||||
const recentLogProcessor = new QueueProcessor(
|
||||
(logs: string[]) => {
|
||||
recentLogs = [...recentLogs, ...logs].splice(-200);
|
||||
logMessages.value = recentLogs;
|
||||
},
|
||||
{ batchSize: 25, delay: 10, suspended: false, concurrentLimit: 1 }
|
||||
).resumePipeLine();
|
||||
// logStore.intercept(e => e.slice(Math.min(e.length - 200, 0)));
|
||||
|
||||
|
||||
export class ModuleLog extends AbstractObsidianModule implements IObsidianModule {
|
||||
|
||||
registerView = this.plugin.registerView.bind(this.plugin);
|
||||
|
||||
statusBar?: HTMLElement;
|
||||
@@ -41,7 +57,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
logHistory?: HTMLDivElement;
|
||||
messageArea?: HTMLDivElement;
|
||||
|
||||
statusBarLabels!: ReactiveValue<{ message: string, status: string }>;
|
||||
statusBarLabels!: ReactiveValue<{ message: string; status: string }>;
|
||||
statusLog = reactiveSource("");
|
||||
notifies: { [key: string]: { notice: Notice; count: number } } = {};
|
||||
|
||||
@@ -52,7 +68,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
const formatted = reactiveSource("");
|
||||
let timer: ReturnType<typeof setTimeout> | undefined = undefined;
|
||||
let maxLen = 1;
|
||||
numI.onChanged(numX => {
|
||||
numI.onChanged((numX) => {
|
||||
const num = numX.value;
|
||||
const numLen = `${Math.abs(num)}`.length + 1;
|
||||
maxLen = maxLen < numLen ? numLen : maxLen;
|
||||
@@ -63,8 +79,8 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
maxLen = 1;
|
||||
}, 3000);
|
||||
}
|
||||
formatted.value = ` ${mark}${`${padSpaces}${num}`.slice(-(maxLen))}`;
|
||||
})
|
||||
formatted.value = ` ${mark}${`${padSpaces}${num}`.slice(-maxLen)}`;
|
||||
});
|
||||
return computed(() => formatted.value);
|
||||
}
|
||||
const labelReplication = padLeftSpComputed(this.core.replicationResultCount, `📥`);
|
||||
@@ -74,16 +90,16 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
const labelPluginScanCount = padLeftSpComputed(pluginScanningCount, `🔌`);
|
||||
const labelConflictProcessCount = padLeftSpComputed(this.core.conflictProcessQueueCount, `🔩`);
|
||||
const hiddenFilesCount = reactive(() => hiddenFilesEventCount.value + hiddenFilesProcessingCount.value);
|
||||
const labelHiddenFilesCount = padLeftSpComputed(hiddenFilesCount, `⚙️`)
|
||||
const labelHiddenFilesCount = padLeftSpComputed(hiddenFilesCount, `⚙️`);
|
||||
const queueCountLabelX = reactive(() => {
|
||||
return `${labelReplication()}${labelDBCount()}${labelStorageCount()}${labelChunkCount()}${labelPluginScanCount()}${labelHiddenFilesCount()}${labelConflictProcessCount()}`;
|
||||
})
|
||||
});
|
||||
const queueCountLabel = () => queueCountLabelX.value;
|
||||
|
||||
const requestingStatLabel = computed(() => {
|
||||
const diff = this.core.requestCount.value - this.core.responseCount.value;
|
||||
return diff != 0 ? "📲 " : "";
|
||||
})
|
||||
});
|
||||
|
||||
const replicationStatLabel = computed(() => {
|
||||
const e = this.core.replicationStat.value;
|
||||
@@ -97,10 +113,10 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
let pullLast = "";
|
||||
let w = "";
|
||||
const labels: Partial<Record<DatabaseConnectingStatus, string>> = {
|
||||
"CONNECTED": "⚡",
|
||||
"JOURNAL_SEND": "📦↑",
|
||||
"JOURNAL_RECEIVE": "📦↓",
|
||||
}
|
||||
CONNECTED: "⚡",
|
||||
JOURNAL_SEND: "📦↑",
|
||||
JOURNAL_RECEIVE: "📦↓",
|
||||
};
|
||||
switch (e.syncStatus) {
|
||||
case "CLOSED":
|
||||
case "COMPLETED":
|
||||
@@ -117,8 +133,18 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
case "JOURNAL_SEND":
|
||||
case "JOURNAL_RECEIVE":
|
||||
w = labels[e.syncStatus] || "⚡";
|
||||
pushLast = ((lastSyncPushSeq == 0) ? "" : (lastSyncPushSeq >= maxPushSeq ? " (LIVE)" : ` (${maxPushSeq - lastSyncPushSeq})`));
|
||||
pullLast = ((lastSyncPullSeq == 0) ? "" : (lastSyncPullSeq >= maxPullSeq ? " (LIVE)" : ` (${maxPullSeq - lastSyncPullSeq})`));
|
||||
pushLast =
|
||||
lastSyncPushSeq == 0
|
||||
? ""
|
||||
: lastSyncPushSeq >= maxPushSeq
|
||||
? " (LIVE)"
|
||||
: ` (${maxPushSeq - lastSyncPushSeq})`;
|
||||
pullLast =
|
||||
lastSyncPullSeq == 0
|
||||
? ""
|
||||
: lastSyncPullSeq >= maxPullSeq
|
||||
? " (LIVE)"
|
||||
: ` (${maxPullSeq - lastSyncPullSeq})`;
|
||||
break;
|
||||
case "ERRORED":
|
||||
w = "⚠";
|
||||
@@ -127,13 +153,13 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
w = "?";
|
||||
}
|
||||
return { w, sent, pushLast, arrived, pullLast };
|
||||
})
|
||||
});
|
||||
const labelProc = padLeftSpComputed(this.core.processing, `⏳`);
|
||||
const labelPend = padLeftSpComputed(this.core.totalQueued, `🛫`);
|
||||
const labelInBatchDelay = padLeftSpComputed(this.core.batched, `📬`);
|
||||
const waitingLabel = computed(() => {
|
||||
return `${labelProc()}${labelPend()}${labelInBatchDelay()}`;
|
||||
})
|
||||
});
|
||||
const statusLineLabel = computed(() => {
|
||||
const { w, sent, pushLast, arrived, pullLast } = replicationStatLabel();
|
||||
const queued = queueCountLabel();
|
||||
@@ -142,24 +168,26 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
return {
|
||||
message: `${networkActivity}Sync: ${w} ↑ ${sent}${pushLast} ↓ ${arrived}${pullLast}${waiting}${queued}`,
|
||||
};
|
||||
})
|
||||
});
|
||||
const statusBarLabels = reactive(() => {
|
||||
const scheduleMessage = this.core.$$isReloadingScheduled() ? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n` : "";
|
||||
const scheduleMessage = this.core.$$isReloadingScheduled()
|
||||
? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n`
|
||||
: "";
|
||||
const { message } = statusLineLabel();
|
||||
const status = scheduleMessage + this.statusLog.value;
|
||||
|
||||
return {
|
||||
message, status
|
||||
}
|
||||
})
|
||||
message,
|
||||
status,
|
||||
};
|
||||
});
|
||||
this.statusBarLabels = statusBarLabels;
|
||||
|
||||
const applyToDisplay = throttle((label: typeof statusBarLabels.value) => {
|
||||
// const v = label;
|
||||
this.applyStatusBarText();
|
||||
|
||||
}, 20);
|
||||
statusBarLabels.onChanged(label => applyToDisplay(label.value))
|
||||
statusBarLabels.onChanged((label) => applyToDisplay(label.value));
|
||||
}
|
||||
|
||||
$everyOnload(): Promise<boolean> {
|
||||
@@ -182,10 +210,13 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
if (!thisFile) return "";
|
||||
// Case Sensitivity
|
||||
if (this.core.$$shouldCheckCaseInsensitive()) {
|
||||
const f = this.core.storageAccess.getFiles().map(e => e.path).filter(e => e.toLowerCase() == thisFile.path.toLowerCase());
|
||||
const f = this.core.storageAccess
|
||||
.getFiles()
|
||||
.map((e) => e.path)
|
||||
.filter((e) => e.toLowerCase() == thisFile.path.toLowerCase());
|
||||
if (f.length > 1) return "Not synchronised: There are multiple files with the same name";
|
||||
}
|
||||
if (!await this.core.$$isTargetFile(thisFile.path)) return "Not synchronised: not a target file";
|
||||
if (!(await this.core.$$isTargetFile(thisFile.path))) return "Not synchronised: not a target file";
|
||||
if (this.core.$$isFileSizeExceeded(thisFile.stat.size)) return "Not synchronised: File size exceeded";
|
||||
return "";
|
||||
}
|
||||
@@ -197,11 +228,10 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
this.adjustStatusDivPosition();
|
||||
await this.setFileStatus();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
nextFrameQueue: ReturnType<typeof requestAnimationFrame> | undefined = undefined;
|
||||
logLines: { ttl: number, message: string }[] = [];
|
||||
logLines: { ttl: number; message: string }[] = [];
|
||||
|
||||
applyStatusBarText() {
|
||||
if (this.nextFrameQueue) {
|
||||
@@ -222,10 +252,13 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
if (this.settings?.showStatusOnEditor && this.statusDiv) {
|
||||
if (this.settings.showLongerLogInsideEditor) {
|
||||
const now = new Date().getTime();
|
||||
this.logLines = this.logLines.filter(e => e.ttl > now);
|
||||
const minimumNext = this.logLines.reduce((a, b) => a < b.ttl ? a : b.ttl, Number.MAX_SAFE_INTEGER);
|
||||
this.logLines = this.logLines.filter((e) => e.ttl > now);
|
||||
const minimumNext = this.logLines.reduce(
|
||||
(a, b) => (a < b.ttl ? a : b.ttl),
|
||||
Number.MAX_SAFE_INTEGER
|
||||
);
|
||||
if (this.logLines.length > 0) setTimeout(() => this.applyStatusBarText(), minimumNext - now);
|
||||
const recent = this.logLines.map(e => e.message);
|
||||
const recent = this.logLines.map((e) => e.message);
|
||||
const recentLogs = recent.reverse().join("\n");
|
||||
if (isDirty("recentLogs", recentLogs)) this.logHistory!.innerText = recentLogs;
|
||||
}
|
||||
@@ -237,14 +270,16 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
}
|
||||
});
|
||||
|
||||
scheduleTask("log-hide", 3000, () => { this.statusLog.value = "" });
|
||||
scheduleTask("log-hide", 3000, () => {
|
||||
this.statusLog.value = "";
|
||||
});
|
||||
}
|
||||
|
||||
$allStartOnUnload(): Promise<boolean> {
|
||||
if (this.statusDiv) {
|
||||
this.statusDiv.remove();
|
||||
}
|
||||
document.querySelectorAll(`.livesync-status`)?.forEach(e => e.remove());
|
||||
document.querySelectorAll(`.livesync-status`)?.forEach((e) => e.remove());
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
@@ -264,22 +299,28 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
name: "Show log",
|
||||
callback: () => {
|
||||
void this.core.$$showView(VIEW_TYPE_LOG);
|
||||
}
|
||||
},
|
||||
});
|
||||
this.registerView(
|
||||
VIEW_TYPE_LOG,
|
||||
(leaf) => new LogPaneView(leaf, this.plugin)
|
||||
);
|
||||
this.registerView(VIEW_TYPE_LOG, (leaf) => new LogPaneView(leaf, this.plugin));
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
$everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||
logStore.pipeTo(new QueueProcessor(logs => logs.forEach(e => this.core.$$addLog(e.message, e.level, e.key)), { suspended: false, batchSize: 20, concurrentLimit: 1, delay: 0 })).startPipeline();
|
||||
logStore
|
||||
.pipeTo(
|
||||
new QueueProcessor((logs) => logs.forEach((e) => this.core.$$addLog(e.message, e.level, e.key)), {
|
||||
suspended: false,
|
||||
batchSize: 20,
|
||||
concurrentLimit: 1,
|
||||
delay: 0,
|
||||
})
|
||||
)
|
||||
.startPipeline();
|
||||
eventHub.onEvent(EVENT_FILE_RENAMED, (data) => {
|
||||
void this.setFileStatus();
|
||||
});
|
||||
|
||||
const w = document.querySelectorAll(`.livesync-status`);
|
||||
w.forEach(e => e.remove());
|
||||
w.forEach((e) => e.remove());
|
||||
|
||||
this.observeForLogs();
|
||||
|
||||
@@ -298,15 +339,20 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
}
|
||||
|
||||
writeLogToTheFile(now: Date, vaultName: string, newMessage: string) {
|
||||
fireAndForget(() => serialized("writeLog", async () => {
|
||||
const time = now.toISOString().split("T")[0];
|
||||
const logDate = `${PREFIXMD_LOGFILE}${time}.md`;
|
||||
const file = await this.core.storageAccess.isExists(normalizePath(logDate));
|
||||
if (!file) {
|
||||
await this.core.storageAccess.appendHiddenFile(normalizePath(logDate), "```\n");
|
||||
}
|
||||
await this.core.storageAccess.appendHiddenFile(normalizePath(logDate), vaultName + ":" + newMessage + "\n");
|
||||
}));
|
||||
fireAndForget(() =>
|
||||
serialized("writeLog", async () => {
|
||||
const time = now.toISOString().split("T")[0];
|
||||
const logDate = `${PREFIXMD_LOGFILE}${time}.md`;
|
||||
const file = await this.core.storageAccess.isExists(normalizePath(logDate));
|
||||
if (!file) {
|
||||
await this.core.storageAccess.appendHiddenFile(normalizePath(logDate), "```\n");
|
||||
}
|
||||
await this.core.storageAccess.appendHiddenFile(
|
||||
normalizePath(logDate),
|
||||
vaultName + ":" + newMessage + "\n"
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
$$addLog(message: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key = ""): void {
|
||||
if (level == LOG_LEVEL_DEBUG) {
|
||||
@@ -321,7 +367,12 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
const vaultName = this.core.$$getVaultName();
|
||||
const now = new Date();
|
||||
const timestamp = now.toLocaleString();
|
||||
const messageContent = typeof message == "string" ? message : message instanceof Error ? `${message.name}:${message.message}` : JSON.stringify(message, null, 2);
|
||||
const messageContent =
|
||||
typeof message == "string"
|
||||
? message
|
||||
: message instanceof Error
|
||||
? `${message.name}:${message.message}`
|
||||
: JSON.stringify(message, null, 2);
|
||||
if (message instanceof Error) {
|
||||
// debugger;
|
||||
console.dir(message.stack);
|
||||
@@ -342,7 +393,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
if (!key) key = messageContent;
|
||||
if (key in this.notifies) {
|
||||
// @ts-ignore
|
||||
const isShown = this.notifies[key].notice.noticeEl?.isShown()
|
||||
const isShown = this.notifies[key].notice.noticeEl?.isShown();
|
||||
if (!isShown) {
|
||||
this.notifies[key].notice = new Notice(messageContent, 0);
|
||||
}
|
||||
@@ -369,9 +420,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
} catch {
|
||||
// NO OP
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,20 +7,15 @@ import { DocumentHistoryModal } from "./DocumentHistory/DocumentHistoryModal.ts"
|
||||
import { getPath } from "../../common/utils.ts";
|
||||
import { fireAndForget } from "octagonal-wheels/promises";
|
||||
|
||||
|
||||
|
||||
export class ModuleObsidianDocumentHistory extends AbstractObsidianModule implements IObsidianModule {
|
||||
|
||||
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
|
||||
this.addCommand({
|
||||
id: "livesync-history",
|
||||
name: "Show history",
|
||||
callback: () => {
|
||||
const file = this.core.$$getActiveFilePath();
|
||||
if (file) this.showHistory(file, undefined);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
@@ -30,9 +25,12 @@ export class ModuleObsidianDocumentHistory extends AbstractObsidianModule implem
|
||||
fireAndForget(async () => await this.fileHistory());
|
||||
},
|
||||
});
|
||||
eventHub.onEvent(EVENT_REQUEST_SHOW_HISTORY, ({ file, fileOnDB }: { file: TFile | FilePathWithPrefix, fileOnDB: LoadedEntry }) => {
|
||||
this.showHistory(file, fileOnDB._id);
|
||||
})
|
||||
eventHub.onEvent(
|
||||
EVENT_REQUEST_SHOW_HISTORY,
|
||||
({ file, fileOnDB }: { file: TFile | FilePathWithPrefix; fileOnDB: LoadedEntry }) => {
|
||||
this.showHistory(file, fileOnDB._id);
|
||||
}
|
||||
);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
@@ -41,17 +39,16 @@ export class ModuleObsidianDocumentHistory extends AbstractObsidianModule implem
|
||||
}
|
||||
|
||||
async fileHistory() {
|
||||
const notes: { id: DocumentID, path: FilePathWithPrefix, dispPath: string, mtime: number }[] = [];
|
||||
const notes: { id: DocumentID; path: FilePathWithPrefix; dispPath: string; mtime: number }[] = [];
|
||||
for await (const doc of this.localDatabase.findAllDocs()) {
|
||||
notes.push({ id: doc._id, path: getPath(doc), dispPath: getPath(doc), mtime: doc.mtime });
|
||||
}
|
||||
notes.sort((a, b) => b.mtime - a.mtime);
|
||||
const notesList = notes.map(e => e.dispPath);
|
||||
const notesList = notes.map((e) => e.dispPath);
|
||||
const target = await this.core.confirm.askSelectString("File to view History", notesList);
|
||||
if (target) {
|
||||
const targetId = notes.find(e => e.dispPath == target)!;
|
||||
const targetId = notes.find((e) => e.dispPath == target)!;
|
||||
this.showHistory(targetId.path, targetId.id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
import { type IObsidianModule, AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
// import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
|
||||
import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events";
|
||||
import { type BucketSyncSetting, type ConfigPassphraseStore, type CouchDBConnection, DEFAULT_SETTINGS, type ObsidianLiveSyncSettings, SALT_OF_PASSPHRASE } from "../../lib/src/common/types";
|
||||
import {
|
||||
type BucketSyncSetting,
|
||||
type ConfigPassphraseStore,
|
||||
type CouchDBConnection,
|
||||
DEFAULT_SETTINGS,
|
||||
type ObsidianLiveSyncSettings,
|
||||
SALT_OF_PASSPHRASE,
|
||||
} from "../../lib/src/common/types";
|
||||
import { LOG_LEVEL_NOTICE, LOG_LEVEL_URGENT } from "octagonal-wheels/common/logger";
|
||||
import { encrypt, tryDecrypt } from "octagonal-wheels/encryption";
|
||||
import { setLang } from "../../lib/src/common/i18n";
|
||||
import { isCloudantURI } from "../../lib/src/pouchdb/utils_couchdb";
|
||||
export class ModuleObsidianSettings extends AbstractObsidianModule implements IObsidianModule {
|
||||
|
||||
getPassphrase(settings: ObsidianLiveSyncSettings) {
|
||||
const methods: Record<ConfigPassphraseStore, (() => Promise<string | false>)> = {
|
||||
const methods: Record<ConfigPassphraseStore, () => Promise<string | false>> = {
|
||||
"": () => Promise.resolve("*"),
|
||||
"LOCALSTORAGE": () => Promise.resolve(localStorage.getItem("ls-setting-passphrase") ?? false),
|
||||
"ASK_AT_LAUNCH": () => this.core.confirm.askString("Passphrase", "passphrase", "")
|
||||
}
|
||||
LOCALSTORAGE: () => Promise.resolve(localStorage.getItem("ls-setting-passphrase") ?? false),
|
||||
ASK_AT_LAUNCH: () => this.core.confirm.askString("Passphrase", "passphrase", ""),
|
||||
};
|
||||
const method = settings.configPassphraseStore;
|
||||
const methodFunc = method in methods ? methods[method] : methods[""];
|
||||
return methodFunc();
|
||||
@@ -29,7 +35,6 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
this.usedPassphrase = "";
|
||||
}
|
||||
|
||||
|
||||
async decryptConfigurationItem(encrypted: string, passphrase: string) {
|
||||
const dec = await tryDecrypt(encrypted, passphrase + SALT_OF_PASSPHRASE, false);
|
||||
if (dec) {
|
||||
@@ -39,7 +44,6 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
async encryptConfigurationItem(src: string, settings: ObsidianLiveSyncSettings) {
|
||||
if (this.usedPassphrase != "") {
|
||||
return await encrypt(src, this.usedPassphrase + SALT_OF_PASSPHRASE, false);
|
||||
@@ -47,7 +51,10 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
|
||||
const passphrase = await this.getPassphrase(settings);
|
||||
if (passphrase === false) {
|
||||
this._log("Could not determine passphrase to save data.json! You probably make the configuration sure again!", LOG_LEVEL_URGENT);
|
||||
this._log(
|
||||
"Could not determine passphrase to save data.json! You probably make the configuration sure again!",
|
||||
LOG_LEVEL_URGENT
|
||||
);
|
||||
return "";
|
||||
}
|
||||
const dec = await encrypt(src, passphrase + SALT_OF_PASSPHRASE, false);
|
||||
@@ -60,17 +67,25 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
}
|
||||
|
||||
get appId() {
|
||||
return `${("appId" in this.app ? this.app.appId : "")}`;
|
||||
return `${"appId" in this.app ? this.app.appId : ""}`;
|
||||
}
|
||||
|
||||
async $$saveSettingData() {
|
||||
this.core.$$saveDeviceAndVaultName();
|
||||
const settings = { ...this.settings };
|
||||
settings.deviceAndVaultName = "";
|
||||
if (this.usedPassphrase == "" && !await this.getPassphrase(settings)) {
|
||||
this._log("Could not determine passphrase for saving data.json! Our data.json have insecure items!", LOG_LEVEL_NOTICE);
|
||||
if (this.usedPassphrase == "" && !(await this.getPassphrase(settings))) {
|
||||
this._log(
|
||||
"Could not determine passphrase for saving data.json! Our data.json have insecure items!",
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
} else {
|
||||
if (settings.couchDB_PASSWORD != "" || settings.couchDB_URI != "" || settings.couchDB_USER != "" || settings.couchDB_DBNAME) {
|
||||
if (
|
||||
settings.couchDB_PASSWORD != "" ||
|
||||
settings.couchDB_URI != "" ||
|
||||
settings.couchDB_USER != "" ||
|
||||
settings.couchDB_DBNAME
|
||||
) {
|
||||
const connectionSetting: CouchDBConnection & BucketSyncSetting = {
|
||||
couchDB_DBNAME: settings.couchDB_DBNAME,
|
||||
couchDB_PASSWORD: settings.couchDB_PASSWORD,
|
||||
@@ -81,9 +96,12 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
endpoint: settings.endpoint,
|
||||
region: settings.region,
|
||||
secretKey: settings.secretKey,
|
||||
useCustomRequestHandler: settings.useCustomRequestHandler
|
||||
useCustomRequestHandler: settings.useCustomRequestHandler,
|
||||
};
|
||||
settings.encryptedCouchDBConnection = await this.encryptConfigurationItem(JSON.stringify(connectionSetting), settings);
|
||||
settings.encryptedCouchDBConnection = await this.encryptConfigurationItem(
|
||||
JSON.stringify(connectionSetting),
|
||||
settings
|
||||
);
|
||||
settings.couchDB_PASSWORD = "";
|
||||
settings.couchDB_DBNAME = "";
|
||||
settings.couchDB_URI = "";
|
||||
@@ -98,7 +116,6 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
settings.encryptedPassphrase = await this.encryptConfigurationItem(settings.passphrase, settings);
|
||||
settings.passphrase = "";
|
||||
}
|
||||
|
||||
}
|
||||
await this.core.saveData(settings);
|
||||
eventHub.emitEvent(EVENT_SETTING_SAVED, settings);
|
||||
@@ -127,7 +144,10 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
}
|
||||
const passphrase = await this.getPassphrase(settings);
|
||||
if (passphrase === false) {
|
||||
this._log("Could not determine passphrase for reading data.json! DO NOT synchronize with the remote before making sure your configuration is!", LOG_LEVEL_URGENT);
|
||||
this._log(
|
||||
"Could not determine passphrase for reading data.json! DO NOT synchronize with the remote before making sure your configuration is!",
|
||||
LOG_LEVEL_URGENT
|
||||
);
|
||||
} else {
|
||||
if (settings.encryptedCouchDBConnection) {
|
||||
const keys = [
|
||||
@@ -139,17 +159,23 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
"bucket",
|
||||
"endpoint",
|
||||
"region",
|
||||
"secretKey"] as (keyof CouchDBConnection | keyof BucketSyncSetting)[];
|
||||
const decrypted = this.tryDecodeJson(await this.decryptConfigurationItem(settings.encryptedCouchDBConnection, passphrase)) as (CouchDBConnection & BucketSyncSetting);
|
||||
"secretKey",
|
||||
] as (keyof CouchDBConnection | keyof BucketSyncSetting)[];
|
||||
const decrypted = this.tryDecodeJson(
|
||||
await this.decryptConfigurationItem(settings.encryptedCouchDBConnection, passphrase)
|
||||
) as CouchDBConnection & BucketSyncSetting;
|
||||
if (decrypted) {
|
||||
for (const key of keys) {
|
||||
if (key in decrypted) {
|
||||
//@ts-ignore
|
||||
settings[key] = decrypted[key]
|
||||
settings[key] = decrypted[key];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._log("Could not decrypt passphrase for reading data.json! DO NOT synchronize with the remote before making sure your configuration is!", LOG_LEVEL_URGENT);
|
||||
this._log(
|
||||
"Could not decrypt passphrase for reading data.json! DO NOT synchronize with the remote before making sure your configuration is!",
|
||||
LOG_LEVEL_URGENT
|
||||
);
|
||||
for (const key of keys) {
|
||||
//@ts-ignore
|
||||
settings[key] = "";
|
||||
@@ -162,11 +188,13 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
if (decrypted) {
|
||||
settings.passphrase = decrypted;
|
||||
} else {
|
||||
this._log("Could not decrypt passphrase for reading data.json! DO NOT synchronize with the remote before making sure your configuration is!", LOG_LEVEL_URGENT);
|
||||
this._log(
|
||||
"Could not decrypt passphrase for reading data.json! DO NOT synchronize with the remote before making sure your configuration is!",
|
||||
LOG_LEVEL_URGENT
|
||||
);
|
||||
settings.passphrase = "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
this.settings = settings;
|
||||
setLang(this.settings.displayLanguage);
|
||||
@@ -191,7 +219,10 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
}
|
||||
}
|
||||
if (isCloudantURI(this.settings.couchDB_URI) && this.settings.customChunkSize != 0) {
|
||||
this._log("Configuration verification founds problems with your configuration. This has been fixed automatically. But you may already have data that cannot be synchronised. If this is the case, please rebuild everything.", LOG_LEVEL_NOTICE)
|
||||
this._log(
|
||||
"Configuration verification founds problems with your configuration. This has been fixed automatically. But you may already have data that cannot be synchronised. If this is the case, please rebuild everything.",
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
this.settings.customChunkSize = 0;
|
||||
}
|
||||
this.core.$$setDeviceAndVaultName(localStorage.getItem(lsKey) || "");
|
||||
@@ -204,4 +235,4 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
// this.core.ignoreFiles = this.settings.ignoreFiles.split(",").map(e => e.trim());
|
||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE }
|
||||
const SETTING_HEADER = "````yaml:livesync-setting\n";
|
||||
const SETTING_FOOTER = "\n````";
|
||||
export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule implements IObsidianModule {
|
||||
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
this.addCommand({
|
||||
id: "livesync-export-config",
|
||||
@@ -21,8 +20,8 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
||||
fireAndForget(async () => {
|
||||
await this.core.$$saveSettingData();
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
this.addCommand({
|
||||
id: "livesync-import-config",
|
||||
name: "Parse setting file",
|
||||
@@ -33,32 +32,33 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
||||
return ret.body != "";
|
||||
}
|
||||
if (ctx.file) {
|
||||
const file = ctx.file
|
||||
const file = ctx.file;
|
||||
fireAndForget(async () => await this.checkAndApplySettingFromMarkdown(file.path, false));
|
||||
}
|
||||
},
|
||||
})
|
||||
eventHub.onEvent("event-file-changed", (info: {
|
||||
file: FilePathWithPrefix, automated: boolean
|
||||
}) => {
|
||||
});
|
||||
eventHub.onEvent("event-file-changed", (info: { file: FilePathWithPrefix; automated: boolean }) => {
|
||||
fireAndForget(() => this.checkAndApplySettingFromMarkdown(info.file, info.automated));
|
||||
});
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
||||
if (settings.settingSyncFile != "") {
|
||||
fireAndForget(() => this.saveSettingToMarkdown(settings.settingSyncFile));
|
||||
}
|
||||
})
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
|
||||
extractSettingFromWholeText(data: string): {
|
||||
preamble: string, body: string, postscript: string
|
||||
preamble: string;
|
||||
body: string;
|
||||
postscript: string;
|
||||
} {
|
||||
if (data.indexOf(SETTING_HEADER) === -1) {
|
||||
return {
|
||||
preamble: data, body: "", postscript: ""
|
||||
}
|
||||
preamble: data,
|
||||
body: "",
|
||||
postscript: "",
|
||||
};
|
||||
}
|
||||
const startMarkerPos = data.indexOf(SETTING_HEADER);
|
||||
const dataStartPos = startMarkerPos == -1 ? data.length : startMarkerPos;
|
||||
@@ -68,27 +68,33 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
||||
const ret = {
|
||||
preamble: data.substring(0, dataStartPos),
|
||||
body,
|
||||
postscript: data.substring(dataEndPos + SETTING_FOOTER.length + 1)
|
||||
}
|
||||
postscript: data.substring(dataEndPos + SETTING_FOOTER.length + 1),
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
async parseSettingFromMarkdown(filename: string, data?: string) {
|
||||
const file = await this.core.storageAccess.isExists(filename);
|
||||
if (!file) return {
|
||||
preamble: "", body: "", postscript: "",
|
||||
};
|
||||
if (!file)
|
||||
return {
|
||||
preamble: "",
|
||||
body: "",
|
||||
postscript: "",
|
||||
};
|
||||
if (data) {
|
||||
return this.extractSettingFromWholeText(data);
|
||||
}
|
||||
const parseData = data ?? await this.core.storageAccess.readFileText(filename);
|
||||
const parseData = data ?? (await this.core.storageAccess.readFileText(filename));
|
||||
return this.extractSettingFromWholeText(parseData);
|
||||
}
|
||||
|
||||
async checkAndApplySettingFromMarkdown(filename: string, automated?: boolean) {
|
||||
if (automated && !this.settings.notifyAllSettingSyncFile) {
|
||||
if (!this.settings.settingSyncFile || this.settings.settingSyncFile != filename) {
|
||||
this._log(`Setting file (${filename}) is not matched to the current configuration. skipped.`, LOG_LEVEL_DEBUG);
|
||||
this._log(
|
||||
`Setting file (${filename}) is not matched to the current configuration. skipped.`,
|
||||
LOG_LEVEL_DEBUG
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -103,61 +109,80 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
||||
}
|
||||
|
||||
if ("settingSyncFile" in newSetting && newSetting.settingSyncFile != filename) {
|
||||
this._log("This setting file seems to backed up one. Please fix the filename or settingSyncFile value.", automated ? LOG_LEVEL_INFO : LOG_LEVEL_NOTICE);
|
||||
this._log(
|
||||
"This setting file seems to backed up one. Please fix the filename or settingSyncFile value.",
|
||||
automated ? LOG_LEVEL_INFO : LOG_LEVEL_NOTICE
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let settingToApply = { ...DEFAULT_SETTINGS } as ObsidianLiveSyncSettings;
|
||||
settingToApply = { ...settingToApply, ...newSetting }
|
||||
if (!(settingToApply?.writeCredentialsForSettingSync)) {
|
||||
//New setting does not contains credentials.
|
||||
settingToApply = { ...settingToApply, ...newSetting };
|
||||
if (!settingToApply?.writeCredentialsForSettingSync) {
|
||||
//New setting does not contains credentials.
|
||||
settingToApply.couchDB_USER = this.settings.couchDB_USER;
|
||||
settingToApply.couchDB_PASSWORD = this.settings.couchDB_PASSWORD;
|
||||
settingToApply.passphrase = this.settings.passphrase;
|
||||
}
|
||||
const oldSetting = this.generateSettingForMarkdown(this.settings, settingToApply.writeCredentialsForSettingSync);
|
||||
const oldSetting = this.generateSettingForMarkdown(
|
||||
this.settings,
|
||||
settingToApply.writeCredentialsForSettingSync
|
||||
);
|
||||
if (!isObjectDifferent(oldSetting, this.generateSettingForMarkdown(settingToApply))) {
|
||||
this._log("Setting markdown has been detected, but not changed.", automated ? LOG_LEVEL_INFO : LOG_LEVEL_NOTICE);
|
||||
return
|
||||
this._log(
|
||||
"Setting markdown has been detected, but not changed.",
|
||||
automated ? LOG_LEVEL_INFO : LOG_LEVEL_NOTICE
|
||||
);
|
||||
return;
|
||||
}
|
||||
const addMsg = this.settings.settingSyncFile != filename ? " (This is not-active file)" : "";
|
||||
this.core.confirm.askInPopup("apply-setting-from-md", `Setting markdown ${filename}${addMsg} has been detected. Apply this from {HERE}.`, (anchor) => {
|
||||
anchor.text = "HERE";
|
||||
anchor.addEventListener("click", () => {
|
||||
fireAndForget(async () => {
|
||||
const APPLY_ONLY = "Apply settings";
|
||||
const APPLY_AND_RESTART = "Apply settings and restart obsidian";
|
||||
const APPLY_AND_REBUILD = "Apply settings and restart obsidian with red_flag_rebuild.md";
|
||||
const APPLY_AND_FETCH = "Apply settings and restart obsidian with red_flag_fetch.md";
|
||||
const CANCEL = "Cancel";
|
||||
const result = await this.core.confirm.askSelectStringDialogue("Ready for apply the setting.", [
|
||||
APPLY_AND_RESTART,
|
||||
APPLY_ONLY,
|
||||
APPLY_AND_FETCH,
|
||||
APPLY_AND_REBUILD,
|
||||
CANCEL], { defaultAction: APPLY_AND_RESTART });
|
||||
if (result == APPLY_ONLY || result == APPLY_AND_RESTART || result == APPLY_AND_REBUILD || result == APPLY_AND_FETCH) {
|
||||
this.core.settings = settingToApply;
|
||||
await this.core.$$saveSettingData();
|
||||
if (result == APPLY_ONLY) {
|
||||
this._log("Loaded settings have been applied!", LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
this.core.confirm.askInPopup(
|
||||
"apply-setting-from-md",
|
||||
`Setting markdown ${filename}${addMsg} has been detected. Apply this from {HERE}.`,
|
||||
(anchor) => {
|
||||
anchor.text = "HERE";
|
||||
anchor.addEventListener("click", () => {
|
||||
fireAndForget(async () => {
|
||||
const APPLY_ONLY = "Apply settings";
|
||||
const APPLY_AND_RESTART = "Apply settings and restart obsidian";
|
||||
const APPLY_AND_REBUILD = "Apply settings and restart obsidian with red_flag_rebuild.md";
|
||||
const APPLY_AND_FETCH = "Apply settings and restart obsidian with red_flag_fetch.md";
|
||||
const CANCEL = "Cancel";
|
||||
const result = await this.core.confirm.askSelectStringDialogue(
|
||||
"Ready for apply the setting.",
|
||||
[APPLY_AND_RESTART, APPLY_ONLY, APPLY_AND_FETCH, APPLY_AND_REBUILD, CANCEL],
|
||||
{ defaultAction: APPLY_AND_RESTART }
|
||||
);
|
||||
if (
|
||||
result == APPLY_ONLY ||
|
||||
result == APPLY_AND_RESTART ||
|
||||
result == APPLY_AND_REBUILD ||
|
||||
result == APPLY_AND_FETCH
|
||||
) {
|
||||
this.core.settings = settingToApply;
|
||||
await this.core.$$saveSettingData();
|
||||
if (result == APPLY_ONLY) {
|
||||
this._log("Loaded settings have been applied!", LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
}
|
||||
if (result == APPLY_AND_REBUILD) {
|
||||
await this.core.rebuilder.scheduleRebuild();
|
||||
}
|
||||
if (result == APPLY_AND_FETCH) {
|
||||
await this.core.rebuilder.scheduleFetch();
|
||||
}
|
||||
this.core.$$performRestart();
|
||||
}
|
||||
if (result == APPLY_AND_REBUILD) {
|
||||
await this.core.rebuilder.scheduleRebuild();
|
||||
}
|
||||
if (result == APPLY_AND_FETCH) {
|
||||
await this.core.rebuilder.scheduleFetch();
|
||||
}
|
||||
this.core.$$performRestart();
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
generateSettingForMarkdown(settings?: ObsidianLiveSyncSettings, keepCredential?: boolean): Partial<ObsidianLiveSyncSettings> {
|
||||
generateSettingForMarkdown(
|
||||
settings?: ObsidianLiveSyncSettings,
|
||||
keepCredential?: boolean
|
||||
): Partial<ObsidianLiveSyncSettings> {
|
||||
const saveData = { ...(settings ? settings : this.settings) } as Partial<ObsidianLiveSyncSettings>;
|
||||
delete saveData.encryptedCouchDBConnection;
|
||||
delete saveData.encryptedPassphrase;
|
||||
@@ -174,7 +199,6 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
||||
const saveData = this.generateSettingForMarkdown();
|
||||
const file = await this.core.storageAccess.isExists(filename);
|
||||
|
||||
|
||||
if (!file) {
|
||||
await this.core.storageAccess.ensureDir(filename);
|
||||
const initialContent = `This file contains Self-hosted LiveSync settings as YAML.
|
||||
@@ -188,8 +212,11 @@ We can perform a command in this file.
|
||||
**Note** Please handle it with all of your care if you have configured to write credentials in.
|
||||
|
||||
|
||||
`
|
||||
await this.core.storageAccess.writeFileAuto(filename, initialContent + SETTING_HEADER + "\n" + SETTING_FOOTER);
|
||||
`;
|
||||
await this.core.storageAccess.writeFileAuto(
|
||||
filename,
|
||||
initialContent + SETTING_HEADER + "\n" + SETTING_FOOTER
|
||||
);
|
||||
}
|
||||
// if (!(file instanceof TFile)) {
|
||||
// this._log(`Markdown Setting: ${filename} already exists as a folder`, LOG_LEVEL_NOTICE);
|
||||
@@ -203,9 +230,11 @@ We can perform a command in this file.
|
||||
if (newBody == body) {
|
||||
this._log("Markdown setting: Nothing had been changed", LOG_LEVEL_VERBOSE);
|
||||
} else {
|
||||
await this.core.storageAccess.writeFileAuto(filename, preamble + SETTING_HEADER + newBody + SETTING_FOOTER + postscript);
|
||||
await this.core.storageAccess.writeFileAuto(
|
||||
filename,
|
||||
preamble + SETTING_HEADER + newBody + SETTING_FOOTER + postscript
|
||||
);
|
||||
this._log(`Markdown setting: ${filename} has been updated!`, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { type IObsidianModule, AbstractObsidianModule } from "../AbstractObsidia
|
||||
import { EVENT_REQUEST_OPEN_SETTING_WIZARD, EVENT_REQUEST_OPEN_SETTINGS, eventHub } from "../../common/events.ts";
|
||||
|
||||
export class ModuleObsidianSettingDialogue extends AbstractObsidianModule implements IObsidianModule {
|
||||
|
||||
settingTab!: ObsidianLiveSyncSettingTab;
|
||||
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
@@ -28,8 +27,6 @@ export class ModuleObsidianSettingDialogue extends AbstractObsidianModule implem
|
||||
}
|
||||
|
||||
get appId() {
|
||||
return `${("appId" in this.app ? this.app.appId : "")}`;
|
||||
return `${"appId" in this.app ? this.app.appId : ""}`;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { type ObsidianLiveSyncSettings, DEFAULT_SETTINGS, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "../../lib/src/common/types.ts";
|
||||
import {
|
||||
type ObsidianLiveSyncSettings,
|
||||
DEFAULT_SETTINGS,
|
||||
LOG_LEVEL_NOTICE,
|
||||
LOG_LEVEL_VERBOSE,
|
||||
} from "../../lib/src/common/types.ts";
|
||||
import { configURIBase } from "../../common/types.ts";
|
||||
// import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.js";
|
||||
import { decrypt, encrypt } from "../../lib/src/encryption/e2ee_v2.ts";
|
||||
@@ -6,10 +11,12 @@ import { fireAndForget } from "../../lib/src/common/utils.ts";
|
||||
import { EVENT_REQUEST_COPY_SETUP_URI, EVENT_REQUEST_OPEN_SETUP_URI, eventHub } from "../../common/events.ts";
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
|
||||
|
||||
export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsidianModule {
|
||||
$everyOnload(): Promise<boolean> {
|
||||
this.registerObsidianProtocolHandler("setuplivesync", async (conf: any) => await this.setupWizard(conf.settings));
|
||||
this.registerObsidianProtocolHandler(
|
||||
"setuplivesync",
|
||||
async (conf: any) => await this.setupWizard(conf.settings)
|
||||
);
|
||||
|
||||
this.addCommand({
|
||||
id: "livesync-copysetupuri",
|
||||
@@ -39,30 +46,55 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
}
|
||||
|
||||
async command_copySetupURI(stripExtra = true) {
|
||||
const encryptingPassphrase = await this.core.confirm.askString("Encrypt your settings", "The passphrase to encrypt the setup URI", "", true);
|
||||
if (encryptingPassphrase === false)
|
||||
return;
|
||||
const setting = { ...this.settings, configPassphraseStore: "", encryptedCouchDBConnection: "", encryptedPassphrase: "" } as Partial<ObsidianLiveSyncSettings>;
|
||||
const encryptingPassphrase = await this.core.confirm.askString(
|
||||
"Encrypt your settings",
|
||||
"The passphrase to encrypt the setup URI",
|
||||
"",
|
||||
true
|
||||
);
|
||||
if (encryptingPassphrase === false) return;
|
||||
const setting = {
|
||||
...this.settings,
|
||||
configPassphraseStore: "",
|
||||
encryptedCouchDBConnection: "",
|
||||
encryptedPassphrase: "",
|
||||
} as Partial<ObsidianLiveSyncSettings>;
|
||||
if (stripExtra) {
|
||||
delete setting.pluginSyncExtendedSetting;
|
||||
}
|
||||
const keys = Object.keys(setting) as (keyof ObsidianLiveSyncSettings)[];
|
||||
for (const k of keys) {
|
||||
if (JSON.stringify(k in setting ? setting[k] : "") == JSON.stringify(k in DEFAULT_SETTINGS ? DEFAULT_SETTINGS[k] : "*")) {
|
||||
if (
|
||||
JSON.stringify(k in setting ? setting[k] : "") ==
|
||||
JSON.stringify(k in DEFAULT_SETTINGS ? DEFAULT_SETTINGS[k] : "*")
|
||||
) {
|
||||
delete setting[k];
|
||||
}
|
||||
}
|
||||
const encryptedSetting = encodeURIComponent(await encrypt(JSON.stringify(setting), encryptingPassphrase, false));
|
||||
const encryptedSetting = encodeURIComponent(
|
||||
await encrypt(JSON.stringify(setting), encryptingPassphrase, false)
|
||||
);
|
||||
const uri = `${configURIBase}${encryptedSetting}`;
|
||||
await navigator.clipboard.writeText(uri);
|
||||
this._log("Setup URI copied to clipboard", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
async command_copySetupURIFull() {
|
||||
const encryptingPassphrase = await this.core.confirm.askString("Encrypt your settings", "The passphrase to encrypt the setup URI", "", true);
|
||||
if (encryptingPassphrase === false)
|
||||
return;
|
||||
const setting = { ...this.settings, configPassphraseStore: "", encryptedCouchDBConnection: "", encryptedPassphrase: "" };
|
||||
const encryptedSetting = encodeURIComponent(await encrypt(JSON.stringify(setting), encryptingPassphrase, false));
|
||||
const encryptingPassphrase = await this.core.confirm.askString(
|
||||
"Encrypt your settings",
|
||||
"The passphrase to encrypt the setup URI",
|
||||
"",
|
||||
true
|
||||
);
|
||||
if (encryptingPassphrase === false) return;
|
||||
const setting = {
|
||||
...this.settings,
|
||||
configPassphraseStore: "",
|
||||
encryptedCouchDBConnection: "",
|
||||
encryptedPassphrase: "",
|
||||
};
|
||||
const encryptedSetting = encodeURIComponent(
|
||||
await encrypt(JSON.stringify(setting), encryptingPassphrase, false)
|
||||
);
|
||||
const uri = `${configURIBase}${encryptedSetting}`;
|
||||
await navigator.clipboard.writeText(uri);
|
||||
this._log("Setup URI copied to clipboard", LOG_LEVEL_NOTICE);
|
||||
@@ -72,8 +104,7 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
}
|
||||
async command_openSetupURI() {
|
||||
const setupURI = await this.core.confirm.askString("Easy setup", "Set up URI", `${configURIBase}aaaaa`);
|
||||
if (setupURI === false)
|
||||
return;
|
||||
if (setupURI === false) return;
|
||||
if (!setupURI.startsWith(`${configURIBase}`)) {
|
||||
this._log("Set up URI looks wrong.", LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
@@ -85,12 +116,19 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
async setupWizard(confString: string) {
|
||||
try {
|
||||
const oldConf = JSON.parse(JSON.stringify(this.settings));
|
||||
const encryptingPassphrase = await this.core.confirm.askString("Passphrase", "The passphrase to decrypt your setup URI", "", true);
|
||||
if (encryptingPassphrase === false)
|
||||
return;
|
||||
const encryptingPassphrase = await this.core.confirm.askString(
|
||||
"Passphrase",
|
||||
"The passphrase to decrypt your setup URI",
|
||||
"",
|
||||
true
|
||||
);
|
||||
if (encryptingPassphrase === false) return;
|
||||
const newConf = await JSON.parse(await decrypt(confString, encryptingPassphrase, false));
|
||||
if (newConf) {
|
||||
const result = await this.core.confirm.askYesNoDialog("Importing Configuration from the Setup-URI. Are you sure to proceed?", {});
|
||||
const result = await this.core.confirm.askYesNoDialog(
|
||||
"Importing Configuration from the Setup-URI. Are you sure to proceed?",
|
||||
{}
|
||||
);
|
||||
if (result == "yes") {
|
||||
const newSettingW = Object.assign({}, DEFAULT_SETTINGS, newConf) as ObsidianLiveSyncSettings;
|
||||
this.core.replicator.closeReplication();
|
||||
@@ -100,7 +138,7 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
newSettingW.configPassphraseStore = "";
|
||||
newSettingW.encryptedPassphrase = "";
|
||||
newSettingW.encryptedCouchDBConnection = "";
|
||||
newSettingW.additionalSuffixOfDatabaseName = `${("appId" in this.app ? this.app.appId : "")}`
|
||||
newSettingW.additionalSuffixOfDatabaseName = `${"appId" in this.app ? this.app.appId : ""}`;
|
||||
const setupJustImport = "Just import setting";
|
||||
const setupAsNew = "Set it up as secondary or subsequent device";
|
||||
const setupAsMerge = "Secondary device but try keeping local changes";
|
||||
@@ -114,7 +152,11 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
newSettingW.useIndexedDBAdapter = true;
|
||||
}
|
||||
|
||||
const setupType = await this.core.confirm.askSelectStringDialogue("How would you like to set it up?", [setupAsNew, setupAgain, setupAsMerge, setupJustImport, setupManually], { defaultAction: setupAsNew });
|
||||
const setupType = await this.core.confirm.askSelectStringDialogue(
|
||||
"How would you like to set it up?",
|
||||
[setupAsNew, setupAgain, setupAsMerge, setupJustImport, setupManually],
|
||||
{ defaultAction: setupAsNew }
|
||||
);
|
||||
if (setupType == setupJustImport) {
|
||||
this.core.settings = newSettingW;
|
||||
this.core.$$clearUsedPassphrase();
|
||||
@@ -128,16 +170,27 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
this.core.$$clearUsedPassphrase();
|
||||
await this.core.rebuilder.$fetchLocal(true);
|
||||
} else if (setupType == setupAgain) {
|
||||
const confirm = "I know this operation will rebuild all my databases with files on this device, and files that are on the remote database and I didn't synchronize to any other devices will be lost and want to proceed indeed.";
|
||||
if (await this.core.confirm.askSelectStringDialogue("Do you really want to do this?", ["Cancel", confirm], { defaultAction: "Cancel" }) != confirm) {
|
||||
const confirm =
|
||||
"I know this operation will rebuild all my databases with files on this device, and files that are on the remote database and I didn't synchronize to any other devices will be lost and want to proceed indeed.";
|
||||
if (
|
||||
(await this.core.confirm.askSelectStringDialogue(
|
||||
"Do you really want to do this?",
|
||||
["Cancel", confirm],
|
||||
{ defaultAction: "Cancel" }
|
||||
)) != confirm
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.core.settings = newSettingW;
|
||||
this.core.$$clearUsedPassphrase();
|
||||
await this.core.rebuilder.$rebuildEverything();
|
||||
} else if (setupType == setupManually) {
|
||||
const keepLocalDB = await this.core.confirm.askYesNoDialog("Keep local DB?", { defaultOption: "No" });
|
||||
const keepRemoteDB = await this.core.confirm.askYesNoDialog("Keep remote DB?", { defaultOption: "No" });
|
||||
const keepLocalDB = await this.core.confirm.askYesNoDialog("Keep local DB?", {
|
||||
defaultOption: "No",
|
||||
});
|
||||
const keepRemoteDB = await this.core.confirm.askYesNoDialog("Keep remote DB?", {
|
||||
defaultOption: "No",
|
||||
});
|
||||
if (keepLocalDB == "yes" && keepRemoteDB == "yes") {
|
||||
// nothing to do. so peaceful.
|
||||
this.core.settings = newSettingW;
|
||||
@@ -145,7 +198,9 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
await this.core.$allSuspendAllSync();
|
||||
await this.core.$allSuspendExtraSync();
|
||||
await this.core.saveSettings();
|
||||
const replicate = await this.core.confirm.askYesNoDialog("Unlock and replicate?", { defaultOption: "Yes" });
|
||||
const replicate = await this.core.confirm.askYesNoDialog("Unlock and replicate?", {
|
||||
defaultOption: "Yes",
|
||||
});
|
||||
if (replicate == "yes") {
|
||||
await this.core.$$replicate(true);
|
||||
await this.core.$$markRemoteUnlocked();
|
||||
@@ -154,7 +209,9 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
return;
|
||||
}
|
||||
if (keepLocalDB == "no" && keepRemoteDB == "no") {
|
||||
const reset = await this.core.confirm.askYesNoDialog("Drop everything?", { defaultOption: "No" });
|
||||
const reset = await this.core.confirm.askYesNoDialog("Drop everything?", {
|
||||
defaultOption: "No",
|
||||
});
|
||||
if (reset != "yes") {
|
||||
this._log("Cancelled", LOG_LEVEL_NOTICE);
|
||||
this.core.settings = oldConf;
|
||||
@@ -168,7 +225,9 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
if (keepLocalDB == "no") {
|
||||
await this.core.$$resetLocalDatabase();
|
||||
await this.core.localDatabase.initializeDatabase();
|
||||
const rebuild = await this.core.confirm.askYesNoDialog("Rebuild the database?", { defaultOption: "Yes" });
|
||||
const rebuild = await this.core.confirm.askYesNoDialog("Rebuild the database?", {
|
||||
defaultOption: "Yes",
|
||||
});
|
||||
if (rebuild == "yes") {
|
||||
initDB = this.core.$$initializeDatabase(true);
|
||||
} else {
|
||||
@@ -180,7 +239,9 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
await this.core.$$markRemoteLocked();
|
||||
}
|
||||
if (keepLocalDB == "no" || keepRemoteDB == "no") {
|
||||
const replicate = await this.core.confirm.askYesNoDialog("Replicate once?", { defaultOption: "Yes" });
|
||||
const replicate = await this.core.confirm.askYesNoDialog("Replicate once?", {
|
||||
defaultOption: "Yes",
|
||||
});
|
||||
if (replicate == "yes") {
|
||||
if (initDB != null) {
|
||||
await initDB;
|
||||
@@ -200,6 +261,4 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
this._log(ex, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,35 @@
|
||||
import { Setting, TextComponent, type ToggleComponent, type DropdownComponent, ButtonComponent, type TextAreaComponent, type ValueComponent } from "obsidian";
|
||||
import {
|
||||
Setting,
|
||||
TextComponent,
|
||||
type ToggleComponent,
|
||||
type DropdownComponent,
|
||||
ButtonComponent,
|
||||
type TextAreaComponent,
|
||||
type ValueComponent,
|
||||
} from "obsidian";
|
||||
import { unique } from "octagonal-wheels/collection";
|
||||
import { LEVEL_ADVANCED, LEVEL_POWER_USER, statusDisplay, type ConfigurationItem } from "../../../lib/src/common/types.ts";
|
||||
import { type ObsidianLiveSyncSettingTab, type AutoWireOption, wrapMemo, type OnUpdateResult, createStub, findAttrFromParent } from "./ObsidianLiveSyncSettingTab.ts";
|
||||
import { type AllSettingItemKey, getConfig, type AllSettings, type AllStringItemKey, type AllNumericItemKey, type AllBooleanItemKey } from "./settingConstants.ts";
|
||||
|
||||
import {
|
||||
LEVEL_ADVANCED,
|
||||
LEVEL_POWER_USER,
|
||||
statusDisplay,
|
||||
type ConfigurationItem,
|
||||
} from "../../../lib/src/common/types.ts";
|
||||
import {
|
||||
type ObsidianLiveSyncSettingTab,
|
||||
type AutoWireOption,
|
||||
wrapMemo,
|
||||
type OnUpdateResult,
|
||||
createStub,
|
||||
findAttrFromParent,
|
||||
} from "./ObsidianLiveSyncSettingTab.ts";
|
||||
import {
|
||||
type AllSettingItemKey,
|
||||
getConfig,
|
||||
type AllSettings,
|
||||
type AllStringItemKey,
|
||||
type AllNumericItemKey,
|
||||
type AllBooleanItemKey,
|
||||
} from "./settingConstants.ts";
|
||||
|
||||
export class LiveSyncSetting extends Setting {
|
||||
autoWiredComponent?: TextComponent | ToggleComponent | DropdownComponent | ButtonComponent | TextAreaComponent;
|
||||
@@ -29,8 +55,9 @@ export class LiveSyncSetting extends Setting {
|
||||
DEV: {
|
||||
const paneName = findAttrFromParent(this.settingEl, "data-pane");
|
||||
const panelName = findAttrFromParent(this.settingEl, "data-panel");
|
||||
const itemName = typeof this.nameBuf == "string" ? this.nameBuf : this.nameBuf.textContent?.toString() ?? "";
|
||||
const strValue = typeof value == "string" ? value : value.textContent?.toString() ?? "";
|
||||
const itemName =
|
||||
typeof this.nameBuf == "string" ? this.nameBuf : (this.nameBuf.textContent?.toString() ?? "");
|
||||
const strValue = typeof value == "string" ? value : (value.textContent?.toString() ?? "");
|
||||
|
||||
createStub(itemName, key, strValue, panelName, paneName);
|
||||
}
|
||||
@@ -111,9 +138,11 @@ export class LiveSyncSetting extends Setting {
|
||||
}
|
||||
autoWireText(key: AllStringItemKey, opt?: AutoWireOption) {
|
||||
const conf = this.autoWireSetting(key, opt);
|
||||
this.addText(text => {
|
||||
this.addText((text) => {
|
||||
this.autoWiredComponent = text;
|
||||
const setValue = wrapMemo((value: string) => { text.setValue(value) });
|
||||
const setValue = wrapMemo((value: string) => {
|
||||
text.setValue(value);
|
||||
});
|
||||
this.invalidateValue = () => setValue(`${LiveSyncSetting.env.editingSettings[key]}`);
|
||||
this.invalidateValue();
|
||||
text.onChange(async (value) => {
|
||||
@@ -129,9 +158,11 @@ export class LiveSyncSetting extends Setting {
|
||||
}
|
||||
autoWireTextArea(key: AllStringItemKey, opt?: AutoWireOption) {
|
||||
const conf = this.autoWireSetting(key, opt);
|
||||
this.addTextArea(text => {
|
||||
this.addTextArea((text) => {
|
||||
this.autoWiredComponent = text;
|
||||
const setValue = wrapMemo((value: string) => { text.setValue(value) });
|
||||
const setValue = wrapMemo((value: string) => {
|
||||
text.setValue(value);
|
||||
});
|
||||
this.invalidateValue = () => setValue(`${LiveSyncSetting.env.editingSettings[key]}`);
|
||||
this.invalidateValue();
|
||||
text.onChange(async (value) => {
|
||||
@@ -145,9 +176,12 @@ export class LiveSyncSetting extends Setting {
|
||||
});
|
||||
return this;
|
||||
}
|
||||
autoWireNumeric(key: AllNumericItemKey, opt: AutoWireOption & { clampMin?: number; clampMax?: number; acceptZero?: boolean; }) {
|
||||
autoWireNumeric(
|
||||
key: AllNumericItemKey,
|
||||
opt: AutoWireOption & { clampMin?: number; clampMax?: number; acceptZero?: boolean }
|
||||
) {
|
||||
const conf = this.autoWireSetting(key, opt);
|
||||
this.addText(text => {
|
||||
this.addText((text) => {
|
||||
this.autoWiredComponent = text;
|
||||
if (opt.clampMin) {
|
||||
text.inputEl.setAttribute("min", `${opt.clampMin}`);
|
||||
@@ -156,7 +190,9 @@ export class LiveSyncSetting extends Setting {
|
||||
text.inputEl.setAttribute("max", `${opt.clampMax}`);
|
||||
}
|
||||
let lastError = false;
|
||||
const setValue = wrapMemo((value: string) => { text.setValue(value) });
|
||||
const setValue = wrapMemo((value: string) => {
|
||||
text.setValue(value);
|
||||
});
|
||||
this.invalidateValue = () => {
|
||||
if (!lastError) setValue(`${LiveSyncSetting.env.editingSettings[key]}`);
|
||||
};
|
||||
@@ -193,9 +229,11 @@ export class LiveSyncSetting extends Setting {
|
||||
}
|
||||
autoWireToggle(key: AllBooleanItemKey, opt?: AutoWireOption) {
|
||||
const conf = this.autoWireSetting(key, opt);
|
||||
this.addToggle(toggle => {
|
||||
this.addToggle((toggle) => {
|
||||
this.autoWiredComponent = toggle;
|
||||
const setValue = wrapMemo((value: boolean) => { toggle.setValue(opt?.invert ? !value : value) });
|
||||
const setValue = wrapMemo((value: boolean) => {
|
||||
toggle.setValue(opt?.invert ? !value : value);
|
||||
});
|
||||
this.invalidateValue = () => setValue(LiveSyncSetting.env.editingSettings[key] ?? false);
|
||||
this.invalidateValue();
|
||||
|
||||
@@ -207,16 +245,15 @@ export class LiveSyncSetting extends Setting {
|
||||
});
|
||||
return this;
|
||||
}
|
||||
autoWireDropDown<T extends string>(key: AllStringItemKey, opt: AutoWireOption & { options: Record<T, string>; }) {
|
||||
autoWireDropDown<T extends string>(key: AllStringItemKey, opt: AutoWireOption & { options: Record<T, string> }) {
|
||||
const conf = this.autoWireSetting(key, opt);
|
||||
this.addDropdown(dropdown => {
|
||||
this.addDropdown((dropdown) => {
|
||||
this.autoWiredComponent = dropdown;
|
||||
const setValue = wrapMemo((value: string) => {
|
||||
dropdown.setValue(value);
|
||||
});
|
||||
|
||||
dropdown
|
||||
.addOptions(opt.options);
|
||||
dropdown.addOptions(opt.options);
|
||||
|
||||
this.invalidateValue = () => setValue(LiveSyncSetting.env.editingSettings[key] || "");
|
||||
this.invalidateValue();
|
||||
@@ -264,7 +301,6 @@ export class LiveSyncSetting extends Setting {
|
||||
const newConf = this._getComputedStatus();
|
||||
const keys = Object.keys(newConf) as [keyof OnUpdateResult];
|
||||
for (const k of keys) {
|
||||
|
||||
if (k in this.prevStatus && this.prevStatus[k] == newConf[k]) {
|
||||
continue;
|
||||
}
|
||||
@@ -277,8 +313,8 @@ export class LiveSyncSetting extends Setting {
|
||||
case "classes":
|
||||
break;
|
||||
case "disabled":
|
||||
this.setDisabled((newConf[k] || false));
|
||||
this.settingEl.toggleClass("sls-setting-disabled", (newConf[k] || false));
|
||||
this.setDisabled(newConf[k] || false);
|
||||
this.settingEl.toggleClass("sls-setting-disabled", newConf[k] || false);
|
||||
this.prevStatus[k] = newConf[k];
|
||||
break;
|
||||
case "isCta":
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,21 @@
|
||||
import { $t } from "../../../lib/src/common/i18n.ts";
|
||||
import { DEFAULT_SETTINGS, configurationNames, type ConfigurationItem, type FilterBooleanKeys, type FilterNumberKeys, type FilterStringKeys, type ObsidianLiveSyncSettings } from "../../../lib/src/common/types.ts";
|
||||
import {
|
||||
DEFAULT_SETTINGS,
|
||||
configurationNames,
|
||||
type ConfigurationItem,
|
||||
type FilterBooleanKeys,
|
||||
type FilterNumberKeys,
|
||||
type FilterStringKeys,
|
||||
type ObsidianLiveSyncSettings,
|
||||
} from "../../../lib/src/common/types.ts";
|
||||
|
||||
export type OnDialogSettings = {
|
||||
configPassphrase: string,
|
||||
preset: "" | "PERIODIC" | "LIVESYNC" | "DISABLE",
|
||||
syncMode: "ONEVENTS" | "PERIODIC" | "LIVESYNC"
|
||||
dummy: number,
|
||||
deviceAndVaultName: string,
|
||||
}
|
||||
configPassphrase: string;
|
||||
preset: "" | "PERIODIC" | "LIVESYNC" | "DISABLE";
|
||||
syncMode: "ONEVENTS" | "PERIODIC" | "LIVESYNC";
|
||||
dummy: number;
|
||||
deviceAndVaultName: string;
|
||||
};
|
||||
|
||||
export const OnDialogSettingsDefault: OnDialogSettings = {
|
||||
configPassphrase: "",
|
||||
@@ -15,9 +23,8 @@ export const OnDialogSettingsDefault: OnDialogSettings = {
|
||||
syncMode: "ONEVENTS",
|
||||
dummy: 0,
|
||||
deviceAndVaultName: "",
|
||||
}
|
||||
export const AllSettingDefault =
|
||||
{ ...DEFAULT_SETTINGS, ...OnDialogSettingsDefault }
|
||||
};
|
||||
export const AllSettingDefault = { ...DEFAULT_SETTINGS, ...OnDialogSettingsDefault };
|
||||
|
||||
export type AllSettings = ObsidianLiveSyncSettings & OnDialogSettings;
|
||||
export type AllStringItemKey = FilterStringKeys<AllSettings>;
|
||||
@@ -25,324 +32,326 @@ export type AllNumericItemKey = FilterNumberKeys<AllSettings>;
|
||||
export type AllBooleanItemKey = FilterBooleanKeys<AllSettings>;
|
||||
export type AllSettingItemKey = AllStringItemKey | AllNumericItemKey | AllBooleanItemKey;
|
||||
|
||||
export type ValueOf<T extends AllSettingItemKey> =
|
||||
T extends AllStringItemKey ? string :
|
||||
T extends AllNumericItemKey ? number :
|
||||
T extends AllBooleanItemKey ? boolean :
|
||||
AllSettings[T];
|
||||
export type ValueOf<T extends AllSettingItemKey> = T extends AllStringItemKey
|
||||
? string
|
||||
: T extends AllNumericItemKey
|
||||
? number
|
||||
: T extends AllBooleanItemKey
|
||||
? boolean
|
||||
: AllSettings[T];
|
||||
|
||||
export const SettingInformation: Partial<Record<keyof AllSettings, ConfigurationItem>> = {
|
||||
"liveSync": {
|
||||
"name": "Sync Mode"
|
||||
},
|
||||
"couchDB_URI": {
|
||||
"name": "URI",
|
||||
"placeHolder": "https://........"
|
||||
},
|
||||
"couchDB_USER": {
|
||||
"name": "Username",
|
||||
"desc": "username"
|
||||
},
|
||||
"couchDB_PASSWORD": {
|
||||
"name": "Password",
|
||||
"desc": "password"
|
||||
},
|
||||
"couchDB_DBNAME": {
|
||||
"name": "Database name"
|
||||
},
|
||||
"passphrase": {
|
||||
"name": "Passphrase",
|
||||
"desc": "Encrypting passphrase. If you change the passphrase of an existing database, overwriting the remote database is strongly recommended."
|
||||
},
|
||||
"showStatusOnEditor": {
|
||||
"name": "Show status inside the editor",
|
||||
"desc": "Reflected after reboot"
|
||||
},
|
||||
"showOnlyIconsOnEditor": {
|
||||
"name": "Show status as icons only"
|
||||
},
|
||||
"showStatusOnStatusbar": {
|
||||
"name": "Show status on the status bar",
|
||||
"desc": "Reflected after reboot."
|
||||
},
|
||||
"lessInformationInLog": {
|
||||
"name": "Show only notifications",
|
||||
"desc": "Prevent logging and show only notification. Please disable when you report the logs"
|
||||
},
|
||||
"showVerboseLog": {
|
||||
"name": "Verbose Log",
|
||||
"desc": "Show verbose log. Please enable when you report the logs"
|
||||
},
|
||||
"hashCacheMaxCount": {
|
||||
"name": "Memory cache size (by total items)"
|
||||
},
|
||||
"hashCacheMaxAmount": {
|
||||
"name": "Memory cache size (by total characters)",
|
||||
"desc": "(Mega chars)"
|
||||
},
|
||||
"writeCredentialsForSettingSync": {
|
||||
"name": "Write credentials in the file",
|
||||
"desc": "(Not recommended) If set, credentials will be stored in the file."
|
||||
},
|
||||
"notifyAllSettingSyncFile": {
|
||||
"name": "Notify all setting files"
|
||||
},
|
||||
"configPassphrase": {
|
||||
"name": "Passphrase of sensitive configuration items",
|
||||
"desc": "This passphrase will not be copied to another device. It will be set to `Default` until you configure it again."
|
||||
},
|
||||
"configPassphraseStore": {
|
||||
"name": "Encrypting sensitive configuration items"
|
||||
},
|
||||
"syncOnSave": {
|
||||
"name": "Sync on Save",
|
||||
"desc": "When you save a file, sync automatically"
|
||||
},
|
||||
"syncOnEditorSave": {
|
||||
"name": "Sync on Editor Save",
|
||||
"desc": "When you save a file in the editor, sync automatically"
|
||||
},
|
||||
"syncOnFileOpen": {
|
||||
"name": "Sync on File Open",
|
||||
"desc": "When you open a file, sync automatically"
|
||||
},
|
||||
"syncOnStart": {
|
||||
"name": "Sync on Start",
|
||||
"desc": "Start synchronization after launching Obsidian."
|
||||
},
|
||||
"syncAfterMerge": {
|
||||
"name": "Sync after merging file",
|
||||
"desc": "Sync automatically after merging files"
|
||||
},
|
||||
"trashInsteadDelete": {
|
||||
"name": "Use the trash bin",
|
||||
"desc": "Do not delete files that are deleted in remote, just move to trash."
|
||||
},
|
||||
"doNotDeleteFolder": {
|
||||
"name": "Keep empty folder",
|
||||
"desc": "Normally, a folder is deleted when it becomes empty after a synchronization. Enabling this will prevent it from getting deleted"
|
||||
},
|
||||
"resolveConflictsByNewerFile": {
|
||||
"name": "Always overwrite with a newer file (beta)",
|
||||
"desc": "(Def off) Resolve conflicts by newer files automatically."
|
||||
},
|
||||
"checkConflictOnlyOnOpen": {
|
||||
"name": "Postpone resolution of inactive files"
|
||||
},
|
||||
"showMergeDialogOnlyOnActive": {
|
||||
"name": "Postpone manual resolution of inactive files"
|
||||
},
|
||||
"disableMarkdownAutoMerge": {
|
||||
"name": "Always resolve conflicts manually",
|
||||
"desc": "If this switch is turned on, a merge dialog will be displayed, even if the sensible-merge is possible automatically. (Turn on to previous behavior)"
|
||||
},
|
||||
"writeDocumentsIfConflicted": {
|
||||
"name": "Always reflect synchronized changes even if the note has a conflict",
|
||||
"desc": "Turn on to previous behavior"
|
||||
},
|
||||
"syncInternalFilesInterval": {
|
||||
"name": "Scan hidden files periodically",
|
||||
"desc": "Seconds, 0 to disable"
|
||||
},
|
||||
"batchSave": {
|
||||
"name": "Batch database update",
|
||||
"desc": "Reducing the frequency with which on-disk changes are reflected into the DB"
|
||||
},
|
||||
"readChunksOnline": {
|
||||
"name": "Fetch chunks on demand",
|
||||
"desc": "(ex. Read chunks online) If this option is enabled, LiveSync reads chunks online directly instead of replicating them locally. Increasing Custom chunk size is recommended."
|
||||
},
|
||||
"syncMaxSizeInMB": {
|
||||
"name": "Maximum file size",
|
||||
"desc": "(MB) If this is set, changes to local and remote files that are larger than this will be skipped. If the file becomes smaller again, a newer one will be used."
|
||||
},
|
||||
"useIgnoreFiles": {
|
||||
"name": "(Beta) Use ignore files",
|
||||
"desc": "If this is set, changes to local files which are matched by the ignore files will be skipped. Remote changes are determined using local ignore files."
|
||||
},
|
||||
"ignoreFiles": {
|
||||
"name": "Ignore files",
|
||||
"desc": "We can use multiple ignore files, e.g.) `.gitignore, .dockerignore`"
|
||||
},
|
||||
"batch_size": {
|
||||
"name": "Batch size",
|
||||
"desc": "Number of change feed items to process at a time. Defaults to 50. Minimum is 2."
|
||||
},
|
||||
"batches_limit": {
|
||||
"name": "Batch limit",
|
||||
"desc": "Number of batches to process at a time. Defaults to 40. Minimum is 2. This along with batch size controls how many docs are kept in memory at a time."
|
||||
},
|
||||
"useTimeouts": {
|
||||
"name": "Use timeouts instead of heartbeats",
|
||||
"desc": "If this option is enabled, PouchDB will hold the connection open for 60 seconds, and if no change arrives in that time, close and reopen the socket, instead of holding it open indefinitely. Useful when a proxy limits request duration but can increase resource usage."
|
||||
},
|
||||
"concurrencyOfReadChunksOnline": {
|
||||
"name": "Batch size of on-demand fetching"
|
||||
},
|
||||
"minimumIntervalOfReadChunksOnline": {
|
||||
"name": "The delay for consecutive on-demand fetches"
|
||||
},
|
||||
"suspendFileWatching": {
|
||||
"name": "Suspend file watching",
|
||||
"desc": "Stop watching for file change."
|
||||
},
|
||||
"suspendParseReplicationResult": {
|
||||
"name": "Suspend database reflecting",
|
||||
"desc": "Stop reflecting database changes to storage files."
|
||||
},
|
||||
"writeLogToTheFile": {
|
||||
"name": "Write logs into the file",
|
||||
"desc": "Warning! This will have a serious impact on performance. And the logs will not be synchronised under the default name. Please be careful with logs; they often contain your confidential information."
|
||||
},
|
||||
"deleteMetadataOfDeletedFiles": {
|
||||
"name": "Do not keep metadata of deleted files."
|
||||
},
|
||||
"useIndexedDBAdapter": {
|
||||
"name": "(Obsolete) Use an old adapter for compatibility",
|
||||
"desc": "Before v0.17.16, we used an old adapter for the local database. Now the new adapter is preferred. However, it needs local database rebuilding. Please disable this toggle when you have enough time. If leave it enabled, also while fetching from the remote database, you will be asked to disable this.",
|
||||
"obsolete": true
|
||||
},
|
||||
"watchInternalFileChanges": {
|
||||
"name": "Scan changes on customization sync",
|
||||
"desc": "Do not use internal API"
|
||||
},
|
||||
"doNotSuspendOnFetching": {
|
||||
"name": "Fetch database with previous behaviour"
|
||||
},
|
||||
"disableCheckingConfigMismatch": {
|
||||
"name": "Do not check configuration mismatch before replication"
|
||||
},
|
||||
"usePluginSync": {
|
||||
"name": "Enable customization sync"
|
||||
},
|
||||
"autoSweepPlugins": {
|
||||
"name": "Scan customization automatically",
|
||||
"desc": "Scan customization before replicating."
|
||||
},
|
||||
"autoSweepPluginsPeriodic": {
|
||||
"name": "Scan customization periodically",
|
||||
"desc": "Scan customization every 1 minute."
|
||||
},
|
||||
"notifyPluginOrSettingUpdated": {
|
||||
"name": "Notify customized",
|
||||
"desc": "Notify when other device has newly customized."
|
||||
},
|
||||
"remoteType": {
|
||||
"name": "Remote Type",
|
||||
"desc": "Remote server type"
|
||||
},
|
||||
"endpoint": {
|
||||
"name": "Endpoint URL",
|
||||
"placeHolder": "https://........"
|
||||
},
|
||||
"accessKey": {
|
||||
"name": "Access Key"
|
||||
},
|
||||
"secretKey": {
|
||||
"name": "Secret Key"
|
||||
},
|
||||
"region": {
|
||||
"name": "Region",
|
||||
"placeHolder": "auto"
|
||||
},
|
||||
"bucket": {
|
||||
"name": "Bucket Name"
|
||||
},
|
||||
"useCustomRequestHandler": {
|
||||
"name": "Use Custom HTTP Handler",
|
||||
"desc": "If your Object Storage could not configured accepting CORS, enable this."
|
||||
},
|
||||
"maxChunksInEden": {
|
||||
"name": "Maximum Incubating Chunks",
|
||||
"desc": "The maximum number of chunks that can be incubated within the document. Chunks exceeding this number will immediately graduate to independent chunks."
|
||||
},
|
||||
"maxTotalLengthInEden": {
|
||||
"name": "Maximum Incubating Chunk Size",
|
||||
"desc": "The maximum total size of chunks that can be incubated within the document. Chunks exceeding this size will immediately graduate to independent chunks."
|
||||
},
|
||||
"maxAgeInEden": {
|
||||
"name": "Maximum Incubation Period",
|
||||
"desc": "The maximum duration for which chunks can be incubated within the document. Chunks exceeding this period will graduate to independent chunks."
|
||||
},
|
||||
"settingSyncFile": {
|
||||
"name": "Filename",
|
||||
"desc": "If you set this, all settings are saved in a markdown file. You will be notified when new settings arrive. You can set different files by the platform."
|
||||
},
|
||||
"preset": {
|
||||
"name": "Presets",
|
||||
"desc": "Apply preset configuration"
|
||||
},
|
||||
"syncMode": {
|
||||
liveSync: {
|
||||
name: "Sync Mode",
|
||||
},
|
||||
"periodicReplicationInterval": {
|
||||
"name": "Periodic Sync interval",
|
||||
"desc": "Interval (sec)"
|
||||
couchDB_URI: {
|
||||
name: "URI",
|
||||
placeHolder: "https://........",
|
||||
},
|
||||
"syncInternalFilesBeforeReplication": {
|
||||
"name": "Scan for hidden files before replication"
|
||||
couchDB_USER: {
|
||||
name: "Username",
|
||||
desc: "username",
|
||||
},
|
||||
"automaticallyDeleteMetadataOfDeletedFiles": {
|
||||
"name": "Delete old metadata of deleted files on start-up",
|
||||
"desc": "(Days passed, 0 to disable automatic-deletion)"
|
||||
couchDB_PASSWORD: {
|
||||
name: "Password",
|
||||
desc: "password",
|
||||
},
|
||||
"additionalSuffixOfDatabaseName": {
|
||||
"name": "Database suffix",
|
||||
"desc": "LiveSync could not handle multiple vaults which have same name without different prefix, This should be automatically configured."
|
||||
couchDB_DBNAME: {
|
||||
name: "Database name",
|
||||
},
|
||||
"hashAlg": {
|
||||
"name": configurationNames["hashAlg"]?.name || "",
|
||||
"desc": "xxhash64 is the current default."
|
||||
passphrase: {
|
||||
name: "Passphrase",
|
||||
desc: "Encrypting passphrase. If you change the passphrase of an existing database, overwriting the remote database is strongly recommended.",
|
||||
},
|
||||
"deviceAndVaultName": {
|
||||
"name": "Device name",
|
||||
"desc": "Unique name between all synchronized devices. To edit this setting, please disable customization sync once."
|
||||
showStatusOnEditor: {
|
||||
name: "Show status inside the editor",
|
||||
desc: "Reflected after reboot",
|
||||
},
|
||||
"displayLanguage": {
|
||||
"name": "Display Language",
|
||||
"desc": "Not all messages have been translated. And, please revert to \"Default\" when reporting errors."
|
||||
showOnlyIconsOnEditor: {
|
||||
name: "Show status as icons only",
|
||||
},
|
||||
showStatusOnStatusbar: {
|
||||
name: "Show status on the status bar",
|
||||
desc: "Reflected after reboot.",
|
||||
},
|
||||
lessInformationInLog: {
|
||||
name: "Show only notifications",
|
||||
desc: "Prevent logging and show only notification. Please disable when you report the logs",
|
||||
},
|
||||
showVerboseLog: {
|
||||
name: "Verbose Log",
|
||||
desc: "Show verbose log. Please enable when you report the logs",
|
||||
},
|
||||
hashCacheMaxCount: {
|
||||
name: "Memory cache size (by total items)",
|
||||
},
|
||||
hashCacheMaxAmount: {
|
||||
name: "Memory cache size (by total characters)",
|
||||
desc: "(Mega chars)",
|
||||
},
|
||||
writeCredentialsForSettingSync: {
|
||||
name: "Write credentials in the file",
|
||||
desc: "(Not recommended) If set, credentials will be stored in the file.",
|
||||
},
|
||||
notifyAllSettingSyncFile: {
|
||||
name: "Notify all setting files",
|
||||
},
|
||||
configPassphrase: {
|
||||
name: "Passphrase of sensitive configuration items",
|
||||
desc: "This passphrase will not be copied to another device. It will be set to `Default` until you configure it again.",
|
||||
},
|
||||
configPassphraseStore: {
|
||||
name: "Encrypting sensitive configuration items",
|
||||
},
|
||||
syncOnSave: {
|
||||
name: "Sync on Save",
|
||||
desc: "When you save a file, sync automatically",
|
||||
},
|
||||
syncOnEditorSave: {
|
||||
name: "Sync on Editor Save",
|
||||
desc: "When you save a file in the editor, sync automatically",
|
||||
},
|
||||
syncOnFileOpen: {
|
||||
name: "Sync on File Open",
|
||||
desc: "When you open a file, sync automatically",
|
||||
},
|
||||
syncOnStart: {
|
||||
name: "Sync on Start",
|
||||
desc: "Start synchronization after launching Obsidian.",
|
||||
},
|
||||
syncAfterMerge: {
|
||||
name: "Sync after merging file",
|
||||
desc: "Sync automatically after merging files",
|
||||
},
|
||||
trashInsteadDelete: {
|
||||
name: "Use the trash bin",
|
||||
desc: "Do not delete files that are deleted in remote, just move to trash.",
|
||||
},
|
||||
doNotDeleteFolder: {
|
||||
name: "Keep empty folder",
|
||||
desc: "Normally, a folder is deleted when it becomes empty after a synchronization. Enabling this will prevent it from getting deleted",
|
||||
},
|
||||
resolveConflictsByNewerFile: {
|
||||
name: "Always overwrite with a newer file (beta)",
|
||||
desc: "(Def off) Resolve conflicts by newer files automatically.",
|
||||
},
|
||||
checkConflictOnlyOnOpen: {
|
||||
name: "Postpone resolution of inactive files",
|
||||
},
|
||||
showMergeDialogOnlyOnActive: {
|
||||
name: "Postpone manual resolution of inactive files",
|
||||
},
|
||||
disableMarkdownAutoMerge: {
|
||||
name: "Always resolve conflicts manually",
|
||||
desc: "If this switch is turned on, a merge dialog will be displayed, even if the sensible-merge is possible automatically. (Turn on to previous behavior)",
|
||||
},
|
||||
writeDocumentsIfConflicted: {
|
||||
name: "Always reflect synchronized changes even if the note has a conflict",
|
||||
desc: "Turn on to previous behavior",
|
||||
},
|
||||
syncInternalFilesInterval: {
|
||||
name: "Scan hidden files periodically",
|
||||
desc: "Seconds, 0 to disable",
|
||||
},
|
||||
batchSave: {
|
||||
name: "Batch database update",
|
||||
desc: "Reducing the frequency with which on-disk changes are reflected into the DB",
|
||||
},
|
||||
readChunksOnline: {
|
||||
name: "Fetch chunks on demand",
|
||||
desc: "(ex. Read chunks online) If this option is enabled, LiveSync reads chunks online directly instead of replicating them locally. Increasing Custom chunk size is recommended.",
|
||||
},
|
||||
syncMaxSizeInMB: {
|
||||
name: "Maximum file size",
|
||||
desc: "(MB) If this is set, changes to local and remote files that are larger than this will be skipped. If the file becomes smaller again, a newer one will be used.",
|
||||
},
|
||||
useIgnoreFiles: {
|
||||
name: "(Beta) Use ignore files",
|
||||
desc: "If this is set, changes to local files which are matched by the ignore files will be skipped. Remote changes are determined using local ignore files.",
|
||||
},
|
||||
ignoreFiles: {
|
||||
name: "Ignore files",
|
||||
desc: "We can use multiple ignore files, e.g.) `.gitignore, .dockerignore`",
|
||||
},
|
||||
batch_size: {
|
||||
name: "Batch size",
|
||||
desc: "Number of change feed items to process at a time. Defaults to 50. Minimum is 2.",
|
||||
},
|
||||
batches_limit: {
|
||||
name: "Batch limit",
|
||||
desc: "Number of batches to process at a time. Defaults to 40. Minimum is 2. This along with batch size controls how many docs are kept in memory at a time.",
|
||||
},
|
||||
useTimeouts: {
|
||||
name: "Use timeouts instead of heartbeats",
|
||||
desc: "If this option is enabled, PouchDB will hold the connection open for 60 seconds, and if no change arrives in that time, close and reopen the socket, instead of holding it open indefinitely. Useful when a proxy limits request duration but can increase resource usage.",
|
||||
},
|
||||
concurrencyOfReadChunksOnline: {
|
||||
name: "Batch size of on-demand fetching",
|
||||
},
|
||||
minimumIntervalOfReadChunksOnline: {
|
||||
name: "The delay for consecutive on-demand fetches",
|
||||
},
|
||||
suspendFileWatching: {
|
||||
name: "Suspend file watching",
|
||||
desc: "Stop watching for file change.",
|
||||
},
|
||||
suspendParseReplicationResult: {
|
||||
name: "Suspend database reflecting",
|
||||
desc: "Stop reflecting database changes to storage files.",
|
||||
},
|
||||
writeLogToTheFile: {
|
||||
name: "Write logs into the file",
|
||||
desc: "Warning! This will have a serious impact on performance. And the logs will not be synchronised under the default name. Please be careful with logs; they often contain your confidential information.",
|
||||
},
|
||||
deleteMetadataOfDeletedFiles: {
|
||||
name: "Do not keep metadata of deleted files.",
|
||||
},
|
||||
useIndexedDBAdapter: {
|
||||
name: "(Obsolete) Use an old adapter for compatibility",
|
||||
desc: "Before v0.17.16, we used an old adapter for the local database. Now the new adapter is preferred. However, it needs local database rebuilding. Please disable this toggle when you have enough time. If leave it enabled, also while fetching from the remote database, you will be asked to disable this.",
|
||||
obsolete: true,
|
||||
},
|
||||
watchInternalFileChanges: {
|
||||
name: "Scan changes on customization sync",
|
||||
desc: "Do not use internal API",
|
||||
},
|
||||
doNotSuspendOnFetching: {
|
||||
name: "Fetch database with previous behaviour",
|
||||
},
|
||||
disableCheckingConfigMismatch: {
|
||||
name: "Do not check configuration mismatch before replication",
|
||||
},
|
||||
usePluginSync: {
|
||||
name: "Enable customization sync",
|
||||
},
|
||||
autoSweepPlugins: {
|
||||
name: "Scan customization automatically",
|
||||
desc: "Scan customization before replicating.",
|
||||
},
|
||||
autoSweepPluginsPeriodic: {
|
||||
name: "Scan customization periodically",
|
||||
desc: "Scan customization every 1 minute.",
|
||||
},
|
||||
notifyPluginOrSettingUpdated: {
|
||||
name: "Notify customized",
|
||||
desc: "Notify when other device has newly customized.",
|
||||
},
|
||||
remoteType: {
|
||||
name: "Remote Type",
|
||||
desc: "Remote server type",
|
||||
},
|
||||
endpoint: {
|
||||
name: "Endpoint URL",
|
||||
placeHolder: "https://........",
|
||||
},
|
||||
accessKey: {
|
||||
name: "Access Key",
|
||||
},
|
||||
secretKey: {
|
||||
name: "Secret Key",
|
||||
},
|
||||
region: {
|
||||
name: "Region",
|
||||
placeHolder: "auto",
|
||||
},
|
||||
bucket: {
|
||||
name: "Bucket Name",
|
||||
},
|
||||
useCustomRequestHandler: {
|
||||
name: "Use Custom HTTP Handler",
|
||||
desc: "If your Object Storage could not configured accepting CORS, enable this.",
|
||||
},
|
||||
maxChunksInEden: {
|
||||
name: "Maximum Incubating Chunks",
|
||||
desc: "The maximum number of chunks that can be incubated within the document. Chunks exceeding this number will immediately graduate to independent chunks.",
|
||||
},
|
||||
maxTotalLengthInEden: {
|
||||
name: "Maximum Incubating Chunk Size",
|
||||
desc: "The maximum total size of chunks that can be incubated within the document. Chunks exceeding this size will immediately graduate to independent chunks.",
|
||||
},
|
||||
maxAgeInEden: {
|
||||
name: "Maximum Incubation Period",
|
||||
desc: "The maximum duration for which chunks can be incubated within the document. Chunks exceeding this period will graduate to independent chunks.",
|
||||
},
|
||||
settingSyncFile: {
|
||||
name: "Filename",
|
||||
desc: "If you set this, all settings are saved in a markdown file. You will be notified when new settings arrive. You can set different files by the platform.",
|
||||
},
|
||||
preset: {
|
||||
name: "Presets",
|
||||
desc: "Apply preset configuration",
|
||||
},
|
||||
syncMode: {
|
||||
name: "Sync Mode",
|
||||
},
|
||||
periodicReplicationInterval: {
|
||||
name: "Periodic Sync interval",
|
||||
desc: "Interval (sec)",
|
||||
},
|
||||
syncInternalFilesBeforeReplication: {
|
||||
name: "Scan for hidden files before replication",
|
||||
},
|
||||
automaticallyDeleteMetadataOfDeletedFiles: {
|
||||
name: "Delete old metadata of deleted files on start-up",
|
||||
desc: "(Days passed, 0 to disable automatic-deletion)",
|
||||
},
|
||||
additionalSuffixOfDatabaseName: {
|
||||
name: "Database suffix",
|
||||
desc: "LiveSync could not handle multiple vaults which have same name without different prefix, This should be automatically configured.",
|
||||
},
|
||||
hashAlg: {
|
||||
name: configurationNames["hashAlg"]?.name || "",
|
||||
desc: "xxhash64 is the current default.",
|
||||
},
|
||||
deviceAndVaultName: {
|
||||
name: "Device name",
|
||||
desc: "Unique name between all synchronized devices. To edit this setting, please disable customization sync once.",
|
||||
},
|
||||
displayLanguage: {
|
||||
name: "Display Language",
|
||||
desc: 'Not all messages have been translated. And, please revert to "Default" when reporting errors.',
|
||||
},
|
||||
enableChunkSplitterV2: {
|
||||
name: "Use splitting-limit-capped chunk splitter",
|
||||
desc: "If enabled, chunks will be split into no more than 100 items. However, dedupe is slightly weaker."
|
||||
desc: "If enabled, chunks will be split into no more than 100 items. However, dedupe is slightly weaker.",
|
||||
},
|
||||
disableWorkerForGeneratingChunks: {
|
||||
name: "Do not split chunks in the background",
|
||||
desc: "If disabled(toggled), chunks will be split on the UI thread (Previous behaviour)."
|
||||
desc: "If disabled(toggled), chunks will be split on the UI thread (Previous behaviour).",
|
||||
},
|
||||
processSmallFilesInUIThread: {
|
||||
name: "Process small files in the foreground",
|
||||
desc: "If enabled, the file under 1kb will be processed in the UI thread."
|
||||
desc: "If enabled, the file under 1kb will be processed in the UI thread.",
|
||||
},
|
||||
batchSaveMinimumDelay: {
|
||||
name: "Minimum delay for batch database updating",
|
||||
desc: "Seconds. Saving to the local database will be delayed until this value after we stop typing or saving."
|
||||
desc: "Seconds. Saving to the local database will be delayed until this value after we stop typing or saving.",
|
||||
},
|
||||
batchSaveMaximumDelay: {
|
||||
name: "Maximum delay for batch database updating",
|
||||
desc: "Saving will be performed forcefully after this number of seconds."
|
||||
desc: "Saving will be performed forcefully after this number of seconds.",
|
||||
},
|
||||
"notifyThresholdOfRemoteStorageSize": {
|
||||
notifyThresholdOfRemoteStorageSize: {
|
||||
name: "Notify when the estimated remote storage size exceeds on start up",
|
||||
desc: "MB (0 to disable)."
|
||||
desc: "MB (0 to disable).",
|
||||
},
|
||||
"usePluginSyncV2": {
|
||||
usePluginSyncV2: {
|
||||
name: "Enable per-file customization sync",
|
||||
desc: "If enabled, efficient per-file customization sync will be used. A minor migration is required when enabling this feature, and all devices must be updated to v0.23.18. Enabling this feature will result in losing compatibility with older versions."
|
||||
desc: "If enabled, efficient per-file customization sync will be used. A minor migration is required when enabling this feature, and all devices must be updated to v0.23.18. Enabling this feature will result in losing compatibility with older versions.",
|
||||
},
|
||||
"handleFilenameCaseSensitive": {
|
||||
handleFilenameCaseSensitive: {
|
||||
name: "Handle files as Case-Sensitive",
|
||||
desc: "If this enabled, All files are handled as case-Sensitive (Previous behaviour)."
|
||||
desc: "If this enabled, All files are handled as case-Sensitive (Previous behaviour).",
|
||||
},
|
||||
"doNotUseFixedRevisionForChunks": {
|
||||
doNotUseFixedRevisionForChunks: {
|
||||
name: "Compute revisions for chunks (Previous behaviour)",
|
||||
desc: "If this enabled, all chunks will be stored with the revision made from its content. (Previous behaviour)"
|
||||
desc: "If this enabled, all chunks will be stored with the revision made from its content. (Previous behaviour)",
|
||||
},
|
||||
"sendChunksBulkMaxSize": {
|
||||
sendChunksBulkMaxSize: {
|
||||
name: "Maximum size of chunks to send in one request",
|
||||
desc: "MB"
|
||||
desc: "MB",
|
||||
},
|
||||
"useAdvancedMode": {
|
||||
useAdvancedMode: {
|
||||
name: "Enable advanced features",
|
||||
// desc: "Enable advanced mode"
|
||||
},
|
||||
@@ -354,11 +363,11 @@ export const SettingInformation: Partial<Record<keyof AllSettings, Configuration
|
||||
useEdgeCaseMode: {
|
||||
name: "Enable edge case treatment features",
|
||||
},
|
||||
"enableDebugTools": {
|
||||
enableDebugTools: {
|
||||
name: "Enable Developers' Debug Tools.",
|
||||
desc: "You need a restart to apply this setting."
|
||||
}
|
||||
}
|
||||
desc: "You need a restart to apply this setting.",
|
||||
},
|
||||
};
|
||||
function translateInfo(infoSrc: ConfigurationItem | undefined | false) {
|
||||
if (!infoSrc) return false;
|
||||
const info = { ...infoSrc };
|
||||
@@ -369,7 +378,6 @@ function translateInfo(infoSrc: ConfigurationItem | undefined | false) {
|
||||
return info;
|
||||
}
|
||||
function _getConfig(key: AllSettingItemKey) {
|
||||
|
||||
if (key in configurationNames) {
|
||||
return configurationNames[key as keyof ObsidianLiveSyncSettings];
|
||||
}
|
||||
@@ -385,4 +393,4 @@ export function getConfName(key: AllSettingItemKey) {
|
||||
const conf = getConfig(key);
|
||||
if (!conf) return `${key} (No info)`;
|
||||
return conf.name;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user