prevent conflic between obsidian sync and headless sync.
This commit is contained in:
parent
300e251734
commit
0d7f36ca9b
8 changed files with 212 additions and 109 deletions
|
|
@ -1,9 +1,14 @@
|
||||||
const { Notice } = require("obsidian");
|
const { Notice } = require("obsidian");
|
||||||
const fs = require("fs"); // Using fs shim
|
const fs = require("fs");
|
||||||
|
|
||||||
|
const CORE_PLUGINS_PATH = ".obsidian/core-plugins.json";
|
||||||
|
|
||||||
|
// Reads core-plugins.json via the fs shim. When headless sync is active,
|
||||||
|
// the shim patches sync: false, so this returns false. When the flag is
|
||||||
|
// cleared (user action), this returns the real value.
|
||||||
function isCoreSyncEnabled() {
|
function isCoreSyncEnabled() {
|
||||||
try {
|
try {
|
||||||
const data = fs.readFileSync(".obsidian/core-plugins.json", "utf-8");
|
const data = fs.readFileSync(CORE_PLUGINS_PATH, "utf-8");
|
||||||
const config = JSON.parse(data);
|
const config = JSON.parse(data);
|
||||||
return config.sync === true;
|
return config.sync === true;
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -27,11 +32,31 @@ function showConflictWarning(title, message) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function startCoreSyncWatcher(plugin, api, wsListener) {
|
function startCoreSyncGuard(plugin, api, wsListener) {
|
||||||
|
const app = plugin.app;
|
||||||
|
const vaultId = app.vault.getName();
|
||||||
|
|
||||||
|
// Monkey-patch syncPlugin.enable() to clear the shim flag before
|
||||||
|
// Obsidian writes core-plugins.json. This ensures the read transform
|
||||||
|
// doesn't block a user-initiated core sync enable.
|
||||||
|
const syncPlugin = app.internalPlugins.getPluginById("sync");
|
||||||
|
let origEnable = null;
|
||||||
|
|
||||||
|
if (syncPlugin) {
|
||||||
|
origEnable = syncPlugin.enable.bind(syncPlugin);
|
||||||
|
|
||||||
|
syncPlugin.enable = function (...args) {
|
||||||
|
window.__ignisHeadlessSyncActive = false;
|
||||||
|
api.stopSync(vaultId).catch(() => {});
|
||||||
|
return origEnable(...args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch for core-plugins.json changes via WebSocket.
|
||||||
let wasEnabled = isCoreSyncEnabled();
|
let wasEnabled = isCoreSyncEnabled();
|
||||||
|
|
||||||
const rawHandler = (msg) => {
|
const rawHandler = (msg) => {
|
||||||
if (msg.type === "modified" && msg.path === ".obsidian/core-plugins.json") {
|
if (msg.type === "modified" && msg.path === CORE_PLUGINS_PATH) {
|
||||||
handleCoreSyncChange();
|
handleCoreSyncChange();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -42,9 +67,6 @@ function startCoreSyncWatcher(plugin, api, wsListener) {
|
||||||
const enabled = isCoreSyncEnabled();
|
const enabled = isCoreSyncEnabled();
|
||||||
|
|
||||||
if (enabled && !wasEnabled) {
|
if (enabled && !wasEnabled) {
|
||||||
const vaultId = plugin.app.vault.getName();
|
|
||||||
|
|
||||||
api.stopSync(vaultId).catch(() => {});
|
|
||||||
showConflictWarning(
|
showConflictWarning(
|
||||||
"Headless Sync Stopped",
|
"Headless Sync Stopped",
|
||||||
"Obsidian Sync has been enabled. Headless Sync has been automatically " +
|
"Obsidian Sync has been enabled. Headless Sync has been automatically " +
|
||||||
|
|
@ -59,11 +81,15 @@ function startCoreSyncWatcher(plugin, api, wsListener) {
|
||||||
return {
|
return {
|
||||||
cleanup() {
|
cleanup() {
|
||||||
wsListener.offRaw();
|
wsListener.offRaw();
|
||||||
|
|
||||||
|
if (syncPlugin && origEnable) {
|
||||||
|
syncPlugin.enable = origEnable;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
isCoreSyncEnabled,
|
isCoreSyncEnabled,
|
||||||
startCoreSyncWatcher,
|
startCoreSyncGuard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ const { Plugin } = require("obsidian");
|
||||||
const { HeadlessSyncSettingTab } = require("./settings-tab");
|
const { HeadlessSyncSettingTab } = require("./settings-tab");
|
||||||
const { WsListener } = require("./ws-listener");
|
const { WsListener } = require("./ws-listener");
|
||||||
const { initSyncStatusBar } = require("./sync-status-bar");
|
const { initSyncStatusBar } = require("./sync-status-bar");
|
||||||
const { startCoreSyncWatcher } = require("./core-sync-guard");
|
const { startCoreSyncGuard } = require("./core-sync-guard");
|
||||||
const api = require("./api");
|
const api = require("./api");
|
||||||
|
|
||||||
class IgnisHeadlessSyncPlugin extends Plugin {
|
class IgnisHeadlessSyncPlugin extends Plugin {
|
||||||
|
|
@ -14,7 +14,7 @@ class IgnisHeadlessSyncPlugin extends Plugin {
|
||||||
|
|
||||||
this.addSettingTab(new HeadlessSyncSettingTab(this.app, this));
|
this.addSettingTab(new HeadlessSyncSettingTab(this.app, this));
|
||||||
|
|
||||||
this._coreSyncWatcher = startCoreSyncWatcher(this, api, this.wsListener);
|
this._coreSyncGuard = startCoreSyncGuard(this, api, this.wsListener);
|
||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "start-sync",
|
id: "start-sync",
|
||||||
|
|
@ -53,9 +53,11 @@ class IgnisHeadlessSyncPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
onunload() {
|
onunload() {
|
||||||
if (this._coreSyncWatcher) {
|
window.__ignisHeadlessSyncActive = false;
|
||||||
this._coreSyncWatcher.cleanup();
|
|
||||||
this._coreSyncWatcher = null;
|
if (this._coreSyncGuard) {
|
||||||
|
this._coreSyncGuard.cleanup();
|
||||||
|
this._coreSyncGuard = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._syncStatusBarCleanup) {
|
if (this._syncStatusBarCleanup) {
|
||||||
|
|
|
||||||
|
|
@ -124,12 +124,6 @@ class SyncManager {
|
||||||
throw new Error(`No sync configuration for vault: ${vaultId}`);
|
throw new Error(`No sync configuration for vault: ${vaultId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isCoreSyncEnabled(state.vaultPath)) {
|
|
||||||
const msg = `Cannot start sync for ${vaultId}: Obsidian Sync core plugin is enabled`;
|
|
||||||
this.ctx.log(msg);
|
|
||||||
throw new Error(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.status === "running") {
|
if (state.status === "running") {
|
||||||
this.ctx.log(`Sync already running for ${vaultId}`);
|
this.ctx.log(`Sync already running for ${vaultId}`);
|
||||||
return this.getState(vaultId);
|
return this.getState(vaultId);
|
||||||
|
|
@ -316,31 +310,11 @@ class SyncManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isCoreSyncEnabled(vaultPath) {
|
|
||||||
try {
|
|
||||||
const configPath = path.join(vaultPath, ".obsidian", "core-plugins.json");
|
|
||||||
const data = fs.readFileSync(configPath, "utf-8");
|
|
||||||
const config = JSON.parse(data);
|
|
||||||
return config.sync === true;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
autoStartAll() {
|
autoStartAll() {
|
||||||
let started = 0;
|
let started = 0;
|
||||||
let skipped = 0;
|
|
||||||
|
|
||||||
for (const [vaultId, state] of this.states) {
|
for (const [vaultId, state] of this.states) {
|
||||||
if (state.autoStart && state.status === "stopped") {
|
if (state.autoStart && state.status === "stopped") {
|
||||||
if (this.isCoreSyncEnabled(state.vaultPath)) {
|
|
||||||
this.ctx.log(
|
|
||||||
`Skipping auto-start for ${vaultId}: Obsidian Sync core plugin is enabled`,
|
|
||||||
);
|
|
||||||
skipped++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.startSync(vaultId);
|
this.startSync(vaultId);
|
||||||
started++;
|
started++;
|
||||||
|
|
@ -353,12 +327,6 @@ class SyncManager {
|
||||||
if (started > 0) {
|
if (started > 0) {
|
||||||
this.ctx.log(`Auto-started sync for ${started} vault(s)`);
|
this.ctx.log(`Auto-started sync for ${started} vault(s)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skipped > 0) {
|
|
||||||
this.ctx.log(
|
|
||||||
`Skipped ${skipped} vault(s) due to Obsidian Sync being enabled`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async shutdown() {
|
async shutdown() {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { createFsWatch } from "./watch.js";
|
||||||
import { createWatcherClient } from "./watcher-client.js";
|
import { createWatcherClient } from "./watcher-client.js";
|
||||||
import { createFdOps } from "./fd.js";
|
import { createFdOps } from "./fd.js";
|
||||||
import { constants } from "./constants.js";
|
import { constants } from "./constants.js";
|
||||||
|
import { registerReadTransform, removeReadTransform } from "./read-transforms.js";
|
||||||
|
|
||||||
const metadataCache = new MetadataCache();
|
const metadataCache = new MetadataCache();
|
||||||
const contentCache = new ContentCache();
|
const contentCache = new ContentCache();
|
||||||
|
|
@ -43,6 +44,8 @@ export const fsShim = {
|
||||||
_metadataCache: metadataCache,
|
_metadataCache: metadataCache,
|
||||||
_contentCache: contentCache,
|
_contentCache: contentCache,
|
||||||
_watcherClient: watcherClient,
|
_watcherClient: watcherClient,
|
||||||
|
_registerReadTransform: registerReadTransform,
|
||||||
|
_removeReadTransform: removeReadTransform,
|
||||||
|
|
||||||
async _init(basePath) {
|
async _init(basePath) {
|
||||||
const tree = await transport.fetchTree(basePath);
|
const tree = await transport.fetchTree(basePath);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { markLocalOp } from "./echo-guard.js";
|
import { markLocalOp } from "./echo-guard.js";
|
||||||
import { isInputCachePath, inputCacheGet } from "./input-cache.js";
|
import { isInputCachePath, inputCacheGet } from "./input-cache.js";
|
||||||
|
import { applyReadTransform } from "./read-transforms.js";
|
||||||
|
|
||||||
export function createFsPromises(metadataCache, contentCache, transport) {
|
export function createFsPromises(metadataCache, contentCache, transport) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -46,60 +47,52 @@ export function createFsPromises(metadataCache, contentCache, transport) {
|
||||||
|
|
||||||
const wantText = encoding === "utf8" || encoding === "utf-8";
|
const wantText = encoding === "utf8" || encoding === "utf-8";
|
||||||
|
|
||||||
|
let result = null;
|
||||||
|
|
||||||
// Check input cache for files picked via browser file dialogs.
|
// Check input cache for files picked via browser file dialogs.
|
||||||
if (isInputCachePath(path)) {
|
if (isInputCachePath(path)) {
|
||||||
const inputData = inputCacheGet(path);
|
result = inputCacheGet(path);
|
||||||
|
|
||||||
if (inputData !== null) {
|
|
||||||
if (wantText) {
|
|
||||||
return typeof inputData === "string"
|
|
||||||
? inputData
|
|
||||||
: new TextDecoder().decode(inputData);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof inputData === "string") {
|
|
||||||
return new TextEncoder().encode(inputData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return inputData;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const meta = metadataCache.get(path);
|
if (result === null) {
|
||||||
if (meta && meta.type === "directory") {
|
const meta = metadataCache.get(path);
|
||||||
const e = new Error("EISDIR: illegal operation on a directory, read");
|
|
||||||
e.code = "EISDIR";
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!meta && path) {
|
if (meta && meta.type === "directory") {
|
||||||
const e = new Error(
|
const e = new Error("EISDIR: illegal operation on a directory, read");
|
||||||
`ENOENT: no such file or directory, open '${path}'`,
|
e.code = "EISDIR";
|
||||||
);
|
throw e;
|
||||||
e.code = "ENOENT";
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cached = contentCache.get(path);
|
|
||||||
|
|
||||||
if (cached !== null) {
|
|
||||||
if (wantText) {
|
|
||||||
return typeof cached === "string"
|
|
||||||
? cached
|
|
||||||
: new TextDecoder().decode(cached);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// binary. ensure we return a proper Uint8Array with .buffer
|
if (!meta && path) {
|
||||||
if (typeof cached === "string") {
|
const e = new Error(
|
||||||
return new TextEncoder().encode(cached);
|
`ENOENT: no such file or directory, open '${path}'`,
|
||||||
|
);
|
||||||
|
e.code = "ENOENT";
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cached;
|
result = contentCache.get(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await transport.readFile(path, encoding);
|
if (result === null) {
|
||||||
contentCache.set(path, data);
|
result = await transport.readFile(path, encoding);
|
||||||
return data;
|
contentCache.set(path, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply registered read transforms (e.g., patching synced config files).
|
||||||
|
result = applyReadTransform(path, result);
|
||||||
|
|
||||||
|
if (wantText) {
|
||||||
|
return typeof result === "string"
|
||||||
|
? result
|
||||||
|
: new TextDecoder().decode(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof result === "string") {
|
||||||
|
return new TextEncoder().encode(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
async writeFile(path, data, encoding) {
|
async writeFile(path, data, encoding) {
|
||||||
|
|
|
||||||
49
src/shims/fs/read-transforms.js
Normal file
49
src/shims/fs/read-transforms.js
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Post-read transforms for specific file paths.
|
||||||
|
// Allows patching file content after reading but before returning to the caller.
|
||||||
|
// Used to prevent synced config files from activating conflicting features.
|
||||||
|
|
||||||
|
const transforms = new Map();
|
||||||
|
|
||||||
|
function normalize(p) {
|
||||||
|
return (p || "")
|
||||||
|
.replace(/\\/g, "/")
|
||||||
|
.replace(/^\/+/, "")
|
||||||
|
.replace(/\/+$/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerReadTransform(path, fn) {
|
||||||
|
transforms.set(normalize(path), fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeReadTransform(path) {
|
||||||
|
transforms.delete(normalize(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyReadTransform(path, data) {
|
||||||
|
const norm = normalize(path);
|
||||||
|
const fn = transforms.get(norm);
|
||||||
|
|
||||||
|
if (!fn) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = fn(data);
|
||||||
|
|
||||||
|
if (result !== data) {
|
||||||
|
const before = typeof data === "string" ? data : new TextDecoder().decode(data);
|
||||||
|
const after = typeof result === "string" ? result : new TextDecoder().decode(result);
|
||||||
|
console.log(`[shim:fs] Read transform applied: ${norm}`);
|
||||||
|
console.log(`[shim:fs] before:`, before);
|
||||||
|
console.log(`[shim:fs] after:`, after);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasReadTransform(path) {
|
||||||
|
return transforms.has(normalize(path));
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { markLocalOp } from "./echo-guard.js";
|
import { markLocalOp } from "./echo-guard.js";
|
||||||
import { isInputCachePath, inputCacheGet } from "./input-cache.js";
|
import { isInputCachePath, inputCacheGet } from "./input-cache.js";
|
||||||
|
import { applyReadTransform } from "./read-transforms.js";
|
||||||
|
|
||||||
export function createFsSync(metadataCache, contentCache, transport) {
|
export function createFsSync(metadataCache, contentCache, transport) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -58,6 +59,8 @@ export function createFsSync(metadataCache, contentCache, transport) {
|
||||||
encoding = encoding?.encoding;
|
encoding = encoding?.encoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const wantText = encoding === "utf8" || encoding === "utf-8";
|
||||||
|
|
||||||
const meta = metadataCache.get(path);
|
const meta = metadataCache.get(path);
|
||||||
if (meta && meta.type === "directory") {
|
if (meta && meta.type === "directory") {
|
||||||
const e = new Error("EISDIR: illegal operation on a directory, read");
|
const e = new Error("EISDIR: illegal operation on a directory, read");
|
||||||
|
|
@ -65,39 +68,37 @@ export function createFsSync(metadataCache, contentCache, transport) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let result = null;
|
||||||
|
|
||||||
// Check input cache for files picked via browser file dialogs.
|
// Check input cache for files picked via browser file dialogs.
|
||||||
// These never hit the server; they exist only in browser memory.
|
|
||||||
if (isInputCachePath(path)) {
|
if (isInputCachePath(path)) {
|
||||||
const inputData = inputCacheGet(path);
|
const inputData = inputCacheGet(path);
|
||||||
|
|
||||||
if (inputData !== null) {
|
if (inputData !== null) {
|
||||||
if (encoding === "utf8" || encoding === "utf-8") {
|
result = inputData;
|
||||||
return typeof inputData === "string"
|
|
||||||
? inputData
|
|
||||||
: new TextDecoder().decode(inputData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return inputData;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cached = contentCache.get(path);
|
if (result === null) {
|
||||||
if (cached !== null) {
|
result = contentCache.get(path);
|
||||||
if (encoding === "utf8" || encoding === "utf-8") {
|
|
||||||
return typeof cached === "string"
|
|
||||||
? cached
|
|
||||||
: new TextDecoder().decode(cached);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cached;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.warn("[shim:fs] readFileSync cache miss, using sync XHR:", path);
|
if (result === null) {
|
||||||
|
console.warn("[shim:fs] readFileSync cache miss, using sync XHR:", path);
|
||||||
|
result = transport.readFileSync(path, encoding);
|
||||||
|
contentCache.set(path, result);
|
||||||
|
}
|
||||||
|
|
||||||
const data = transport.readFileSync(path, encoding);
|
// Apply registered read transforms (e.g., patching synced config files).
|
||||||
contentCache.set(path, data);
|
result = applyReadTransform(path, result);
|
||||||
|
|
||||||
return data;
|
if (wantText) {
|
||||||
|
return typeof result === "string"
|
||||||
|
? result
|
||||||
|
: new TextDecoder().decode(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
writeFileSync(path, data, encoding) {
|
writeFileSync(path, data, encoding) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { fsShim } from "./fs/index.js";
|
||||||
import { installRequestUrlShim } from "./request-url.js";
|
import { installRequestUrlShim } from "./request-url.js";
|
||||||
import { vaultService } from "../services/vault-service.js";
|
import { vaultService } from "../services/vault-service.js";
|
||||||
import { showPluginInstallDialog } from "../ui/bootstrap.js";
|
import { showPluginInstallDialog } from "../ui/bootstrap.js";
|
||||||
|
import { registerReadTransform } from "./fs/read-transforms.js";
|
||||||
|
|
||||||
function resolveVaultId() {
|
function resolveVaultId() {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
|
@ -107,11 +108,71 @@ function initPluginPrompt() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if headless sync is active, we transform reads of core-plugins.json to hide the sync setting from Obsidian.
|
||||||
|
// this prevents headless sync from being disabled as a result of a different device syncing "Active core plugins list".
|
||||||
|
// i.e ensure Ignis always has sync: false if headless sync is active.
|
||||||
|
// This may be somewhat overengineered. Could revisit later.
|
||||||
|
function initCoreSyncGuard() {
|
||||||
|
const vaultId = window.__currentVaultId;
|
||||||
|
|
||||||
|
if (!vaultId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
xhr.open("GET", "/api/plugins", false);
|
||||||
|
xhr.send();
|
||||||
|
|
||||||
|
if (xhr.status !== 200) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const plugins = JSON.parse(xhr.responseText);
|
||||||
|
const headlessSync = plugins.find(
|
||||||
|
(p) => p.id === "headless-sync" && p.bundledPluginId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!headlessSync || !headlessSync.enabledVaults.includes(vaultId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"[ignis] Headless sync active for this vault, patching core-plugins.json reads",
|
||||||
|
);
|
||||||
|
window.__ignisHeadlessSyncActive = true;
|
||||||
|
|
||||||
|
registerReadTransform(".obsidian/core-plugins.json", (data) => {
|
||||||
|
if (!window.__ignisHeadlessSyncActive) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
let text =
|
||||||
|
typeof data === "string" ? data : new TextDecoder().decode(data);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const config = JSON.parse(text);
|
||||||
|
|
||||||
|
if (config.sync === true) {
|
||||||
|
config.sync = false;
|
||||||
|
return JSON.stringify(config);
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("[ignis] Failed to init core sync guard:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function initialize() {
|
export function initialize() {
|
||||||
resolveVaultId();
|
resolveVaultId();
|
||||||
initVaultConfig();
|
initVaultConfig();
|
||||||
initVaultList();
|
initVaultList();
|
||||||
initMetadataCache();
|
initMetadataCache();
|
||||||
|
initCoreSyncGuard();
|
||||||
installRequestUrlShim();
|
installRequestUrlShim();
|
||||||
initPluginPrompt();
|
initPluginPrompt();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue