Compare commits

...

2 Commits

Author SHA1 Message Date
vorotamoroz
ecec546f13 Improvements:
- Show sync status information inside the editor.

Fixed:
- Reduce the same messages on popup notifications
- show warning message when synchronization
2021-12-03 12:54:18 +09:00
vorotamoroz
4a8c76efb5 Tidy up:
- Plugin and setting table.
- Add new option showOwnPlugin.
- Change icons.
- Buttons in setting dialog.

Fixed: Could not sync the file that contains "$" in filename.
2021-11-29 16:31:29 +09:00
7 changed files with 176 additions and 29 deletions

View File

@@ -143,6 +143,11 @@ You can dump saved note structure to `Dump informations of this doc`. Replace ev
Default values are 20 letters and 250 letters.
## Miscellaneous
### Show status inside editor
Show information inside the editor pane.
It would be useful for mobile.
## Hatch
From here, everything is under the hood. Please handle it with care.

View File

@@ -142,6 +142,12 @@ Self-hosted LiveSyncは一つのチャンクのサイズを最低minimum chunk s
改行文字と#を除き、すべて●に置換しても、アルゴリズムは有効に働きます。
デフォルトは20文字と、250文字です。
## Miscellaneous
その他の設定です
### Show status inside editor
同期の情報をエディター内に表示します。
モバイルで便利です。
## Hatch
ここから先は、困ったときに開ける蓋の中身です。注意して使用してください。

139
main.ts
View File

