Compare commits

...

13 Commits

Author SHA1 Message Date
vorotamoroz
ab6ff01f1a Bumped 2022-07-25 13:16:49 +09:00
vorotamoroz
c836953fa9 Fixed:
- Leaked prototyping code.

    New feature:
    - `Pick file to show history` shows a file list and you can choose one for showing history.
    - `Back to this revision` implemented on the Document history dialog.
2022-07-25 13:14:59 +09:00
vorotamoroz
e69371ff24 Degraded! 2022-07-25 13:05:00 +09:00
vorotamoroz
d324add086 Fixed:
- Leaked prototyping code.

New feature:
- `Pick file to show history` shows a file list and you can choose one for showing history.
- `Back to this revision` implemented on the Document history dialog.
2022-07-25 12:48:48 +09:00
vorotamoroz
0caf330f39 Merge remote-tracking branch 'origin/snyk-upgrade-52349e72cdcadcd0ba41dd967c715aa7' 2022-07-21 16:40:18 +09:00
vorotamoroz
3a147ca427 Merge branch 'snyk-upgrade-6a3786fc4cbe394542e9d1b6058c46e7' 2022-07-21 16:26:54 +09:00
vorotamoroz
8266cfba40 Merge remote-tracking branch 'origin/snyk-upgrade-d6d88a887d6bc2d451a6e9c7a7759727' 2022-07-21 16:15:35 +09:00
vorotamoroz
e2f06181fa bumped 2022-07-21 13:06:57 +09:00
vorotamoroz
bb6d787607 Imprinting version numbers to boot log. 2022-07-21 13:06:42 +09:00
vorotamoroz
cb406e2db6 New feature.
- Local database name can now be customized.
- Buttons to back skip-patterns of Hidden file sync to default.
2022-07-21 13:05:35 +09:00
snyk-bot
d5a95d43dd fix: upgrade idb from 7.0.1 to 7.0.2
Snyk has created this PR to upgrade idb from 7.0.1 to 7.0.2.

See this package in npm:
https://www.npmjs.com/package/idb

See this project in Snyk:
https://app.snyk.io/org/vrtmrz/project/d2c9b72d-6e38-433f-bbad-725719c0fa4d?utm_source=github&utm_medium=referral&page=upgrade-pr
2022-07-09 05:02:39 +00:00
snyk-bot
7da930a8bb fix: upgrade svelte-preprocess from 4.10.3 to 4.10.5
Snyk has created this PR to upgrade svelte-preprocess from 4.10.3 to 4.10.5.

See this package in npm:
https://www.npmjs.com/package/svelte-preprocess

See this project in Snyk:
https://app.snyk.io/org/vrtmrz/project/d2c9b72d-6e38-433f-bbad-725719c0fa4d?utm_source=github&utm_medium=referral&page=upgrade-pr
2022-04-29 03:54:29 +00:00
snyk-bot
a632b79726 fix: upgrade esbuild-svelte from 0.6.2 to 0.7.0
Snyk has created this PR to upgrade esbuild-svelte from 0.6.2 to 0.7.0.

See this package in npm:
https://www.npmjs.com/package/esbuild-svelte

See this project in Snyk:
https://app.snyk.io/org/vrtmrz/project/d2c9b72d-6e38-433f-bbad-725719c0fa4d?utm_source=github&utm_medium=referral&page=upgrade-pr
2022-04-29 03:54:25 +00:00
8 changed files with 221 additions and 80 deletions

View File

