mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-04 23:01:51 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93e7cbb133 | ||
|
|
716ae32e02 | ||
|
|
d6d8cbcf5a | ||
|
|
efd348b266 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.22.2",
|
"version": "0.22.3",
|
||||||
"minAppVersion": "0.9.12",
|
"minAppVersion": "0.9.12",
|
||||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"author": "vorotamoroz",
|
"author": "vorotamoroz",
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.22.1",
|
"version": "0.22.3",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.22.1",
|
"version": "0.22.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.22.2",
|
"version": "0.22.3",
|
||||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Notice, type PluginManifest, parseYaml, normalizePath } from "./deps";
|
|||||||
import type { EntryDoc, LoadedEntry, InternalFileEntry, FilePathWithPrefix, FilePath, DocumentID, AnyEntry, SavingEntry } from "./lib/src/types";
|
import type { EntryDoc, LoadedEntry, InternalFileEntry, FilePathWithPrefix, FilePath, DocumentID, AnyEntry, SavingEntry } from "./lib/src/types";
|
||||||
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, MODE_SELECTIVE } from "./lib/src/types";
|
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, MODE_SELECTIVE } from "./lib/src/types";
|
||||||
import { ICXHeader, PERIODIC_PLUGIN_SWEEP, } from "./types";
|
import { ICXHeader, PERIODIC_PLUGIN_SWEEP, } from "./types";
|
||||||
import { createTextBlob, delay, getDocData } from "./lib/src/utils";
|
import { createTextBlob, delay, getDocData, sendSignal, waitForSignal } from "./lib/src/utils";
|
||||||
import { Logger } from "./lib/src/logger";
|
import { Logger } from "./lib/src/logger";
|
||||||
import { WrappedNotice } from "./lib/src/wrapper";
|
import { WrappedNotice } from "./lib/src/wrapper";
|
||||||
import { readString, decodeBinary, arrayBufferToBase64, sha1 } from "./lib/src/strbin";
|
import { readString, decodeBinary, arrayBufferToBase64, sha1 } from "./lib/src/strbin";
|
||||||
@@ -335,7 +335,9 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
return;
|
return;
|
||||||
}, { suspended: true, batchSize: 1, concurrentLimit: 5, delay: 300, yieldThreshold: 10 }).pipeTo(
|
}, { suspended: true, batchSize: 1, concurrentLimit: 5, delay: 300, yieldThreshold: 10 }).pipeTo(
|
||||||
new QueueProcessor(
|
new QueueProcessor(
|
||||||
(pluginDataList) => {
|
async (pluginDataList) => {
|
||||||
|
// Concurrency is two, therefore, we can unlock the previous awaiting.
|
||||||
|
sendSignal("plugin-next-load");
|
||||||
let newList = [...this.pluginList];
|
let newList = [...this.pluginList];
|
||||||
for (const item of pluginDataList) {
|
for (const item of pluginDataList) {
|
||||||
newList = newList.filter(x => x.documentPath != item.documentPath);
|
newList = newList.filter(x => x.documentPath != item.documentPath);
|
||||||
@@ -343,9 +345,13 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
}
|
}
|
||||||
this.pluginList = newList;
|
this.pluginList = newList;
|
||||||
pluginList.set(newList);
|
pluginList.set(newList);
|
||||||
|
if (pluginDataList.length != 10) {
|
||||||
|
// If the queue is going to be empty, await subsequent for a while.
|
||||||
|
await waitForSignal("plugin-next-load", 1000);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
, { suspended: true, batchSize: 1000, concurrentLimit: 10, delay: 200, yieldThreshold: 25, totalRemainingReactiveSource: pluginScanningCount })).startPipeline().root.onIdle(() => {
|
, { suspended: true, batchSize: 10, concurrentLimit: 2, delay: 250, yieldThreshold: 25, totalRemainingReactiveSource: pluginScanningCount })).startPipeline().root.onIdle(() => {
|
||||||
Logger(`All files enumerated`, LOG_LEVEL_INFO, "get-plugins");
|
Logger(`All files enumerated`, LOG_LEVEL_INFO, "get-plugins");
|
||||||
this.createMissingConfigurationEntry();
|
this.createMissingConfigurationEntry();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Logger } from "./lib/src/logger";
|
|||||||
import { isPlainText, shouldBeIgnored } from "./lib/src/path";
|
import { isPlainText, shouldBeIgnored } from "./lib/src/path";
|
||||||
import type { KeyedQueueProcessor } from "./lib/src/processor";
|
import type { KeyedQueueProcessor } from "./lib/src/processor";
|
||||||
import { LOG_LEVEL_NOTICE, type FilePath, type ObsidianLiveSyncSettings } from "./lib/src/types";
|
import { LOG_LEVEL_NOTICE, type FilePath, type ObsidianLiveSyncSettings } from "./lib/src/types";
|
||||||
|
import { delay } from "./lib/src/utils";
|
||||||
import { type FileEventItem, type FileEventType, type FileInfo, type InternalFileInfo } from "./types";
|
import { type FileEventItem, type FileEventType, type FileInfo, type InternalFileInfo } from "./types";
|
||||||
|
|
||||||
|
|
||||||
@@ -110,6 +111,8 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
|||||||
let cache: null | string | ArrayBuffer;
|
let cache: null | string | ArrayBuffer;
|
||||||
// new file or something changed, cache the changes.
|
// new file or something changed, cache the changes.
|
||||||
if (file instanceof TFile && (type == "CREATE" || type == "CHANGED")) {
|
if (file instanceof TFile && (type == "CREATE" || type == "CHANGED")) {
|
||||||
|
// Wait for a bit while to let the writer has marked `touched` at the file.
|
||||||
|
await delay(10);
|
||||||
if (this.plugin.vaultAccess.recentlyTouched(file)) {
|
if (this.plugin.vaultAccess.recentlyTouched(file)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/lib
2
src/lib
Submodule src/lib updated: ee376a80a5...dc9cbfe007
44
src/main.ts
44
src/main.ts
@@ -1010,8 +1010,8 @@ Note: We can always able to read V1 format. It will be progressively converted.
|
|||||||
|
|
||||||
async checkAndApplySettingFromMarkdown(filename: string, automated?: boolean) {
|
async checkAndApplySettingFromMarkdown(filename: string, automated?: boolean) {
|
||||||
if (automated && !this.settings.notifyAllSettingSyncFile) {
|
if (automated && !this.settings.notifyAllSettingSyncFile) {
|
||||||
if (this.settings.settingSyncFile != filename) {
|
if (!this.settings.settingSyncFile || this.settings.settingSyncFile != filename) {
|
||||||
Logger(`Setting file (${filename}) is not matched to the current configuration. skipped.`, LOG_LEVEL_INFO);
|
Logger(`Setting file (${filename}) is not matched to the current configuration. skipped.`, LOG_LEVEL_VERBOSE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1291,10 +1291,11 @@ We can perform a command in this file.
|
|||||||
}
|
}
|
||||||
|
|
||||||
pendingFileEventCount = reactiveSource(0);
|
pendingFileEventCount = reactiveSource(0);
|
||||||
|
processingFileEventCount = reactiveSource(0);
|
||||||
fileEventQueue =
|
fileEventQueue =
|
||||||
new KeyedQueueProcessor(
|
new KeyedQueueProcessor(
|
||||||
(items: FileEventItem[]) => this.handleFileEvent(items[0]),
|
(items: FileEventItem[]) => this.handleFileEvent(items[0]),
|
||||||
{ suspended: true, batchSize: 1, concurrentLimit: 5, delay: 100, yieldThreshold: FileWatchEventQueueMax, totalRemainingReactiveSource: this.pendingFileEventCount }
|
{ 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));
|
||||||
|
|
||||||
|
|
||||||
@@ -1468,20 +1469,25 @@ We can perform a command in this file.
|
|||||||
// let performPullFileAgain = false;
|
// let performPullFileAgain = false;
|
||||||
if (existDoc && existDoc._conflicts) {
|
if (existDoc && existDoc._conflicts) {
|
||||||
if (this.settings.writeDocumentsIfConflicted) {
|
if (this.settings.writeDocumentsIfConflicted) {
|
||||||
Logger(`Processing: ${file.path}: Conflicted revision has been deleted, but there were more conflicts. `, LOG_LEVEL_INFO);
|
Logger(`Processing: ${path}: Conflicted revision has been deleted, but there were more conflicts. `, LOG_LEVEL_INFO);
|
||||||
await this.processEntryDoc(docEntry, file, true);
|
await this.processEntryDoc(docEntry, file, true);
|
||||||
return;
|
return;
|
||||||
} else if (force != true) {
|
} else if (force != true) {
|
||||||
Logger(`Processing: ${file.path}: Conflicted revision has been deleted, but there were more conflicts...`);
|
Logger(`Processing: ${path}: Conflicted revision has been deleted, but there were more conflicts...`);
|
||||||
this.queueConflictCheck(file);
|
this.queueConflictCheck(path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If there are no conflicts, or forced to overwrite.
|
// If there are no conflicts, or forced to overwrite.
|
||||||
|
|
||||||
if (docEntry._deleted || docEntry.deleted || existDoc === false) {
|
if (docEntry._deleted || docEntry.deleted || existDoc === false) {
|
||||||
if (path != file.path) {
|
if (!file) {
|
||||||
Logger(`delete skipped: ${file.path} :Not exactly matched`, LOG_LEVEL_VERBOSE);
|
Logger(`delete skipped: ${path} :Already not exist on storage`, LOG_LEVEL_VERBOSE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (file.path != path) {
|
||||||
|
Logger(`delete skipped: ${path} :Not exactly matched`, LOG_LEVEL_VERBOSE);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (existDoc === false) {
|
if (existDoc === false) {
|
||||||
await this.deleteVaultItem(file);
|
await this.deleteVaultItem(file);
|
||||||
@@ -1494,15 +1500,7 @@ We can perform a command in this file.
|
|||||||
const localMtime = ~~((file?.stat?.mtime || 0) / 1000);
|
const localMtime = ~~((file?.stat?.mtime || 0) / 1000);
|
||||||
const docMtime = ~~(docEntry.mtime / 1000);
|
const docMtime = ~~(docEntry.mtime / 1000);
|
||||||
|
|
||||||
// const doc = await this.localDatabase.getDBEntry(path, { rev: docEntry._rev });
|
|
||||||
// if (doc === false) return;
|
|
||||||
const doc = existDoc;
|
const doc = existDoc;
|
||||||
// if (doc === false) {
|
|
||||||
// // The latest file
|
|
||||||
// await this.pullFile(path, null, force);
|
|
||||||
// // Logger(`delete skipped: ${file.path} :Not exactly matched`, LOG_LEVEL_VERBOSE);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (doc.datatype != "newnote" && doc.datatype != "plain") {
|
if (doc.datatype != "newnote" && doc.datatype != "plain") {
|
||||||
Logger(msg + "ERROR, Invalid datatype: " + path + "(" + doc.datatype + ")", LOG_LEVEL_NOTICE);
|
Logger(msg + "ERROR, Invalid datatype: " + path + "(" + doc.datatype + ")", LOG_LEVEL_NOTICE);
|
||||||
@@ -1518,7 +1516,7 @@ We can perform a command in this file.
|
|||||||
try {
|
try {
|
||||||
let outFile;
|
let outFile;
|
||||||
let isChanged = true;
|
let isChanged = true;
|
||||||
if (mode == "create") {
|
if (!file) {
|
||||||
const normalizedPath = normalizePath(path);
|
const normalizedPath = normalizePath(path);
|
||||||
await this.vaultAccess.vaultCreate(normalizedPath, writeData, { ctime: doc.ctime, mtime: doc.mtime, });
|
await this.vaultAccess.vaultCreate(normalizedPath, writeData, { ctime: doc.ctime, mtime: doc.mtime, });
|
||||||
outFile = this.vaultAccess.getAbstractFileByPath(normalizedPath) as TFile;
|
outFile = this.vaultAccess.getAbstractFileByPath(normalizedPath) as TFile;
|
||||||
@@ -1764,7 +1762,7 @@ We can perform a command in this file.
|
|||||||
})
|
})
|
||||||
const waitingLabel = reactive(() => {
|
const waitingLabel = reactive(() => {
|
||||||
const e = this.pendingFileEventCount.value;
|
const e = this.pendingFileEventCount.value;
|
||||||
const proc = this.fileEventQueue.processingEntities;
|
const proc = this.processingFileEventCount.value;
|
||||||
const pend = e - proc;
|
const pend = e - proc;
|
||||||
const labelProc = proc != 0 ? `⏳${proc} ` : "";
|
const labelProc = proc != 0 ? `⏳${proc} ` : "";
|
||||||
const labelPend = pend != 0 ? ` 🛫${pend}` : "";
|
const labelPend = pend != 0 ? ` 🛫${pend}` : "";
|
||||||
@@ -1805,13 +1803,19 @@ We can perform a command in this file.
|
|||||||
const newLog = log;
|
const newLog = log;
|
||||||
// scheduleTask("update-display", 50, () => {
|
// scheduleTask("update-display", 50, () => {
|
||||||
this.statusBar?.setText(newMsg.split("\n")[0]);
|
this.statusBar?.setText(newMsg.split("\n")[0]);
|
||||||
|
const selector = `.CodeMirror-wrap,` +
|
||||||
|
`.markdown-preview-view.cm-s-obsidian,` +
|
||||||
|
`.markdown-source-view.cm-s-obsidian,` +
|
||||||
|
`.canvas-wrapper,` +
|
||||||
|
`.empty-state`
|
||||||
|
;
|
||||||
if (this.settings.showStatusOnEditor) {
|
if (this.settings.showStatusOnEditor) {
|
||||||
const root = activeDocument.documentElement;
|
const root = activeDocument.documentElement;
|
||||||
const q = root.querySelectorAll(`.CodeMirror-wrap,.cm-s-obsidian>.cm-editor,.canvas-wrapper`);
|
const q = root.querySelectorAll(selector);
|
||||||
q.forEach(e => e.setAttr("data-log", '' + (newMsg + "\n" + newLog) + ''))
|
q.forEach(e => e.setAttr("data-log", '' + (newMsg + "\n" + newLog) + ''))
|
||||||
} else {
|
} else {
|
||||||
const root = activeDocument.documentElement;
|
const root = activeDocument.documentElement;
|
||||||
const q = root.querySelectorAll(`.CodeMirror-wrap,.cm-s-obsidian>.cm-editor,.canvas-wrapper`);
|
const q = root.querySelectorAll(selector);
|
||||||
q.forEach(e => e.setAttr("data-log", ''))
|
q.forEach(e => e.setAttr("data-log", ''))
|
||||||
}
|
}
|
||||||
// }, true);
|
// }, true);
|
||||||
|
|||||||
21
styles.css
21
styles.css
@@ -98,8 +98,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror-wrap::before,
|
.CodeMirror-wrap::before,
|
||||||
.cm-s-obsidian > .cm-editor::before,
|
.markdown-preview-view.cm-s-obsidian::before,
|
||||||
.canvas-wrapper::before {
|
.markdown-source-view.cm-s-obsidian::before,
|
||||||
|
.canvas-wrapper::before,
|
||||||
|
.empty-state::before {
|
||||||
content: attr(data-log);
|
content: attr(data-log);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
@@ -115,6 +117,19 @@
|
|||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-state::before,
|
||||||
|
.markdown-preview-view.cm-s-obsidian::before,
|
||||||
|
.markdown-source-view.cm-s-obsidian::before {
|
||||||
|
top: var(--header-height);
|
||||||
|
right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-mobile .empty-state::before,
|
||||||
|
.is-mobile .markdown-preview-view.cm-s-obsidian::before,
|
||||||
|
.is-mobile .markdown-source-view.cm-s-obsidian::before {
|
||||||
|
top: var(--view-header-height);
|
||||||
|
right: 1em;
|
||||||
|
}
|
||||||
.canvas-wrapper::before {
|
.canvas-wrapper::before {
|
||||||
right: 48px;
|
right: 48px;
|
||||||
}
|
}
|
||||||
@@ -292,7 +307,7 @@ span.ls-mark-cr::after {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
animation: ls-blink-diff 0.5s cubic-bezier(0.4, 0, 1, 1) infinite alternate;
|
animation: ls-blink-diff 0.5s cubic-bezier(0.4, 0, 1, 1) infinite alternate;
|
||||||
}
|
}
|
||||||
@keyframes ls-blink-diff {
|
@keyframes ls-blink-diff {
|
||||||
0% {
|
0% {
|
||||||
|
|||||||
12
updates.md
12
updates.md
@@ -10,6 +10,18 @@ Note: we got a very performance improvement.
|
|||||||
Note at 0.22.2: **Now, to rescue mobile devices, Maximum file size is set to 50 by default**. Please configure the limit as you need. If you do not want to limit the sizes, set zero manually, please.
|
Note at 0.22.2: **Now, to rescue mobile devices, Maximum file size is set to 50 by default**. Please configure the limit as you need. If you do not want to limit the sizes, set zero manually, please.
|
||||||
|
|
||||||
#### Version history
|
#### Version history
|
||||||
|
- 0.22.3
|
||||||
|
- Fixed:
|
||||||
|
- No longer detects storage changes which have been caused by Self-hosted LiveSync itself.
|
||||||
|
- Setting sync file will be detected only if it has been configured now.
|
||||||
|
- And its log will be shown only while the verbose log is enabled.
|
||||||
|
- Customisation file enumeration has got less blingy.
|
||||||
|
- Deletion of files is now reliably synchronised.
|
||||||
|
- Fixed and improved:
|
||||||
|
- In-editor-status is now shown in the following areas:
|
||||||
|
- Note editing pane (Source mode and live-preview mode).
|
||||||
|
- New tab pane.
|
||||||
|
- Canvas pane.
|
||||||
- 0.22.2
|
- 0.22.2
|
||||||
- Fixed:
|
- Fixed:
|
||||||
- Now the results of resolving conflicts are surely synchronised.
|
- Now the results of resolving conflicts are surely synchronised.
|
||||||
|
|||||||
Reference in New Issue
Block a user