mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-02-22 20:18:48 +00:00
Compare commits
9 Commits
0.25.21.be
...
0.25.22
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a93066870 | ||
|
|
3a73073505 | ||
|
|
ee0c0ee611 | ||
|
|
d7ea30e304 | ||
|
|
2b9ded60f7 | ||
|
|
40508822cf | ||
|
|
6f938d5f54 | ||
|
|
51dc44bfb0 | ||
|
|
7c4f2bf78a |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-livesync",
|
||||
"name": "Self-hosted LiveSync",
|
||||
"version": "0.25.21.beta1",
|
||||
"version": "0.25.21.beta2",
|
||||
"minAppVersion": "0.9.12",
|
||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||
"author": "vorotamoroz",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-livesync",
|
||||
"name": "Self-hosted LiveSync",
|
||||
"version": "0.25.20",
|
||||
"version": "0.25.22",
|
||||
"minAppVersion": "0.9.12",
|
||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||
"author": "vorotamoroz",
|
||||
|
||||
30
package-lock.json
generated
30
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.21b1",
|
||||
"version": "0.25.22",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.21b1",
|
||||
"version": "0.25.22",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.808.0",
|
||||
@@ -19,7 +19,7 @@
|
||||
"fflate": "^0.8.2",
|
||||
"idb": "^8.0.3",
|
||||
"minimatch": "^10.0.2",
|
||||
"octagonal-wheels": "^0.1.40",
|
||||
"octagonal-wheels": "^0.1.41",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
"trystero": "github:vrtmrz/trystero#9e892a93ec14eeb57ce806d272fbb7c3935256d8",
|
||||
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||
@@ -8605,9 +8605,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/octagonal-wheels": {
|
||||
"version": "0.1.40",
|
||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.40.tgz",
|
||||
"integrity": "sha512-qZkPnuVGCqpfLfu8xtZIxfQRVvmE5BmdzMF/rySriGi5JoctGhMNDjF0aLU/4GWUD5yW1X3io6VhJW4a7k1ieA==",
|
||||
"version": "0.1.41",
|
||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.41.tgz",
|
||||
"integrity": "sha512-cIvdXsyiSCxknyxTwGrNnDKsaYpgZdXeKAy9cXIAk2Jy7T1z6bLjU4s5z47OySNPVPSr32x5r8hSz7hAYYv7qA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"idb": "^8.0.3"
|
||||
@@ -9707,9 +9707,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@@ -17148,9 +17148,9 @@
|
||||
}
|
||||
},
|
||||
"octagonal-wheels": {
|
||||
"version": "0.1.40",
|
||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.40.tgz",
|
||||
"integrity": "sha512-qZkPnuVGCqpfLfu8xtZIxfQRVvmE5BmdzMF/rySriGi5JoctGhMNDjF0aLU/4GWUD5yW1X3io6VhJW4a7k1ieA==",
|
||||
"version": "0.1.41",
|
||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.41.tgz",
|
||||
"integrity": "sha512-cIvdXsyiSCxknyxTwGrNnDKsaYpgZdXeKAy9cXIAk2Jy7T1z6bLjU4s5z47OySNPVPSr32x5r8hSz7hAYYv7qA==",
|
||||
"requires": {
|
||||
"idb": "^8.0.3"
|
||||
}
|
||||
@@ -17902,9 +17902,9 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true
|
||||
},
|
||||
"set-cookie-parser": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.21.beta1",
|
||||
"version": "0.25.22",
|
||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||
"main": "main.js",
|
||||
"type": "module",
|
||||
@@ -93,7 +93,7 @@
|
||||
"fflate": "^0.8.2",
|
||||
"idb": "^8.0.3",
|
||||
"minimatch": "^10.0.2",
|
||||
"octagonal-wheels": "^0.1.40",
|
||||
"octagonal-wheels": "^0.1.41",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
"trystero": "github:vrtmrz/trystero#9e892a93ec14eeb57ce806d272fbb7c3935256d8",
|
||||
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||
|
||||
2
src/lib
2
src/lib
Submodule src/lib updated: 6972cf45b3...422194fb1b
@@ -94,10 +94,14 @@ export class ModuleFileHandler extends AbstractModule {
|
||||
let readFile: UXFileInfo | undefined = undefined;
|
||||
if (!shouldApplied) {
|
||||
readFile = await this.readFileFromStub(file);
|
||||
if (!readFile) {
|
||||
this._log(`File ${file.path} is not exist on the storage`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
if (await isDocContentSame(getDocDataAsArray(entry.data), readFile.body)) {
|
||||
// Timestamp is different but the content is same. therefore, two timestamps should be handled as same.
|
||||
// So, mark the changes are same.
|
||||
markChangesAreSame(file, file.stat.mtime, entry.mtime);
|
||||
markChangesAreSame(readFile, readFile.stat.mtime, entry.mtime);
|
||||
} else {
|
||||
shouldApplied = true;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export class ModuleRebuilder extends AbstractModule implements Rebuilder {
|
||||
await this.services.setting.suspendExtraSync();
|
||||
this.core.settings.isConfigured = true;
|
||||
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.setting.realiseSetting();
|
||||
await this.services.remote.markLocked();
|
||||
await this.services.remote.tryResetDatabase();
|
||||
await this.services.remote.markLocked();
|
||||
@@ -70,7 +70,7 @@ export class ModuleRebuilder extends AbstractModule implements Rebuilder {
|
||||
await this.services.setting.suspendExtraSync();
|
||||
await this.askUseNewAdapter();
|
||||
this.core.settings.isConfigured = true;
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.setting.realiseSetting();
|
||||
await this.resetLocalDatabase();
|
||||
await delay(1000);
|
||||
await this.services.databaseEvents.initialiseDatabase(true, true, true);
|
||||
@@ -181,7 +181,7 @@ export class ModuleRebuilder extends AbstractModule implements Rebuilder {
|
||||
await this.askUseNewAdapter();
|
||||
this.core.settings.isConfigured = true;
|
||||
await this.suspendReflectingDatabase();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.setting.realiseSetting();
|
||||
await this.resetLocalDatabase();
|
||||
await delay(1000);
|
||||
await this.services.database.openDatabase();
|
||||
|
||||
@@ -15,13 +15,12 @@ export class ModuleReplicatorCouchDB extends AbstractModule {
|
||||
return Promise.resolve(new LiveSyncCouchDBReplicator(this.core));
|
||||
}
|
||||
_everyAfterResumeProcess(): Promise<boolean> {
|
||||
if (!this.services.appLifecycle.isSuspended()) return Promise.resolve(true);
|
||||
if (this.services.appLifecycle.isSuspended()) return Promise.resolve(true);
|
||||
if (!this.services.appLifecycle.isReady()) return Promise.resolve(true);
|
||||
if (this.settings.remoteType != REMOTE_MINIO && this.settings.remoteType != REMOTE_P2P) {
|
||||
const LiveSyncEnabled = this.settings.liveSync;
|
||||
const continuous = LiveSyncEnabled;
|
||||
const eventualOnStart = !LiveSyncEnabled && this.settings.syncOnStart;
|
||||
|
||||
// If enabled LiveSync or on start, open replication
|
||||
if (LiveSyncEnabled || eventualOnStart) {
|
||||
// And note that we do not open the conflict detection dialogue directly during this process.
|
||||
|
||||
@@ -222,6 +222,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async readStubContent(stub: UXFileInfoStub): Promise<UXFileInfo | false> {
|
||||
const file = this.vaultAccess.getAbstractFileByPath(stub.path);
|
||||
if (!(file instanceof TFile)) {
|
||||
@@ -231,6 +232,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
|
||||
const data = await this.vaultAccess.vaultReadAuto(file);
|
||||
return {
|
||||
...stub,
|
||||
...TFileToUXFileInfoStub(file),
|
||||
body: createBlob(data),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -295,7 +295,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
||||
concurrentProcessing = Semaphore(5);
|
||||
waitedSince = new Map<FilePath | FilePathWithPrefix, number>();
|
||||
async startStandingBy(filename: FilePath) {
|
||||
// If waited, cancel previous waiting.
|
||||
// If waited, no need to start again (looping inside the function)
|
||||
await skipIfDuplicated(`storage-event-manager-${filename}`, async () => {
|
||||
Logger(`Processing ${filename}: Starting`, LOG_LEVEL_DEBUG);
|
||||
const release = await this.concurrentProcessing.acquire();
|
||||
@@ -315,6 +315,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
||||
// continue;
|
||||
// }
|
||||
const type = target.type;
|
||||
// If already cancelled by other operation, skip this.
|
||||
if (target.cancelled) {
|
||||
Logger(`Processing ${filename}: Cancelled (scheduled): ${operationType}`, LOG_LEVEL_DEBUG);
|
||||
this.cancelStandingBy(target);
|
||||
|
||||
@@ -59,7 +59,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule {
|
||||
this.settings.liveSync = true;
|
||||
this._log("LiveSync Enabled.", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.setting.realiseSetting();
|
||||
await this.services.setting.saveSettingData();
|
||||
},
|
||||
});
|
||||
@@ -74,7 +74,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule {
|
||||
this.services.appLifecycle.setSuspended(true);
|
||||
this._log("Self-hosted LiveSync suspended", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.setting.realiseSetting();
|
||||
await this.services.setting.saveSettingData();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -85,17 +85,15 @@ export class ConflictResolveModal extends Modal {
|
||||
}
|
||||
}
|
||||
|
||||
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)" : "");
|
||||
div2.innerHTML = `
|
||||
div2.setHTMLUnsafe(`
|
||||
<span class='deleted'><span class='conflict-dev-name'>${this.localName}</span>: ${date1}</span><br>
|
||||
<span class='added'><span class='conflict-dev-name'>${this.remoteName}</span>: ${date2}</span><br>
|
||||
`;
|
||||
`);
|
||||
contentEl.createEl("button", { text: `Use ${this.localName}` }, (e) =>
|
||||
e.addEventListener("click", () => this.sendResponse(this.result.right.rev))
|
||||
).style.marginRight = "4px";
|
||||
@@ -110,6 +108,13 @@ export class ConflictResolveModal extends Modal {
|
||||
contentEl.createEl("button", { text: !this.pluginPickMode ? "Not now" : "Cancel" }, (e) =>
|
||||
e.addEventListener("click", () => this.sendResponse(CANCELLED))
|
||||
).style.marginRight = "4px";
|
||||
diff = diff.replace(/\n/g, "<br>");
|
||||
// div.innerHTML = diff;
|
||||
if (diff.length > 100 * 1024) {
|
||||
div.setText("(Too large diff to display)");
|
||||
} else {
|
||||
div.setHTMLUnsafe(diff);
|
||||
}
|
||||
}
|
||||
|
||||
sendResponse(result: MergeDialogResult) {
|
||||
|
||||
@@ -105,7 +105,7 @@ export function paneSyncSettings(
|
||||
if (!this.editingSettings.isConfigured) {
|
||||
this.editingSettings.isConfigured = true;
|
||||
await this.saveAllDirtySettings();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.setting.realiseSetting();
|
||||
await this.rebuildDB("localOnly");
|
||||
// this.resetEditingSettings();
|
||||
if (
|
||||
@@ -124,13 +124,13 @@ export function paneSyncSettings(
|
||||
await this.confirmRebuild();
|
||||
} else {
|
||||
await this.saveAllDirtySettings();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.setting.realiseSetting();
|
||||
this.services.appLifecycle.askRestart();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await this.saveAllDirtySettings();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.setting.realiseSetting();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -169,7 +169,7 @@ export function paneSyncSettings(
|
||||
}
|
||||
await this.saveSettings(["liveSync", "periodicReplication"]);
|
||||
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.setting.realiseSetting();
|
||||
});
|
||||
|
||||
new Setting(paneEl)
|
||||
|
||||
@@ -48,7 +48,7 @@ export class ModuleLiveSyncMain extends AbstractModule {
|
||||
}
|
||||
if (!(await this.core.services.appLifecycle.onFirstInitialise())) return false;
|
||||
// await this.core.$$realizeSettingSyncMode();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.setting.realiseSetting();
|
||||
fireAndForget(async () => {
|
||||
this._log($msg("moduleLiveSyncMain.logAdditionalSafetyScan"), LOG_LEVEL_VERBOSE);
|
||||
if (!(await this.services.appLifecycle.onScanningStartupIssues())) {
|
||||
@@ -67,7 +67,7 @@ export class ModuleLiveSyncMain extends AbstractModule {
|
||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||
});
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
||||
fireAndForget(() => this.core.services.setting.onRealiseSetting());
|
||||
fireAndForget(() => this.core.services.setting.realiseSetting());
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
79
updates.md
79
updates.md
@@ -1,12 +1,64 @@
|
||||
## 0.25
|
||||
# 0.25
|
||||
|
||||
Since 19th July, 2025 (beta1 in 0.25.0-beta1, 13th July, 2025)
|
||||
|
||||
After reading Issue #668, I conducted another self-review of the E2EE-related code. In retrospect, it was clearly written by someone inexperienced, which is understandable, but it is still rather embarrassing. Three years is certainly enough time for growth.
|
||||
The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). Because 0.25 got a lot of updates, thankfully, compatibility is kept and we do not need breaking changes! In other words, when get enough stabled. The next version will be v1.0.0. Even though it my hope.
|
||||
|
||||
I have now rewritten the E2EE code to be more robust and easier to understand. It is significantly more readable and should be easier to maintain in the future. The performance issue, previously considered a concern, has been addressed by introducing a master key and deriving keys using HKDF. This approach is both fast and robust, and it provides protection against rainbow table attacks. (In addition, this implementation has been [a dedicated package on the npm registry](https://github.com/vrtmrz/octagonal-wheels), and tested in 100% branch-coverage).
|
||||
## 0.25.22
|
||||
|
||||
As a result, this is the first time in a while that forward compatibility has been broken. We have also taken the opportunity to change all metadata to use encryption rather than obfuscation. Furthermore, the `Dynamic Iteration Count` setting is now redundant and has been moved to the `Patches` pane in the settings. Thanks to Rabin-Karp, the eden setting is also no longer necessary and has been relocated accordingly. Therefore, v0.25.0 represents a legitimate and correct evolution.
|
||||
15th October, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a bug that caused wrong event bindings and flag inversion (#727)
|
||||
- This caused following issues:
|
||||
- In some cases, settings changes were not applied or saved correctly.
|
||||
- Automatic synchronisation did not begin correctly.
|
||||
|
||||
### Improved
|
||||
- Too large diffs are not shown in the file comparison view, due to performance reasons.
|
||||
|
||||
### Notes
|
||||
- The checking algorithm implemented in 0.25.20 is also raised as PR (#237). And completely I merged it manually.
|
||||
- Sorry for lacking merging this PR, and let me say thanks to the great contribution, @bioluks !
|
||||
- Known issues:
|
||||
- Sync on Editor save seems not to work correctly in some cases.
|
||||
- I am investigating this issue. If you have any information, please let me know.
|
||||
|
||||
## 0.25.21
|
||||
|
||||
13th October, 2025
|
||||
|
||||
This release including 0.25.21.beta1 and 0.25.21.beta2.
|
||||
|
||||
Apologies for taking a little time. I was seriously tackling this.
|
||||
(Of course, being caught up in an unfamiliar structure due to personnel changes on my workplace played a part, but fortunately I have returned to a place where I can do research and development rather than production. Completely beside the point, though).
|
||||
Now then, this time, moving away from 'convention over configuration', I have changed to a mechanism for manually binding events. This makes it much easier to leverage IDE assistance.
|
||||
And, also, we are ready to separate `Features` and `APIs` from `Module`. Features are still in the module, but APIs will be moved to a Service layer. This will make it easier to maintain and extend the codebase in the future.
|
||||
|
||||
If you have found any issues, please let me know. I am now on the following:
|
||||
- GitHub [Issues](https://github.com/vrtmrz/obsidian-livesync/issues) Excellent! May the other contributors will help you too.
|
||||
- Twitter [@vorotamoroz](https://twitter.com/vorotamoroz) Quickest!
|
||||
- Matrix [@vrtmrz:matrix.org](https://matrix.to/#/@vrtmrz:matrix.org) Also quick, and if you need to keep it private!
|
||||
I am creating rooms too, but I'm struggling to figure out how to use them effectively because I cannot tell the difference of use-case between them and discussions. However, if you want to use Discord, this is a answer; We should on E2E encrypted platform.
|
||||
## 0.25.21.beta2
|
||||
|
||||
8th October, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed wrong event type bindings (which caused some events not to be handled correctly).
|
||||
- Fixed detected a timing issue in StorageEventManager
|
||||
- When multiple events for the same file are fired in quick succession, metadata has been kept older information. This induces unexpected wrong notifications and write prevention.
|
||||
|
||||
## 0.25.21.beta1
|
||||
|
||||
6th October, 2025
|
||||
|
||||
### Refactored
|
||||
|
||||
- Event handling now does not rely on 'convention over configuration'.
|
||||
- Services.ts now have a proper event handler registration system.
|
||||
|
||||
## 0.25.20
|
||||
|
||||
@@ -75,24 +127,5 @@ As a result, this is the first time in a while that forward compatibility has be
|
||||
- These features use a patch that has not been incorporated upstream.
|
||||
- This patch is available at [vrtmrz/trystero](https://github.com/vrtmrz/trystero).
|
||||
|
||||
## 0.25.15
|
||||
|
||||
3rd September, 2025
|
||||
|
||||
### Improved
|
||||
|
||||
- Now we can configure `forcePathStyle` for bucket synchronisation (#707).
|
||||
|
||||
## 0.25.14
|
||||
|
||||
2nd September, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- Opening IndexedDB handling has been ensured.
|
||||
- Migration check of corrupted files detection has been fixed.
|
||||
- Now informs us about conflicted files as non-recoverable, but noted so.
|
||||
- No longer errors on not-found files.
|
||||
|
||||
Older notes are in
|
||||
[updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md).
|
||||
|
||||
@@ -10,6 +10,25 @@ As a result, this is the first time in a while that forward compatibility has be
|
||||
|
||||
---
|
||||
|
||||
## 0.25.15
|
||||
|
||||
3rd September, 2025
|
||||
|
||||
### Improved
|
||||
|
||||
- Now we can configure `forcePathStyle` for bucket synchronisation (#707).
|
||||
|
||||
## 0.25.14
|
||||
|
||||
2nd September, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- Opening IndexedDB handling has been ensured.
|
||||
- Migration check of corrupted files detection has been fixed.
|
||||
- Now informs us about conflicted files as non-recoverable, but noted so.
|
||||
- No longer errors on not-found files.
|
||||
|
||||
## 0.25.13
|
||||
|
||||
1st September, 2025
|
||||
|
||||
Reference in New Issue
Block a user