@@ -3,7 +3,7 @@ import process from "process";
import builtins from "builtin-modules"; import builtins from "builtin-modules";
import sveltePlugin from "esbuild-svelte"; import sveltePlugin from "esbuild-svelte";
import sveltePreprocess from "svelte-preprocess"; import sveltePreprocess from "svelte-preprocess";
import fs from "node:fs";
const banner = `/* const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin if you want to view the source, please visit the github repository of this plugin
@@ -11,7 +11,8 @@ if you want to view the source, please visit the github repository of this plugi
`; `;
const prod = process.argv[2] === "production"; const prod = process.argv[2] === "production";
const manifestJson = JSON.parse(fs.readFileSync("./manifest.json"));
const packageJson = JSON.parse(fs.readFileSync("./package.json"));
esbuild esbuild
.build({ .build({
banner: { banner: {
@@ -19,6 +20,10 @@ esbuild
}, },
entryPoints: ["src/main.ts"], entryPoints: ["src/main.ts"],
bundle: true, bundle: true,
define: {
"MANIFEST_VERSION": `"${manifestJson.version}"`,
"PACKAGE_VERSION": `"${packageJson.version}"`,
},
external: ["obsidian", "electron", ...builtins], external: ["obsidian", "electron", ...builtins],
format: "cjs", format: "cjs",
watch: !prod, watch: !prod,

View File

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

64
package-lock.json generated
View File

@@ -1,19 +1,19 @@
{ {
"name": "obsidian-livesync", "name": "obsidian-livesync",
"version": "0.12.1", "version": "0.12.3",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "obsidian-livesync", "name": "obsidian-livesync",
"version": "0.12.1", "version": "0.12.3",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"diff-match-patch": "^1.0.5", "diff-match-patch": "^1.0.5",
"esbuild": "0.13.12", "esbuild": "0.13.12",
"esbuild-svelte": "^0.6.0", "esbuild-svelte": "^0.7.0",
"idb": "^7.0.1", "idb": "^7.0.2",
"svelte-preprocess": "^4.10.2", "svelte-preprocess": "^4.10.5",
"xxhash-wasm": "^0.4.2" "xxhash-wasm": "^0.4.2"
}, },
"devDependencies": { "devDependencies": {
@@ -27,13 +27,13 @@
"@typescript-eslint/parser": "^5.0.0", "@typescript-eslint/parser": "^5.0.0",
"builtin-modules": "^3.2.0", "builtin-modules": "^3.2.0",
"esbuild": "0.13.12", "esbuild": "0.13.12",
"esbuild-svelte": "^0.6.0", "esbuild-svelte": "^0.7.0",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-airbnb-base": "^14.2.1", "eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.25.2", "eslint-plugin-import": "^2.25.2",
"obsidian": "^0.15.4", "obsidian": "^0.15.4",
"rollup": "^2.32.1", "rollup": "^2.32.1",
"svelte-preprocess": "^4.10.2", "svelte-preprocess": "^4.10.5",
"tslib": "^2.2.0", "tslib": "^2.2.0",
"typescript": "^4.2.4" "typescript": "^4.2.4"
} }
@@ -1416,18 +1416,16 @@
] ]
}, },
"node_modules/esbuild-svelte": { "node_modules/esbuild-svelte": {
"version": "0.6.2", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.6.2.tgz", "resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.7.0.tgz",
"integrity": "sha512-FRHcyaQqIm4ncFsbk97b+80fHAI0VA15Ty56zOai9zpOPOQ1kgqWJt7JYn0jNGm+1VSvJNaGUj8QB85H/P43jA==", "integrity": "sha512-hfiauhEXtGocUf7oVcxTrLhhF57ajBbvNCCClsS3KntEeITddKU+1WFhmsCt9SO0dQJlCFzJtpPu2dI7dRkXBw==",
"dev": true, "dev": true,
"dependencies": {
"svelte": "^3.46.2"
},
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
"peerDependencies": { "peerDependencies": {
"esbuild": ">=0.9.6" "esbuild": ">=0.9.6",
"svelte": ">=3.43.0"
} }
}, },
"node_modules/esbuild-windows-32": { "node_modules/esbuild-windows-32": {
@@ -2109,9 +2107,9 @@
} }
}, },
"node_modules/idb": { "node_modules/idb": {
"version": "7.0.1", "version": "7.0.2",
"resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.2.tgz",
"integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" "integrity": "sha512-jjKrT1EnyZewQ/gCBb/eyiYrhGzws2FeY92Yx8qT9S9GeQAmo4JFVIiWRIfKW/6Ob9A+UDAOW9j9jn58fy2HIg=="
}, },
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.1.9", "version": "5.1.9",
@@ -3189,14 +3187,15 @@
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.4.tgz", "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.4.tgz",
"integrity": "sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg==", "integrity": "sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg==",
"dev": true, "dev": true,
"peer": true,
"engines": { "engines": {
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/svelte-preprocess": { "node_modules/svelte-preprocess": {
"version": "4.10.3", "version": "4.10.5",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.3.tgz", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.5.tgz",
"integrity": "sha512-ttw17lJfb/dx2ZJT9sesaXT5l7mPQ9Apx1H496Kli3Hkk7orIRGpOw6rCPkRNzr6ueVPqb4vzodS5x7sBFhKHw==", "integrity": "sha512-VKXPRScCzAZqeBZOGq4LLwtNrAu++mVn7XvQox3eFDV7Ciq0Lg70Q8QWjH9iXF7J+pMlXhPsSFwpCb2E+hoeyA==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
@@ -4526,13 +4525,11 @@
"optional": true "optional": true
}, },
"esbuild-svelte": { "esbuild-svelte": {
"version": "0.6.2", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.6.2.tgz", "resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.7.0.tgz",
"integrity": "sha512-FRHcyaQqIm4ncFsbk97b+80fHAI0VA15Ty56zOai9zpOPOQ1kgqWJt7JYn0jNGm+1VSvJNaGUj8QB85H/P43jA==", "integrity": "sha512-hfiauhEXtGocUf7oVcxTrLhhF57ajBbvNCCClsS3KntEeITddKU+1WFhmsCt9SO0dQJlCFzJtpPu2dI7dRkXBw==",
"dev": true, "dev": true,
"requires": { "requires": {}
"svelte": "^3.46.2"
}
}, },
"esbuild-windows-32": { "esbuild-windows-32": {
"version": "0.13.12", "version": "0.13.12",
@@ -5052,9 +5049,9 @@
} }
}, },
"idb": { "idb": {
"version": "7.0.1", "version": "7.0.2",
"resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.2.tgz",
"integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" "integrity": "sha512-jjKrT1EnyZewQ/gCBb/eyiYrhGzws2FeY92Yx8qT9S9GeQAmo4JFVIiWRIfKW/6Ob9A+UDAOW9j9jn58fy2HIg=="
}, },
"ignore": { "ignore": {
"version": "5.1.9", "version": "5.1.9",
@@ -5827,12 +5824,13 @@
"version": "3.46.4", "version": "3.46.4",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.4.tgz", "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.4.tgz",
"integrity": "sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg==", "integrity": "sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg==",
"dev": true "dev": true,
"peer": true
}, },
"svelte-preprocess": { "svelte-preprocess": {
"version": "4.10.3", "version": "4.10.5",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.3.tgz", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.5.tgz",
"integrity": "sha512-ttw17lJfb/dx2ZJT9sesaXT5l7mPQ9Apx1H496Kli3Hkk7orIRGpOw6rCPkRNzr6ueVPqb4vzodS5x7sBFhKHw==", "integrity": "sha512-VKXPRScCzAZqeBZOGq4LLwtNrAu++mVn7XvQox3eFDV7Ciq0Lg70Q8QWjH9iXF7J+pMlXhPsSFwpCb2E+hoeyA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/pug": "^2.0.4", "@types/pug": "^2.0.4",

View File

@@ -1,6 +1,6 @@
{ {
"name": "obsidian-livesync", "name": "obsidian-livesync",
"version": "0.12.1", "version": "0.12.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",
@@ -23,22 +23,22 @@
"@typescript-eslint/parser": "^5.0.0", "@typescript-eslint/parser": "^5.0.0",
"builtin-modules": "^3.2.0", "builtin-modules": "^3.2.0",
"esbuild": "0.13.12", "esbuild": "0.13.12",
"esbuild-svelte": "^0.6.0", "esbuild-svelte": "^0.7.0",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-airbnb-base": "^14.2.1", "eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.25.2", "eslint-plugin-import": "^2.25.2",
"obsidian": "^0.15.4", "obsidian": "^0.15.4",
"rollup": "^2.32.1", "rollup": "^2.32.1",
"svelte-preprocess": "^4.10.2", "svelte-preprocess": "^4.10.5",
"tslib": "^2.2.0", "tslib": "^2.2.0",
"typescript": "^4.2.4" "typescript": "^4.2.4"
}, },
"dependencies": { "dependencies": {
"diff-match-patch": "^1.0.5", "diff-match-patch": "^1.0.5",
"esbuild": "0.13.12", "esbuild": "0.13.12",
"esbuild-svelte": "^0.6.0", "esbuild-svelte": "^0.7.0",
"idb": "^7.0.1", "svelte-preprocess": "^4.10.5",
"svelte-preprocess": "^4.10.2", "idb": "^7.0.2",
"xxhash-wasm": "^0.4.2" "xxhash-wasm": "^0.4.2"
} }
} }

View File

@@ -1,9 +1,9 @@
import { TFile, Modal, App } from "obsidian"; import { TFile, Modal, App } from "obsidian";
import { path2id } from "./utils"; import { path2id } from "./utils";
import { escapeStringToHTML } from "./lib/src/utils"; import { base64ToArrayBuffer, base64ToString, escapeStringToHTML, isValidPath } from "./lib/src/utils";
import ObsidianLiveSyncPlugin from "./main"; import ObsidianLiveSyncPlugin from "./main";
import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch"; import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
import { LOG_LEVEL } from "./lib/src/types"; import { LoadedEntry, LOG_LEVEL } from "./lib/src/types";
import { Logger } from "./lib/src/logger"; import { Logger } from "./lib/src/logger";
export class DocumentHistoryModal extends Modal { export class DocumentHistoryModal extends Modal {
@@ -17,12 +17,13 @@ export class DocumentHistoryModal extends Modal {
file: string; file: string;
revs_info: PouchDB.Core.RevisionInfo[] = []; revs_info: PouchDB.Core.RevisionInfo[] = [];
currentDoc: LoadedEntry;
currentText = ""; currentText = "";
constructor(app: App, plugin: ObsidianLiveSyncPlugin, file: TFile) { constructor(app: App, plugin: ObsidianLiveSyncPlugin, file: TFile | string) {
super(app); super(app);
this.plugin = plugin; this.plugin = plugin;
this.file = file.path; this.file = (file instanceof TFile) ? file.path : file;
if (localStorage.getItem("ols-history-highlightdiff") == "1") { if (localStorage.getItem("ols-history-highlightdiff") == "1") {
this.showDiff = true; this.showDiff = true;
} }
@@ -47,9 +48,12 @@ export class DocumentHistoryModal extends Modal {
this.info.innerHTML = ""; this.info.innerHTML = "";
this.contentView.innerHTML = `Could not read this revision<br>(${rev.rev})`; this.contentView.innerHTML = `Could not read this revision<br>(${rev.rev})`;
} else { } else {
this.currentDoc = w;
this.info.innerHTML = `Modified:${new Date(w.mtime).toLocaleString()}`; this.info.innerHTML = `Modified:${new Date(w.mtime).toLocaleString()}`;
let result = ""; let result = "";
this.currentText = w.data; const w1data = w.datatype == "plain" ? w.data : base64ToString(w.data);
this.currentText = w1data;
if (this.showDiff) { if (this.showDiff) {
const prevRevIdx = this.revs_info.length - 1 - ((this.range.value as any) / 1 - 1); const prevRevIdx = this.revs_info.length - 1 - ((this.range.value as any) / 1 - 1);
if (prevRevIdx >= 0 && prevRevIdx < this.revs_info.length) { if (prevRevIdx >= 0 && prevRevIdx < this.revs_info.length) {
@@ -57,7 +61,8 @@ export class DocumentHistoryModal extends Modal {
const w2 = await db.getDBEntry(path2id(this.file), { rev: oldRev }, false, false); const w2 = await db.getDBEntry(path2id(this.file), { rev: oldRev }, false, false);
if (w2 != false) { if (w2 != false) {
const dmp = new diff_match_patch(); const dmp = new diff_match_patch();
const diff = dmp.diff_main(w2.data, w.data); const w2data = w2.datatype == "plain" ? w2.data : base64ToString(w2.data);
const diff = dmp.diff_main(w2data, w1data);
dmp.diff_cleanupSemantic(diff); dmp.diff_cleanupSemantic(diff);
for (const v of diff) { for (const v of diff) {
const x1 = v[0]; const x1 = v[0];
@@ -73,13 +78,13 @@ export class DocumentHistoryModal extends Modal {
result = result.replace(/\n/g, "<br>"); result = result.replace(/\n/g, "<br>");
} else { } else {
result = escapeStringToHTML(w.data); result = escapeStringToHTML(w1data);
} }
} else { } else {
result = escapeStringToHTML(w.data); result = escapeStringToHTML(w1data);
} }
} else { } else {
result = escapeStringToHTML(w.data); result = escapeStringToHTML(w1data);
} }
this.contentView.innerHTML = result; this.contentView.innerHTML = result;
} }
@@ -138,6 +143,25 @@ export class DocumentHistoryModal extends Modal {
Logger(`Old content copied to clipboard`, LOG_LEVEL.NOTICE); Logger(`Old content copied to clipboard`, LOG_LEVEL.NOTICE);
}); });
}); });
buttons.createEl("button", { text: "Back to this revision" }, (e) => {
e.addClass("mod-cta");
e.addEventListener("click", async () => {
const pathToWrite = this.file.startsWith("i:") ? this.file.substring("i:".length) : this.file;
if (!isValidPath(pathToWrite)) {
Logger("Path is not vaild to write content.", LOG_LEVEL.INFO);
}
if (this.currentDoc?.datatype == "plain") {
await this.app.vault.adapter.write(pathToWrite, this.currentDoc.data);
this.close();
} else if (this.currentDoc?.datatype == "newnote") {
await this.app.vault.adapter.writeBinary(pathToWrite, base64ToArrayBuffer(this.currentDoc.data));
this.close();
} else {
Logger(`Could not parse entry`, LOG_LEVEL.NOTICE);
}
});
});
} }
onClose() { onClose() {
const { contentEl } = this; const { contentEl } = this;

View File

@@ -1,4 +1,4 @@
import { App, PluginSettingTab, Setting, sanitizeHTMLToDom, RequestUrlParam, requestUrl } from "obsidian"; import { App, PluginSettingTab, Setting, sanitizeHTMLToDom, RequestUrlParam, requestUrl, TextAreaComponent } from "obsidian";
import { EntryDoc, LOG_LEVEL, RemoteDBSettings } from "./lib/src/types"; import { EntryDoc, LOG_LEVEL, RemoteDBSettings } from "./lib/src/types";
import { path2id, id2path } from "./utils"; import { path2id, id2path } from "./utils";
import { delay, runWithLock } from "./lib/src/utils"; import { delay, runWithLock } from "./lib/src/utils";
@@ -591,6 +591,31 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
}); });
text.inputEl.setAttribute("type", "number"); text.inputEl.setAttribute("type", "number");
}); });
let newDatabaseName = this.plugin.settings.additionalSuffixOfDatabaseName + "";
new Setting(containerLocalDatabaseEl)
.setName("Database suffix")
.setDesc("Set unique name for using same vault name on different directory.")
.addText((text) => {
text.setPlaceholder("")
.setValue(newDatabaseName)
.onChange((value) => {
newDatabaseName = value;
});
}).addButton((button) => {
button.setButtonText("Change")
.onClick(async () => {
if (this.plugin.settings.additionalSuffixOfDatabaseName == newDatabaseName) {
Logger("Suffix was not changed.", LOG_LEVEL.NOTICE);
return;
}
this.plugin.settings.additionalSuffixOfDatabaseName = newDatabaseName;
await this.plugin.saveSettings();
Logger("Suffix has been changed. Reopening database...", LOG_LEVEL.NOTICE);
await this.plugin.initializeDatabase();
})
})
addScreenElement("10", containerLocalDatabaseEl); addScreenElement("10", containerLocalDatabaseEl);
const containerGeneralSettingsEl = containerEl.createDiv(); const containerGeneralSettingsEl = containerEl.createDiv();
@@ -787,6 +812,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
); );
new Setting(containerSyncSettingEl) new Setting(containerSyncSettingEl)
.setName("Scan hidden files periodicaly.") .setName("Scan hidden files periodicaly.")
.setDesc("Seconds, zero to disable.")
.addText((text) => { .addText((text) => {
text.setPlaceholder("") text.setPlaceholder("")
.setValue(this.plugin.settings.syncInternalFilesInterval + "") .setValue(this.plugin.settings.syncInternalFilesInterval + "")
@@ -800,12 +826,15 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
}); });
text.inputEl.setAttribute("type", "number"); text.inputEl.setAttribute("type", "number");
}); });
let skipPatternTextArea: TextAreaComponent = null;
const defaultSkipPattern = "\\/node_modules\\/, \\/\\.git\\/, \\/obsidian-livesync\\/";
const defaultSkipPatternXPlat = defaultSkipPattern + ",\\/workspace$";
new Setting(containerSyncSettingEl) new Setting(containerSyncSettingEl)
.setName("Skip patterns") .setName("Skip patterns")
.setDesc( .setDesc(
"Regular expression" "Regular expression, If you use hidden file sync between desktop and mobile, adding `workspace$` is recommended."
) )
.addTextArea((text) => .addTextArea((text) => {
text text
.setValue(this.plugin.settings.syncInternalFilesIgnorePatterns) .setValue(this.plugin.settings.syncInternalFilesIgnorePatterns)
.setPlaceholder("\\/node_modules\\/, \\/\\.git\\/") .setPlaceholder("\\/node_modules\\/, \\/\\.git\\/")
@@ -813,7 +842,27 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
this.plugin.settings.syncInternalFilesIgnorePatterns = value; this.plugin.settings.syncInternalFilesIgnorePatterns = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}) })
skipPatternTextArea = text;
return text;
}
); );
new Setting(containerSyncSettingEl)
.setName("Skip patterns defaults")
.addButton((button) => {
button.setButtonText("Default")
.onClick(async () => {
skipPatternTextArea.setValue(defaultSkipPattern);
this.plugin.settings.syncInternalFilesIgnorePatterns = defaultSkipPattern;
await this.plugin.saveSettings();
})
}).addButton((button) => {
button.setButtonText("Cross-platform")
.onClick(async () => {
skipPatternTextArea.setValue(defaultSkipPatternXPlat);
this.plugin.settings.syncInternalFilesIgnorePatterns = defaultSkipPatternXPlat;
await this.plugin.saveSettings();
})
})
new Setting(containerSyncSettingEl) new Setting(containerSyncSettingEl)
.setName("Touch hidden files") .setName("Touch hidden files")

Submodule src/lib updated: 1f67fb604c...1133f82732

View File

@@ -29,12 +29,14 @@ import { DocumentHistoryModal } from "./DocumentHistoryModal";
//@ts-ignore //@ts-ignore
import PluginPane from "./PluginPane.svelte"; import PluginPane from "./PluginPane.svelte";
import { clearAllPeriodic, clearAllTriggers, disposeMemoObject, id2path, memoIfNotExist, memoObject, path2id, retriveMemoObject, setTrigger } from "./utils"; import { clearAllPeriodic, clearAllTriggers, disposeMemoObject, id2path, memoIfNotExist, memoObject, path2id, retriveMemoObject, setTrigger } from "./utils";
import { decrypt, encrypt } from "./lib/src/e2ee_v2"; import { decrypt, encrypt } from "./lib/src/e2ee_v2";
const isDebug = false; const isDebug = false;
setNoticeClass(Notice); setNoticeClass(Notice);
class PluginDialogModal extends Modal { class PluginDialogModal extends Modal {
plugin: ObsidianLiveSyncPlugin; plugin: ObsidianLiveSyncPlugin;
logEl: HTMLDivElement; logEl: HTMLDivElement;
@@ -118,19 +120,24 @@ class InputStringDialog extends Modal {
} }
} }
} }
class PopoverYesNo extends FuzzySuggestModal<string> { class PopoverSelectString extends FuzzySuggestModal<string> {
app: App; app: App;
callback: (e: string) => void = () => { }; callback: (e: string) => void = () => { };
getItemsFun: () => string[] = () => {
return ["yes", "no"];
constructor(app: App, note: string, callback: (e: string) => void) { }
constructor(app: App, note: string, placeholder: string | null, getItemsFun: () => string[], callback: (e: string) => void) {
super(app); super(app);
this.app = app; this.app = app;
this.setPlaceholder("y/n) " + note); this.setPlaceholder(placeholder ?? "y/n) " + note);
if (getItemsFun) this.getItemsFun = getItemsFun;
this.callback = callback; this.callback = callback;
} }
getItems(): string[] { getItems(): string[] {
return ["yes", "no"]; return this.getItemsFun();
} }
getItemText(item: string): string { getItemText(item: string): string {
@@ -153,11 +160,20 @@ class PopoverYesNo extends FuzzySuggestModal<string> {
const askYesNo = (app: App, message: string): Promise<"yes" | "no"> => { const askYesNo = (app: App, message: string): Promise<"yes" | "no"> => {
return new Promise((res) => { return new Promise((res) => {
const popover = new PopoverYesNo(app, message, (result) => res(result as "yes" | "no")); const popover = new PopoverSelectString(app, message, null, null, (result) => res(result as "yes" | "no"));
popover.open(); popover.open();
}); });
}; };
const askSelectString = (app: App, message: string, items: string[]): Promise<string> => {
const getItemsFun = () => items;
return new Promise((res) => {
const popover = new PopoverSelectString(app, message, "Select file)", getItemsFun, (result) => res(result));
popover.open();
});
};
const askString = (app: App, title: string, key: string, placeholder: string): Promise<string | false> => { const askString = (app: App, title: string, key: string, placeholder: string): Promise<string | false> => {
return new Promise((res) => { return new Promise((res) => {
const dialog = new InputStringDialog(app, title, key, placeholder, (result) => res(result)); const dialog = new InputStringDialog(app, title, key, placeholder, (result) => res(result));
@@ -189,6 +205,10 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
deviceAndVaultName: string; deviceAndVaultName: string;
isMobile = false; isMobile = false;
getVaultName(): string {
return this.app.vault.getName() + (this.settings?.additionalSuffixOfDatabaseName ? ("-" + this.settings.additionalSuffixOfDatabaseName) : "");
}
setInterval(handler: () => any, timeout?: number): number { setInterval(handler: () => any, timeout?: number): number {
const timer = window.setInterval(handler, timeout); const timer = window.setInterval(handler, timeout);
this.registerInterval(timer); this.registerInterval(timer);
@@ -203,7 +223,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
return false; return false;
} }
showHistory(file: TFile) { showHistory(file: TFile | string) {
if (!this.settings.useHistory) { if (!this.settings.useHistory) {
Logger("You have to enable Use History in misc.", LOG_LEVEL.NOTICE); Logger("You have to enable Use History in misc.", LOG_LEVEL.NOTICE);
} else { } else {
@@ -211,10 +231,38 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
} }
} }
async fileHistory() {
const pageLimit = 2500;
let nextKey = "";
const notes = [];
do {
const docs = await this.localDatabase.localDatabase.allDocs({ limit: pageLimit, startkey: nextKey, include_docs: true });
nextKey = "";
for (const row of docs.rows) {
const doc = row.doc;
nextKey = `${row.id}\u{10ffff}`;
if (!("type" in doc)) continue;
if (doc.type == "newnote" || doc.type == "plain") {
// const docId = doc._id.startsWith("i:") ? doc._id.substring("i:".length) : doc._id;
notes.push(id2path(doc._id))
}
}
} while (nextKey != "");
const target = await askSelectString(this.app, "File to view History", notes);
if (target) {
this.showHistory(target);
}
}
async onload() { async onload() {
setLogger(this.addLog.bind(this)); // Logger moved to global. setLogger(this.addLog.bind(this)); // Logger moved to global.
Logger("loading plugin"); Logger("loading plugin");
const lsname = "obsidian-live-sync-ver" + this.app.vault.getName(); //@ts-ignore
const manifestVersion = MANIFEST_VERSION || "-";
//@ts-ignore
const packageVersion = PACKAGE_VERSION || "-";
Logger(`Self-hosted LiveSync v${manifestVersion} ${packageVersion} `);
const lsname = "obsidian-live-sync-ver" + this.getVaultName();
const last_version = localStorage.getItem(lsname); const last_version = localStorage.getItem(lsname);
await this.loadSettings(); await this.loadSettings();
//@ts-ignore //@ts-ignore
@@ -296,6 +344,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
this.settings.autoSweepPlugins = false; this.settings.autoSweepPlugins = false;
this.settings.usePluginSync = false; this.settings.usePluginSync = false;
this.settings.suspendFileWatching = true; this.settings.suspendFileWatching = true;
this.settings.syncInternalFiles = false;
await this.saveSettings(); await this.saveSettings();
await this.openDatabase(); await this.openDatabase();
const warningMessage = "The red flag is raised! The whole initialize steps are skipped, and any file changes are not captured."; const warningMessage = "The red flag is raised! The whole initialize steps are skipped, and any file changes are not captured.";
@@ -489,6 +538,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
this.showHistory(view.file); this.showHistory(view.file);
}, },
}); });
this.triggerRealizeSettingSyncMode = debounce(this.triggerRealizeSettingSyncMode.bind(this), 1000); this.triggerRealizeSettingSyncMode = debounce(this.triggerRealizeSettingSyncMode.bind(this), 1000);
this.triggerCheckPluginUpdate = debounce(this.triggerCheckPluginUpdate.bind(this), 3000); this.triggerCheckPluginUpdate = debounce(this.triggerCheckPluginUpdate.bind(this), 3000);
setLockNotifier(() => { setLockNotifier(() => {
@@ -509,6 +559,14 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
this.syncInternalFilesAndDatabase("safe", true); this.syncInternalFilesAndDatabase("safe", true);
}, },
}); });
this.addCommand({
id: "livesync-filehistory",
name: "Pick file to show history",
callback: () => {
this.fileHistory();
},
})
} }
pluginDialog: PluginDialogModal = null; pluginDialog: PluginDialogModal = null;
@@ -555,7 +613,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
if (this.localDatabase != null) { if (this.localDatabase != null) {
this.localDatabase.close(); this.localDatabase.close();
} }
const vaultName = this.app.vault.getName(); const vaultName = this.getVaultName();
Logger("Open Database..."); Logger("Open Database...");
//@ts-ignore //@ts-ignore
const isMobile = this.app.isMobile; const isMobile = this.app.isMobile;
@@ -582,7 +640,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
// So, use history is always enabled. // So, use history is always enabled.
this.settings.useHistory = true; this.settings.useHistory = true;
const lsname = "obsidian-live-sync-vaultanddevicename-" + this.app.vault.getName(); const lsname = "obsidian-live-sync-vaultanddevicename-" + this.getVaultName();
if (this.settings.deviceAndVaultName != "") { if (this.settings.deviceAndVaultName != "") {
if (!localStorage.getItem(lsname)) { if (!localStorage.getItem(lsname)) {
this.deviceAndVaultName = this.settings.deviceAndVaultName; this.deviceAndVaultName = this.settings.deviceAndVaultName;
@@ -598,7 +656,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
} }
async saveSettings() { async saveSettings() {
const lsname = "obsidian-live-sync-vaultanddevicename-" + this.app.vault.getName(); const lsname = "obsidian-live-sync-vaultanddevicename-" + this.getVaultName();
localStorage.setItem(lsname, this.deviceAndVaultName || ""); localStorage.setItem(lsname, this.deviceAndVaultName || "");
await this.saveData(this.settings); await this.saveData(this.settings);
@@ -861,7 +919,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
if (this.settings && !this.settings.showVerboseLog && level == LOG_LEVEL.VERBOSE) { if (this.settings && !this.settings.showVerboseLog && level == LOG_LEVEL.VERBOSE) {
return; return;
} }
const valutName = this.app.vault.getName(); const valutName = this.getVaultName();
const timestamp = new Date().toLocaleString(); const timestamp = new Date().toLocaleString();
const messagecontent = typeof message == "string" ? message : message instanceof Error ? `${message.name}:${message.message}` : JSON.stringify(message, null, 2); const messagecontent = typeof message == "string" ? message : message instanceof Error ? `${message.name}:${message.message}` : JSON.stringify(message, null, 2);
const newmessage = timestamp + "->" + messagecontent; const newmessage = timestamp + "->" + messagecontent;
@@ -1115,11 +1173,11 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
saveQueuedFiles() { saveQueuedFiles() {
const saveData = JSON.stringify(this.queuedFiles.filter((e) => !e.done).map((e) => e.entry._id)); const saveData = JSON.stringify(this.queuedFiles.filter((e) => !e.done).map((e) => e.entry._id));
const lsname = "obsidian-livesync-queuefiles-" + this.app.vault.getName(); const lsname = "obsidian-livesync-queuefiles-" + this.getVaultName();
localStorage.setItem(lsname, saveData); localStorage.setItem(lsname, saveData);
} }
async loadQueuedFiles() { async loadQueuedFiles() {
const lsname = "obsidian-livesync-queuefiles-" + this.app.vault.getName(); const lsname = "obsidian-livesync-queuefiles-" + this.getVaultName();
const ids = JSON.parse(localStorage.getItem(lsname) || "[]") as string[]; const ids = JSON.parse(localStorage.getItem(lsname) || "[]") as string[];
const ret = await this.localDatabase.localDatabase.allDocs({ keys: ids, include_docs: true }); const ret = await this.localDatabase.localDatabase.allDocs({ keys: ids, include_docs: true });
for (const doc of ret.rows) { for (const doc of ret.rows) {
@@ -1501,7 +1559,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
Logger("Updating database by new files"); Logger("Updating database by new files");
this.setStatusBarText(`UPDATE DATABASE`); this.setStatusBarText(`UPDATE DATABASE`);
const runAll = async <T>(procedurename: string, objects: T[], callback: (arg: T) => Promise<void>) => { const runAll = async<T>(procedurename: string, objects: T[], callback: (arg: T) => Promise<void>) => {
const count = objects.length; const count = objects.length;
Logger(procedurename); Logger(procedurename);
let i = 0; let i = 0;
@@ -1537,6 +1595,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
await p.all(); await p.all();
Logger(`${procedurename} done.`); Logger(`${procedurename} done.`);
}; };
await runAll("UPDATE DATABASE", onlyInStorage, async (e) => { await runAll("UPDATE DATABASE", onlyInStorage, async (e) => {
Logger(`Update into ${e.path}`); Logger(`Update into ${e.path}`);
@@ -1565,7 +1624,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
} }
this.setStatusBarText(`NOW TRACKING!`); this.setStatusBarText(`NOW TRACKING!`);
Logger("Initialized,NOW TRACKING!"); Logger("Initialized, NOW TRACKING!");
if (!isInitialized) { if (!isInitialized) {
await (this.localDatabase.kvDB.set("initialized", true)) await (this.localDatabase.kvDB.set("initialized", true))
} }
@@ -2147,30 +2206,39 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
async getFiles( async getFiles(
path: string, path: string,
ignoreList: string[], ignoreList: string[],
filter: RegExp[] filter: RegExp[],
ignoreFilter: RegExp[],
) { ) {
const w = await this.app.vault.adapter.list(path); const w = await this.app.vault.adapter.list(path);
let files = [ let files = [
...w.files ...w.files
.filter((e) => !ignoreList.some((ee) => e.endsWith(ee))) .filter((e) => !ignoreList.some((ee) => e.endsWith(ee)))
.filter((e) => !filter || filter.some((ee) => e.match(ee))), .filter((e) => !filter || filter.some((ee) => e.match(ee)))
.filter((e) => !ignoreFilter || ignoreFilter.every((ee) => !e.match(ee))),
]; ];
L1: for (const v of w.folders) { L1: for (const v of w.folders) {
for (const ignore of ignoreList) { for (const ignore of ignoreList) {
if (v.endsWith(ignore)) { if (v.endsWith(ignore)) {
continue L1; continue L1;
} }
} }
files = files.concat(await this.getFiles(v, ignoreList, filter)); if (ignoreFilter && ignoreFilter.some(e => v.match(e))) {
continue L1;
}
files = files.concat(await this.getFiles(v, ignoreList, filter, ignoreFilter));
} }
return files; return files;
} }
async scanInternalFiles(): Promise<InternalFileInfo[]> { async scanInternalFiles(): Promise<InternalFileInfo[]> {
const ignoreFiles = ["node_modules", ".git", "obsidian-pouch"]; const ignoreFilter = this.settings.syncInternalFilesIgnorePatterns.toLocaleLowerCase()
.replace(/\n| /g, "")
.split(",").filter(e => e).map(e => new RegExp(e));
const root = this.app.vault.getRoot(); const root = this.app.vault.getRoot();
const findRoot = root.path; const findRoot = root.path;
const filenames = (await this.getFiles(findRoot, ignoreFiles, null)).filter(e => e.startsWith(".")).filter(e => !e.startsWith(".trash")); const filenames = (await this.getFiles(findRoot, [], null, ignoreFilter)).filter(e => e.startsWith(".")).filter(e => !e.startsWith(".trash"));
const files = filenames.map(async e => { const files = filenames.map(async e => {
return { return {
path: e, path: e,
@@ -2226,7 +2294,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
} }
} }
await this.localDatabase.putDBEntry(saveData, true); await this.localDatabase.putDBEntry(saveData, true);
Logger(`internal files STORAGE --> DB:${file.path}: Done`); Logger(`STORAGE --> DB:${file.path}: (hidden) Done`);
}); });
} }
@@ -2330,14 +2398,11 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
}); });
} }
async filterTargetFiles(files: InternalFileInfo[], targetFiles: string[] | false = false) { filterTargetFiles(files: InternalFileInfo[], targetFiles: string[] | false = false) {
const ignorePatterns = this.settings.syncInternalFilesIgnorePatterns.toLocaleLowerCase() const ignorePatterns = this.settings.syncInternalFilesIgnorePatterns.toLocaleLowerCase()
.replace(/\n| /g, "") .replace(/\n| /g, "")
.split(",").filter(e => e).map(e => new RegExp(e)); .split(",").filter(e => e).map(e => new RegExp(e));
// const files = await this.scanInternalFiles();
return files.filter(file => !ignorePatterns.some(e => file.path.match(e))).filter(file => !targetFiles || (targetFiles && targetFiles.indexOf(file.path) !== -1)) return files.filter(file => !ignorePatterns.some(e => file.path.match(e))).filter(file => !targetFiles || (targetFiles && targetFiles.indexOf(file.path) !== -1))
//if (ignorePatterns.some(e => filename.match(e))) continue;
//if (targetFiles !== false && targetFiles.indexOf(filename) == -1) continue;
} }
async applyMTimeToFile(file: InternalFileInfo) { async applyMTimeToFile(file: InternalFileInfo) {