@@ -53,6 +53,8 @@ interface ObsidianLiveSyncSettings {
batchSave: boolean;
deviceAndVaultName: string;
usePluginSettings: boolean;
showOwnPlugins: boolean;
showStatusOnEditor: boolean;
}
const DEFAULT_SETTINGS: ObsidianLiveSyncSettings = {
@@ -84,6 +86,8 @@ const DEFAULT_SETTINGS: ObsidianLiveSyncSettings = {
batchSave: false,
deviceAndVaultName: "",
usePluginSettings: false,
showOwnPlugins: false,
showStatusOnEditor: false,
};
interface Entry {
@@ -351,7 +355,7 @@ const bumpRemoteVersion = async (db: PouchDB.Database, barrier: number = VER): P
};
function isValidPath(filename: string): boolean {
let regex = /[\u0000-\u001f]|[\\"':?<>|*$]/g;
let regex = /[\u0000-\u001f]|[\\"':?<>|*]/g;
let x = filename.replace(regex, "_");
let win = /(\\|\/)(COM\d|LPT\d|CON|PRN|AUX|NUL|CLOCK$)($|\.)/gi;
let sx = (x = x.replace(win, "/_"));
@@ -2002,6 +2006,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
}
addLogHook: () => void = null;
//--> Basic document Functions
notifies: { [key: string]: { notice: Notice; timer: NodeJS.Timeout; count: number } } = {};
async addLog(message: any, level: LOG_LEVEL = LOG_LEVEL.INFO) {
if (level < LOG_LEVEL.INFO && this.settings && this.settings.lessInformationInLog) {
return;
@@ -2019,8 +2024,23 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
// if (this.statusBar2 != null) {
// this.statusBar2.setText(newmessage.substring(0, 60));
// }
if (level >= LOG_LEVEL.NOTICE) {
new Notice(messagecontent);
if (messagecontent in this.notifies) {
clearTimeout(this.notifies[messagecontent].timer);
this.notifies[messagecontent].count++;
this.notifies[messagecontent].notice.setMessage(`(${this.notifies[messagecontent].count}):${messagecontent}`);
} else {
let notify = new Notice(messagecontent, 0);
this.notifies[messagecontent] = {
count: 0,
notice: notify,
timer: setTimeout(() => {
notify.hide();
delete this.notifies[messagecontent];
}, 5000),
};
}
}
if (this.addLogHook != null) this.addLogHook();
}
@@ -2239,6 +2259,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
}
this.setPeriodicSync();
}
lastMessage = "";
refreshStatusText() {
let sent = this.localDatabase.docSent;
let arrived = this.localDatabase.docArrived;
@@ -2265,9 +2286,24 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
this.statusBar.title = this.localDatabase.syncStatus;
let waiting = "";
if (this.settings.batchSave) {
waiting = " " + this.batchFileChange.map((e) => "🚀").join("");
waiting = " " + this.batchFileChange.map((e) => "🛫").join("");
waiting = waiting.replace(/🛫{10}/g, "🚀");
}
const message = `Sync:${w}${sent}${arrived}${waiting}`;
this.setStatusBarText(message);
}
setStatusBarText(message: string) {
if (this.lastMessage != message) {
this.statusBar.setText(message);
if (this.settings.showStatusOnEditor) {
const root = document.documentElement;
root.style.setProperty("--slsmessage", '"' + message + '"');
} else {
const root = document.documentElement;
root.style.setProperty("--slsmessage", '""');
}
this.lastMessage = message;
}
this.statusBar.setText(`Sync:${w}${sent}${arrived}${waiting}`);
}
async replicate(showMessage?: boolean) {
if (this.settings.versionUpFlash != "") {
@@ -2313,7 +2349,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
const syncFiles = filesStorage.filter((e) => onlyInStorageNames.indexOf(e.path) == -1);
Logger("Initialize and checking database files");
Logger("Updating database by new files");
this.statusBar.setText(`UPDATE DATABASE`);
this.setStatusBarText(`UPDATE DATABASE`);
let _this = this;
async function runAll<T>(procedurename: string, objects: T[], callback: (arg: T) => Promise<void>) {
const count = objects.length;
Logger(procedurename);
@@ -2330,7 +2367,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
if (notice != null) notice.setMessage(notify);
Logger(notify);
// lastTicks = performance.now() + 2000;
// this.statusBar.setText(notify);
_this.setStatusBarText(notify);
}
} catch (ex) {
Logger(`Error while ${procedurename}`, LOG_LEVEL.NOTICE);
@@ -2366,7 +2403,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
await runAll("CHECK FILE STATUS", syncFiles, async (e) => {
await this.syncFileBetweenDBandStorage(e, filesStorage);
});
this.statusBar.setText(`NOW TRACKING!`);
this.setStatusBarText(`NOW TRACKING!`);
Logger("Initialized,NOW TRACKING!");
if (showingNotice) {
notice.hide();
@@ -2804,6 +2841,9 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
containerEl.createEl("h2", { text: "Settings for Self-hosted LiveSync." });
containerEl.createEl("h3", { text: "Remote Database configuration" });
let syncWarn = containerEl.createEl("div", { text: "The remote configuration is locked while any synchronization is enabled." });
syncWarn.addClass("op-warn");
syncWarn.addClass("sls-hidden");
const isAnySyncEnabled = (): boolean => {
if (this.plugin.settings.liveSync) return true;
@@ -2817,10 +2857,12 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
dbsettings.forEach((e) => {
e.setDisabled(true).setTooltip("When any sync is enabled, It cound't be changed.");
});
syncWarn.removeClass("sls-hidden");
} else {
dbsettings.forEach((e) => {
e.setDisabled(false).setTooltip("");
});
syncWarn.addClass("sls-hidden");
}
if (this.plugin.settings.liveSync) {
syncNonLive.forEach((e) => {
@@ -3012,6 +3054,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
.setButtonText("Apply and send")
.setWarning()
.setDisabled(false)
.setClass("sls-btn-left")
.onClick(async () => {
await applyEncryption(true);
})
@@ -3021,6 +3064,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
.setButtonText("Apply and receive")
.setWarning()
.setDisabled(false)
.setClass("sls-btn-right")
.onClick(async () => {
await applyEncryption(false);
})
@@ -3052,6 +3096,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
if (this.plugin.settings.versionUpFlash != "") {
let c = containerEl.createEl("div", { text: this.plugin.settings.versionUpFlash });
c.createEl("button", { text: "I got it and updated." }, (e) => {
e.addClass("mod-cta");
e.addEventListener("click", async () => {
this.plugin.settings.versionUpFlash = "";
await this.plugin.saveSettings();
@@ -3209,6 +3254,17 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
text.inputEl.setAttribute("type", "number");
});
containerEl.createEl("h3", { text: "Miscellaneous" });
new Setting(containerEl)
.setName("Show status inside editor")
.setDesc("")
.addToggle((toggle) =>
toggle.setValue(this.plugin.settings.showStatusOnEditor).onChange(async (value) => {
this.plugin.settings.showStatusOnEditor = value;
await this.plugin.saveSettings();
})
);
containerEl.createEl("h3", { text: "Hatch" });
if (this.plugin.localDatabase.remoteLockedAndDeviceNotAccepted) {
@@ -3216,6 +3272,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
text: "To prevent unwanted vault corruption, the remote database has been locked for synchronization, and this device was not marked as 'resolved'. it caused by some operations like this. re-initialized. Local database initialization should be required. please back your vault up, reset local database, and press 'Mark this device as resolved'. ",
});
c.createEl("button", { text: "I'm ready, mark this device 'resolved'" }, (e) => {
e.addClass("mod-warning");
e.addEventListener("click", async () => {
await this.plugin.markRemoteResolved();
c.remove();
@@ -3228,6 +3285,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
text: "To prevent unwanted vault corruption, the remote database has been locked for synchronization. (This device is marked 'resolved') When all your devices are marked 'resolved', unlock the database.",
});
c.createEl("button", { text: "I'm ready, unlock the database" }, (e) => {
e.addClass("mod-warning");
e.addEventListener("click", async () => {
await this.plugin.markRemoteUnlocked();
c.remove();
@@ -3265,6 +3323,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
.setButtonText("Drop and send")
.setWarning()
.setDisabled(false)
.setClass("sls-btn-left")
.onClick(async () => {
await dropHistory(true);
})
@@ -3274,6 +3333,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
.setButtonText("Drop and receive")
.setWarning()
.setDisabled(false)
.setClass("sls-btn-right")
.onClick(async () => {
await dropHistory(false);
})
@@ -3286,6 +3346,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
button
.setButtonText("Lock")
.setDisabled(false)
.setWarning()
.onClick(async () => {
await this.plugin.markRemoteLocked();
})
@@ -3308,6 +3369,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
button
.setButtonText("Reset")
.setDisabled(false)
.setWarning()
.onClick(async () => {
await this.plugin.tryResetRemoteDatabase();
})
@@ -3319,6 +3381,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
button
.setButtonText("Reset")
.setDisabled(false)
.setWarning()
.onClick(async () => {
await this.plugin.resetLocalDatabase();
})
@@ -3351,6 +3414,17 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
// })
// );
new Setting(containerEl)
.setName("Show own plugins and settings")
.setDesc("Show ")
.addToggle((toggle) =>
toggle.setValue(this.plugin.settings.showOwnPlugins).onChange(async (value) => {
this.plugin.settings.showOwnPlugins = value;
await this.plugin.saveSettings();
updatePluginPane();
})
);
new Setting(containerEl)
.setName("Device and Vault name")
.setDesc("")
@@ -3439,25 +3513,26 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
let html = `
<div class='sls-plugins-wrap'>
<table class='sls-plugins-tbl'>
<tr>
<th>vault</th>
<th>plugin</th>
<th>version</th>
<th>modified</th>
<th>plugin</th>
<th>setting</th>
</tr>`;
`;
for (let vaults in plugins) {
if (vaults == this.plugin.settings.deviceAndVaultName) continue;
if (!this.plugin.settings.showOwnPlugins && vaults == this.plugin.settings.deviceAndVaultName) continue;
html += `
<tr>
<th colspan=2>${escapeStringToHTML(vaults)}</th>
</tr>`;
for (let v of plugins[vaults]) {
let mtime = v.mtime == 0 ? "-" : new Date(v.mtime).toLocaleString();
let settingApplyable: boolean | string = "-";
let settingFleshness: string = "";
let isSameVersion = false;
let isSameContents = false;
if (thisDevicePlugins[v.manifest.id]) {
if (thisDevicePlugins[v.manifest.id].manifest.version == v.manifest.version) {
isSameVersion = true;
}
if (thisDevicePlugins[v.manifest.id].styleCss == v.styleCss && thisDevicePlugins[v.manifest.id].mainJs == v.mainJs && thisDevicePlugins[v.manifest.id].manifestJson == v.manifestJson) {
isSameContents = true;
}
}
if (thisDevicePlugins[v.manifest.id] && thisDevicePlugins[v.manifest.id].dataJson && v.dataJson) {
// have this plugin.
@@ -3482,16 +3557,26 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
settingApplyable = "N/A";
}
// very ugly way.
let piece = `<tr>
<th>${escapeStringToHTML(v.deviceVaultName)}</th>
<td>${escapeStringToHTML(v.manifest.name)}</td>
<td class="tcenter">${escapeStringToHTML(v.manifest.version)}</td>
<td class="tcenter">${escapeStringToHTML(mtime)}</td>
<td class="tcenter">${isSameVersion ? "even" : "<button data-key='" + v._id + "' class='apply-plugin-version'>Use</button>"}</td>
<td class="tcenter">${settingApplyable === true ? "<button data-key='" + v._id + "' class='apply-plugin-data'>Apply (" + settingFleshness + ")</button>" : settingApplyable}</td>
</tr>`;
let piece = `
<tr class='divider'>
<th colspan=2></th>
</tr>
<tr>
<th class='sls-table-head'>${escapeStringToHTML(v.manifest.name)}</th>
<td class="sls-table-tail tcenter">${isSameContents ? "even" : `<button data-key='${v._id}' class='apply-plugin-version mod-cta'>Use (${isSameVersion ? "=" : ""}${v.manifest.version}) </button>`}</td>
</tr>
<tr>
<td class="sls-table-head tcenter">${escapeStringToHTML(mtime)}</td>
<td class="sls-table-tail tcenter">${settingApplyable === true ? "<button data-key='" + v._id + "' class='apply-plugin-data mod-cta'>Apply (" + settingFleshness + ")</button>" : settingApplyable}</td>
</tr>
`;
html += piece;
}
html += `
<tr class='divider'>
<th colspan=2></th>
</tr>
`;
}
html += "</table></div>";
pluginConfig.innerHTML = html;
@@ -3577,6 +3662,10 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
Logger("You have to encrypt the database to use plugin setting sync.", LOG_LEVEL.NOTICE);
return;
}
if (!this.plugin.settings.deviceAndVaultName) {
Logger("You have to set your device and vault name.", LOG_LEVEL.NOTICE);
return;
}
await sweepPlugin();
})
);
@@ -3594,6 +3683,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
xx.remove();
});
});
ba.addClass("mod-warning");
xx.createEl("button", { text: `Restore from file` }, (e) => {
e.addEventListener("click", async () => {
let f = await this.app.vault.getFiles().filter((e) => path2id(e.path) == k);
@@ -3605,6 +3695,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
xx.remove();
});
});
xx.addClass("mod-warning");
}
} else {
let cx = containerEl.createEl("div", { text: "There's no collupted data." });

View File

@@ -1,7 +1,7 @@
{
"id": "obsidian-livesync",
"name": "Self-hosted LiveSync",
"version": "0.1.21",
"version": "0.1.23",
"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",

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "obsidian-livesync",
"version": "0.1.21",
"version": "0.1.23",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "obsidian-livesync",
"version": "0.1.21",
"version": "0.1.23",
"license": "MIT",
"dependencies": {
"diff-match-patch": "^1.0.5",

View File

@@ -1,6 +1,6 @@
{
"name": "obsidian-livesync",
"version": "0.1.21",
"version": "0.1.23",
"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",
"scripts": {

View File

@@ -34,7 +34,52 @@
.sls-plugins-wrap {
display: flex;
flex-grow: 1;
overflow: scroll;
/* overflow: scroll; */
}
.sls-plugins-tbl {
border: 1px solid var(--background-modifier-border);
width: 100%;
}
.divider th {
border-top: 1px solid var(--background-modifier-border);
}
/* .sls-table-head{
width:50%;
}
.sls-table-tail{
width:50%;
} */
.sls-btn-left {
padding-right: 4px;
}
.sls-btn-right {
padding-left: 4px;
}
.sls-hidden {
display: none;
}
:root {
--slsmessage: "";
}
.CodeMirror-wrap::before , .cm-s-obsidian > .cm-editor::before {
content: var(--slsmessage);
position: absolute;
border-radius: 4px;
/* border:1px solid --background-modifier-border; */
display: inline-block;
top: 8px;
color: --text-normal;
opacity: 0.5;
font-size:80%;
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
}
.CodeMirror-wrap::before {
right: 0px;
} .cm-s-obsidian > .cm-editor::before {
right: 16px;
}