mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2025-12-16 03:05:57 +00:00
Fixed:
- No longer missing tasks which have queued as the same key (e.g., for the same operation to the same file). - Some trivial issues have been fixed. New feature: - Reloading Obsidian can be scheduled until that file and database operations are stable.
This commit is contained in:
@@ -10,7 +10,7 @@ import { readString, decodeBinary, arrayBufferToBase64, digestHash } from "../li
|
|||||||
import { serialized } from "../lib/src/concurrency/lock.ts";
|
import { serialized } from "../lib/src/concurrency/lock.ts";
|
||||||
import { LiveSyncCommands } from "./LiveSyncCommands.ts";
|
import { LiveSyncCommands } from "./LiveSyncCommands.ts";
|
||||||
import { stripAllPrefixes } from "../lib/src/string_and_binary/path.ts";
|
import { stripAllPrefixes } from "../lib/src/string_and_binary/path.ts";
|
||||||
import { PeriodicProcessor, askYesNo, disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask } from "../common/utils.ts";
|
import { PeriodicProcessor, disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask } from "../common/utils.ts";
|
||||||
import { PluginDialogModal } from "../common/dialogs.ts";
|
import { PluginDialogModal } from "../common/dialogs.ts";
|
||||||
import { JsonResolveModal } from "../ui/JsonResolveModal.ts";
|
import { JsonResolveModal } from "../ui/JsonResolveModal.ts";
|
||||||
import { QueueProcessor } from '../lib/src/concurrency/processor.ts';
|
import { QueueProcessor } from '../lib/src/concurrency/processor.ts';
|
||||||
@@ -466,12 +466,7 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
Logger(`Plugin reloaded: ${pluginManifest.name}`, LOG_LEVEL_NOTICE, "plugin-reload-" + pluginManifest.id);
|
Logger(`Plugin reloaded: ${pluginManifest.name}`, LOG_LEVEL_NOTICE, "plugin-reload-" + pluginManifest.id);
|
||||||
}
|
}
|
||||||
} else if (data.category == "CONFIG") {
|
} else if (data.category == "CONFIG") {
|
||||||
scheduleTask("configReload", 250, async () => {
|
this.plugin.askReload();
|
||||||
if (await askYesNo(this.app, "Do you want to restart and reload Obsidian now?") == "yes") {
|
|
||||||
// @ts-ignore
|
|
||||||
this.app.commands.executeCommandById("app:reload")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
|||||||
@@ -432,13 +432,14 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
|
|
||||||
// If something changes left, notify for reloading Obsidian.
|
// If something changes left, notify for reloading Obsidian.
|
||||||
if (updatedCount != 0) {
|
if (updatedCount != 0) {
|
||||||
this.plugin.askInPopup(`updated-any-hidden`, `Hidden files have been synchronized, Press {HERE} to reload Obsidian, or press elsewhere to dismiss this message.`, (anchor) => {
|
if (!this.plugin.isReloadingScheduled) {
|
||||||
anchor.text = "HERE";
|
this.plugin.askInPopup(`updated-any-hidden`, `Hidden files have been synchronised, Press {HERE} to schedule a reload of Obsidian, or press elsewhere to dismiss this message.`, (anchor) => {
|
||||||
anchor.addEventListener("click", () => {
|
anchor.text = "HERE";
|
||||||
// @ts-ignore
|
anchor.addEventListener("click", () => {
|
||||||
this.app.commands.executeCommandById("app:reload");
|
this.plugin.scheduleAppReload();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -471,6 +472,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
children: [],
|
children: [],
|
||||||
deleted: false,
|
deleted: false,
|
||||||
type: "newnote",
|
type: "newnote",
|
||||||
|
eden: {},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (await isDocContentSame(readAsBlob(old), content) && !forceWrite) {
|
if (await isDocContentSame(readAsBlob(old), content) && !forceWrite) {
|
||||||
@@ -521,6 +523,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
children: [],
|
children: [],
|
||||||
deleted: true,
|
deleted: true,
|
||||||
type: "newnote",
|
type: "newnote",
|
||||||
|
eden: {}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Remove all conflicted before deleting.
|
// Remove all conflicted before deleting.
|
||||||
|
|||||||
2
src/lib
2
src/lib
Submodule src/lib updated: 57f0be0464...13f8370ef5
149
src/main.ts
149
src/main.ts
@@ -32,7 +32,7 @@ import { LogPaneView, VIEW_TYPE_LOG } from "./ui/LogPaneView.ts";
|
|||||||
import { LRUCache } from "./lib/src/memory/LRUCache.ts";
|
import { LRUCache } from "./lib/src/memory/LRUCache.ts";
|
||||||
import { SerializedFileAccess } from "./storages/SerializedFileAccess.js";
|
import { SerializedFileAccess } from "./storages/SerializedFileAccess.js";
|
||||||
import { QueueProcessor } from "./lib/src/concurrency/processor.js";
|
import { QueueProcessor } from "./lib/src/concurrency/processor.js";
|
||||||
import { reactive, reactiveSource } from "./lib/src/dataobject/reactive.js";
|
import { reactive, reactiveSource, type ReactiveValue } from "./lib/src/dataobject/reactive.js";
|
||||||
import { initializeStores } from "./common/stores.js";
|
import { initializeStores } from "./common/stores.js";
|
||||||
import { JournalSyncMinio } from "./lib/src/replication/journal/objectstore/JournalSyncMinio.js";
|
import { JournalSyncMinio } from "./lib/src/replication/journal/objectstore/JournalSyncMinio.js";
|
||||||
import { LiveSyncJournalReplicator, type LiveSyncJournalReplicatorEnv } from "./lib/src/replication/journal/LiveSyncJournalReplicator.js";
|
import { LiveSyncJournalReplicator, type LiveSyncJournalReplicatorEnv } from "./lib/src/replication/journal/LiveSyncJournalReplicator.js";
|
||||||
@@ -1422,55 +1422,62 @@ We can perform a command in this file.
|
|||||||
}
|
}
|
||||||
async handleFileEvent(queue: FileEventItem): Promise<any> {
|
async handleFileEvent(queue: FileEventItem): Promise<any> {
|
||||||
const file = queue.args.file;
|
const file = queue.args.file;
|
||||||
const key = `file-last-proc-${queue.type}-${file.path}`;
|
const lockKey = `handleFile:${file.path}`;
|
||||||
const last = Number(await this.kvDB.get(key) || 0);
|
return await serialized(lockKey, async () => {
|
||||||
let mtime = file.mtime;
|
const key = `file-last-proc-${queue.type}-${file.path}`;
|
||||||
if (queue.type == "DELETE") {
|
const last = Number(await this.kvDB.get(key) || 0);
|
||||||
await this.deleteFromDBbyPath(file.path);
|
let mtime = file.mtime;
|
||||||
mtime = file.mtime - 1;
|
if (queue.type == "DELETE") {
|
||||||
const keyD1 = `file-last-proc-CREATE-${file.path}`;
|
await this.deleteFromDBbyPath(file.path);
|
||||||
const keyD2 = `file-last-proc-CHANGED-${file.path}`;
|
mtime = file.mtime - 1;
|
||||||
await this.kvDB.set(keyD1, mtime);
|
const keyD1 = `file-last-proc-CREATE-${file.path}`;
|
||||||
await this.kvDB.set(keyD2, mtime);
|
const keyD2 = `file-last-proc-CHANGED-${file.path}`;
|
||||||
} else if (queue.type == "INTERNAL") {
|
|
||||||
await this.addOnHiddenFileSync.watchVaultRawEventsAsync(file.path);
|
|
||||||
await this.addOnConfigSync.watchVaultRawEventsAsync(file.path);
|
|
||||||
} else {
|
|
||||||
const targetFile = this.vaultAccess.getAbstractFileByPath(file.path);
|
|
||||||
if (!(targetFile instanceof TFile)) {
|
|
||||||
Logger(`Target file was not found: ${file.path}`, LOG_LEVEL_INFO);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (file.mtime == last) {
|
|
||||||
Logger(`File has been already scanned on ${queue.type}, skip: ${file.path}`, LOG_LEVEL_VERBOSE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// const cache = queue.args.cache;
|
|
||||||
if (queue.type == "CREATE" || queue.type == "CHANGED") {
|
|
||||||
fireAndForget(() => this.checkAndApplySettingFromMarkdown(queue.args.file.path, true));
|
|
||||||
const keyD1 = `file-last-proc-DELETED-${file.path}`;
|
|
||||||
await this.kvDB.set(keyD1, mtime);
|
await this.kvDB.set(keyD1, mtime);
|
||||||
if (!await this.updateIntoDB(targetFile, undefined)) {
|
await this.kvDB.set(keyD2, mtime);
|
||||||
Logger(`STORAGE -> DB: failed, cancel the relative operations: ${targetFile.path}`, LOG_LEVEL_INFO);
|
} else if (queue.type == "INTERNAL") {
|
||||||
// cancel running queues and remove one of atomic operation
|
await this.addOnHiddenFileSync.watchVaultRawEventsAsync(file.path);
|
||||||
this.cancelRelativeEvent(queue);
|
await this.addOnConfigSync.watchVaultRawEventsAsync(file.path);
|
||||||
|
} else {
|
||||||
|
const targetFile = this.vaultAccess.getAbstractFileByPath(file.path);
|
||||||
|
if (!(targetFile instanceof TFile)) {
|
||||||
|
Logger(`Target file was not found: ${file.path}`, LOG_LEVEL_INFO);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (file.mtime == last) {
|
||||||
|
Logger(`File has been already scanned on ${queue.type}, skip: ${file.path}`, LOG_LEVEL_VERBOSE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const cache = queue.args.cache;
|
||||||
|
if (queue.type == "CREATE" || queue.type == "CHANGED") {
|
||||||
|
fireAndForget(() => this.checkAndApplySettingFromMarkdown(queue.args.file.path, true));
|
||||||
|
const keyD1 = `file-last-proc-DELETED-${file.path}`;
|
||||||
|
await this.kvDB.set(keyD1, mtime);
|
||||||
|
if (!await this.updateIntoDB(targetFile, undefined)) {
|
||||||
|
Logger(`STORAGE -> DB: failed, cancel the relative operations: ${targetFile.path}`, LOG_LEVEL_INFO);
|
||||||
|
// cancel running queues and remove one of atomic operation
|
||||||
|
this.cancelRelativeEvent(queue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (queue.type == "RENAME") {
|
||||||
|
// Obsolete
|
||||||
|
await this.watchVaultRenameAsync(targetFile, queue.args.oldPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (queue.type == "RENAME") {
|
await this.kvDB.set(key, mtime);
|
||||||
// Obsolete
|
});
|
||||||
await this.watchVaultRenameAsync(targetFile, queue.args.oldPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await this.kvDB.set(key, mtime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingFileEventCount = reactiveSource(0);
|
pendingFileEventCount = reactiveSource(0);
|
||||||
processingFileEventCount = reactiveSource(0);
|
processingFileEventCount = reactiveSource(0);
|
||||||
fileEventQueue =
|
fileEventQueue =
|
||||||
new QueueProcessor(
|
new QueueProcessor(
|
||||||
(items: FileEventItem[]) => this.handleFileEvent(items[0]),
|
async (items: FileEventItem[]) => {
|
||||||
|
await this.handleFileEvent(items[0]);
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
,
|
||||||
{ suspended: true, batchSize: 1, concurrentLimit: 5, delay: 100, yieldThreshold: FileWatchEventQueueMax, totalRemainingReactiveSource: this.pendingFileEventCount, processingEntitiesReactiveSource: this.processingFileEventCount }
|
{ suspended: true, batchSize: 1, concurrentLimit: 5, delay: 100, yieldThreshold: FileWatchEventQueueMax, totalRemainingReactiveSource: this.pendingFileEventCount, processingEntitiesReactiveSource: this.processingFileEventCount }
|
||||||
).replaceEnqueueProcessor((items, newItem) => this.queueNextFileEvent(items, newItem));
|
).replaceEnqueueProcessor((items, newItem) => this.queueNextFileEvent(items, newItem));
|
||||||
|
|
||||||
@@ -1894,7 +1901,7 @@ We can perform a command in this file.
|
|||||||
observeForLogs() {
|
observeForLogs() {
|
||||||
const padSpaces = `\u{2007}`.repeat(10);
|
const padSpaces = `\u{2007}`.repeat(10);
|
||||||
// const emptyMark = `\u{2003}`;
|
// const emptyMark = `\u{2003}`;
|
||||||
const rerenderTimer = new Map<string, [ReturnType<typeof setTimeout>, number]>;
|
const rerenderTimer = new Map<string, [ReturnType<typeof setTimeout>, number]>();
|
||||||
const tick = reactiveSource(0);
|
const tick = reactiveSource(0);
|
||||||
function padLeftSp(num: number, mark: string) {
|
function padLeftSp(num: number, mark: string) {
|
||||||
const numLen = `${num}`.length + 1;
|
const numLen = `${num}`.length + 1;
|
||||||
@@ -2005,9 +2012,9 @@ We can perform a command in this file.
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
const statusBarLabels = reactive(() => {
|
const statusBarLabels = reactive(() => {
|
||||||
|
const scheduleMessage = this.isReloadingScheduled ? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n` : "";
|
||||||
const { message } = statusLineLabel.value;
|
const { message } = statusLineLabel.value;
|
||||||
const status = this.statusLog.value;
|
const status = scheduleMessage + this.statusLog.value;
|
||||||
return {
|
return {
|
||||||
message, status
|
message, status
|
||||||
}
|
}
|
||||||
@@ -3174,5 +3181,61 @@ Or if you are sure know what had been happened, we can unlock the database from
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.app.commands.executeCommandById(id)
|
this.app.commands.executeCommandById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_totalProcessingCount?: ReactiveValue<number>;
|
||||||
|
get isReloadingScheduled() {
|
||||||
|
return this._totalProcessingCount !== undefined;
|
||||||
|
}
|
||||||
|
askReload(message?: string) {
|
||||||
|
if (this.isReloadingScheduled) {
|
||||||
|
Logger(`Reloading is already scheduled`, LOG_LEVEL_VERBOSE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scheduleTask("configReload", 250, async () => {
|
||||||
|
const RESTART_NOW = "Yes, restart immediately";
|
||||||
|
const RESTART_AFTER_STABLE = "Yes, schedule a restart after stabilisation";
|
||||||
|
const RETRY_LATER = "No, Leave it to me";
|
||||||
|
const ret = await askSelectString(this.app, message || "Do you want to restart and reload Obsidian now?", [RESTART_AFTER_STABLE, RESTART_NOW, RETRY_LATER]);
|
||||||
|
if (ret == RESTART_NOW) {
|
||||||
|
this.performAppReload();
|
||||||
|
} else if (ret == RESTART_AFTER_STABLE) {
|
||||||
|
this.scheduleAppReload();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
scheduleAppReload() {
|
||||||
|
if (!this._totalProcessingCount) {
|
||||||
|
const __tick = reactiveSource(0);
|
||||||
|
this._totalProcessingCount = reactive(() => {
|
||||||
|
const dbCount = this.databaseQueueCount.value;
|
||||||
|
const replicationCount = this.replicationResultCount.value;
|
||||||
|
const storageApplyingCount = this.storageApplyingCount.value;
|
||||||
|
const chunkCount = collectingChunks.value;
|
||||||
|
const pluginScanCount = pluginScanningCount.value;
|
||||||
|
const hiddenFilesCount = hiddenFilesEventCount.value + hiddenFilesProcessingCount.value;
|
||||||
|
const conflictProcessCount = this.conflictProcessQueueCount.value;
|
||||||
|
const e = this.pendingFileEventCount.value;
|
||||||
|
const proc = this.processingFileEventCount.value;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const __ = __tick.value;
|
||||||
|
return dbCount + replicationCount + storageApplyingCount + chunkCount + pluginScanCount + hiddenFilesCount + conflictProcessCount + e + proc;
|
||||||
|
})
|
||||||
|
this.registerInterval(setInterval(() => {
|
||||||
|
__tick.value++;
|
||||||
|
}, 1000) as unknown as number);
|
||||||
|
|
||||||
|
let stableCheck = 3;
|
||||||
|
this._totalProcessingCount.onChanged(e => {
|
||||||
|
if (e.value == 0) {
|
||||||
|
if (stableCheck-- <= 0) {
|
||||||
|
this.performAppReload();
|
||||||
|
}
|
||||||
|
Logger(`Obsidian will be restarted soon! (Within ${stableCheck} seconds)`, LOG_LEVEL_NOTICE, "restart-notice");
|
||||||
|
} else {
|
||||||
|
stableCheck = 3;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { Logger } from "../lib/src/common/logger.ts";
|
|||||||
import { checkSyncInfo, isCloudantURI } from "../lib/src/pouchdb/utils_couchdb.ts";
|
import { checkSyncInfo, isCloudantURI } from "../lib/src/pouchdb/utils_couchdb.ts";
|
||||||
import { testCrypt } from "../lib/src/encryption/e2ee_v2.ts";
|
import { testCrypt } from "../lib/src/encryption/e2ee_v2.ts";
|
||||||
import ObsidianLiveSyncPlugin from "../main.ts";
|
import ObsidianLiveSyncPlugin from "../main.ts";
|
||||||
import { askYesNo, performRebuildDB, requestToCouchDB, scheduleTask } from "../common/utils.ts";
|
import { askYesNo, performRebuildDB, requestToCouchDB } from "../common/utils.ts";
|
||||||
import { request, type ButtonComponent, TFile } from "obsidian";
|
import { request, type ButtonComponent, TFile } from "obsidian";
|
||||||
import { shouldBeIgnored } from "../lib/src/string_and_binary/path.ts";
|
import { shouldBeIgnored } from "../lib/src/string_and_binary/path.ts";
|
||||||
import MultipleRegExpControl from './components/MultipleRegExpControl.svelte';
|
import MultipleRegExpControl from './components/MultipleRegExpControl.svelte';
|
||||||
@@ -51,15 +51,6 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
await replicator.tryConnectRemote(trialSetting);
|
await replicator.tryConnectRemote(trialSetting);
|
||||||
}
|
}
|
||||||
|
|
||||||
askReload(message?: string) {
|
|
||||||
scheduleTask("configReload", 250, async () => {
|
|
||||||
if (await askYesNo(this.app, message || "Do you want to restart and reload Obsidian now?") == "yes") {
|
|
||||||
// @ts-ignore
|
|
||||||
this.app.commands.executeCommandById("app:reload")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
closeSetting() {
|
closeSetting() {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.plugin.app.setting.close()
|
this.plugin.app.setting.close()
|
||||||
@@ -217,7 +208,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
text.setButtonText("Enable").onClick(async () => {
|
text.setButtonText("Enable").onClick(async () => {
|
||||||
this.plugin.settings.isConfigured = true;
|
this.plugin.settings.isConfigured = true;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
this.askReload();
|
this.plugin.askReload();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -231,7 +222,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
await this.plugin.saveSettingData();
|
await this.plugin.saveSettingData();
|
||||||
await this.plugin.resetLocalDatabase();
|
await this.plugin.resetLocalDatabase();
|
||||||
// await this.plugin.initializeDatabase();
|
// await this.plugin.initializeDatabase();
|
||||||
this.askReload();
|
this.plugin.askReload();
|
||||||
}
|
}
|
||||||
}).setWarning()
|
}).setWarning()
|
||||||
})
|
})
|
||||||
@@ -1317,7 +1308,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
Logger("All done! Please set up subsequent devices with 'Copy current settings as a new setup URI' and 'Use the copied setup URI'.", LOG_LEVEL_NOTICE);
|
Logger("All done! Please set up subsequent devices with 'Copy current settings as a new setup URI' and 'Use the copied setup URI'.", LOG_LEVEL_NOTICE);
|
||||||
await this.plugin.addOnSetup.command_copySetupURI();
|
await this.plugin.addOnSetup.command_copySetupURI();
|
||||||
} else {
|
} else {
|
||||||
this.askReload();
|
this.plugin.askReload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1976,7 +1967,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
.onClick(async () => {
|
.onClick(async () => {
|
||||||
this.plugin.settings.isConfigured = false;
|
this.plugin.settings.isConfigured = false;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
this.askReload();
|
this.plugin.askReload();
|
||||||
}));
|
}));
|
||||||
const hatchWarn = containerHatchEl.createEl("div", { text: `To stop the boot up sequence for fixing problems on databases, you can put redflag.md on top of your vault (Rebooting obsidian is required).` });
|
const hatchWarn = containerHatchEl.createEl("div", { text: `To stop the boot up sequence for fixing problems on databases, you can put redflag.md on top of your vault (Rebooting obsidian is required).` });
|
||||||
hatchWarn.addClass("op-warn-info");
|
hatchWarn.addClass("op-warn-info");
|
||||||
@@ -2167,7 +2158,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
toggle.setValue(this.plugin.settings.suspendFileWatching).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.suspendFileWatching).onChange(async (value) => {
|
||||||
this.plugin.settings.suspendFileWatching = value;
|
this.plugin.settings.suspendFileWatching = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
this.askReload();
|
this.plugin.askReload();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
new Setting(containerHatchEl)
|
new Setting(containerHatchEl)
|
||||||
@@ -2177,7 +2168,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
toggle.setValue(this.plugin.settings.suspendParseReplicationResult).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.suspendParseReplicationResult).onChange(async (value) => {
|
||||||
this.plugin.settings.suspendParseReplicationResult = value;
|
this.plugin.settings.suspendParseReplicationResult = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
this.askReload();
|
this.plugin.askReload();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
new Setting(containerHatchEl)
|
new Setting(containerHatchEl)
|
||||||
|
|||||||
Reference in New Issue
Block a user