mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-09-01 16:27:07 +00:00
(chore): removing DOM Operation
This commit is contained in:
@@ -41,7 +41,7 @@ async function renderHistoryList(): Promise<VaultHistoryItem[]> {
|
|||||||
|
|
||||||
const [items, lastUsedId] = await Promise.all([historyStore.getVaultHistory(), historyStore.getLastUsedVaultId()]);
|
const [items, lastUsedId] = await Promise.all([historyStore.getVaultHistory(), historyStore.getLastUsedVaultId()]);
|
||||||
|
|
||||||
listEl.innerHTML = "";
|
listEl.replaceChildren();
|
||||||
emptyEl.classList.toggle("is-hidden", items.length > 0);
|
emptyEl.classList.toggle("is-hidden", items.length > 0);
|
||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
|
|||||||
+1
-1
Submodule src/lib updated: 97530553a6...00f7e65304
@@ -1,6 +1,6 @@
|
|||||||
import { TFile, Modal, App, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "../../../deps.ts";
|
import { TFile, Modal, App, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "../../../deps.ts";
|
||||||
import { getPathFromTFile, isValidPath } from "../../../common/utils.ts";
|
import { getPathFromTFile, isValidPath } from "../../../common/utils.ts";
|
||||||
import { decodeBinary, escapeStringToHTML, readString } from "../../../lib/src/string_and_binary/convert.ts";
|
import { decodeBinary, readString } from "../../../lib/src/string_and_binary/convert.ts";
|
||||||
import ObsidianLiveSyncPlugin from "../../../main.ts";
|
import ObsidianLiveSyncPlugin from "../../../main.ts";
|
||||||
import {
|
import {
|
||||||
type DocumentID,
|
type DocumentID,
|
||||||
@@ -145,22 +145,66 @@ export class DocumentHistoryModal extends Modal {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepareContentView(usePreformatted = true) {
|
||||||
|
this.contentView.empty();
|
||||||
|
this.contentView.toggleClass("op-pre", usePreformatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendTextDiff(diff: [number, string][]) {
|
||||||
|
for (const [operation, text] of diff) {
|
||||||
|
if (operation == DIFF_DELETE) {
|
||||||
|
this.contentView.createSpan({ text, cls: "history-deleted" });
|
||||||
|
} else if (operation == DIFF_EQUAL) {
|
||||||
|
this.contentView.createSpan({ text, cls: "history-normal" });
|
||||||
|
} else if (operation == DIFF_INSERT) {
|
||||||
|
this.contentView.createSpan({ text, cls: "history-added" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendImageDiff(baseSrc: string, overlaySrc?: string) {
|
||||||
|
const wrap = this.contentView.createDiv({ cls: "ls-imgdiff-wrap" });
|
||||||
|
const overlay = wrap.createDiv({ cls: "overlay" });
|
||||||
|
overlay.createEl("img", { cls: "img-base" }, (img) => {
|
||||||
|
img.src = baseSrc;
|
||||||
|
});
|
||||||
|
if (overlaySrc) {
|
||||||
|
overlay.createEl("img", { cls: "img-overlay" }, (img) => {
|
||||||
|
img.src = overlaySrc;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendDeletedNotice(usePreformatted = true) {
|
||||||
|
const notice = "(At this revision, the file has been deleted)";
|
||||||
|
if (usePreformatted) {
|
||||||
|
this.contentView.appendText(`${notice}\n`);
|
||||||
|
} else {
|
||||||
|
this.contentView.createDiv({ text: notice });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async showExactRev(rev: string) {
|
async showExactRev(rev: string) {
|
||||||
const db = this.core.localDatabase;
|
const db = this.core.localDatabase;
|
||||||
const w = await db.getDBEntry(this.file, { rev: rev }, false, false, true);
|
const w = await db.getDBEntry(this.file, { rev: rev }, false, false, true);
|
||||||
this.currentText = "";
|
this.currentText = "";
|
||||||
this.currentDeleted = false;
|
this.currentDeleted = false;
|
||||||
|
this.prepareContentView();
|
||||||
if (w === false) {
|
if (w === false) {
|
||||||
this.currentDeleted = true;
|
this.currentDeleted = true;
|
||||||
this.info.innerHTML = "";
|
this.info.empty();
|
||||||
this.contentView.innerHTML = `Could not read this revision<br>(${rev})`;
|
this.contentView.appendText("Could not read this revision");
|
||||||
|
this.contentView.createEl("br");
|
||||||
|
this.contentView.appendText(`(${rev})`);
|
||||||
} else {
|
} else {
|
||||||
this.currentDoc = w;
|
this.currentDoc = w;
|
||||||
this.info.innerHTML = `Modified:${new Date(w.mtime).toLocaleString()}`;
|
this.info.setText(`Modified:${new Date(w.mtime).toLocaleString()}`);
|
||||||
let result = undefined;
|
|
||||||
const w1data = readDocument(w);
|
const w1data = readDocument(w);
|
||||||
this.currentDeleted = !!w.deleted;
|
this.currentDeleted = !!w.deleted;
|
||||||
// this.currentText = w1data;
|
if (typeof w1data == "string") {
|
||||||
|
this.currentText = w1data;
|
||||||
|
}
|
||||||
|
let rendered = false;
|
||||||
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) {
|
||||||
@@ -168,58 +212,55 @@ export class DocumentHistoryModal extends Modal {
|
|||||||
const w2 = await db.getDBEntry(this.file, { rev: oldRev }, false, false, true);
|
const w2 = await db.getDBEntry(this.file, { rev: oldRev }, false, false, true);
|
||||||
if (w2 != false) {
|
if (w2 != false) {
|
||||||
if (typeof w1data == "string") {
|
if (typeof w1data == "string") {
|
||||||
result = "";
|
const w2data = readDocument(w2);
|
||||||
const dmp = new diff_match_patch();
|
if (typeof w2data == "string") {
|
||||||
const w2data = readDocument(w2) as string;
|
const dmp = new diff_match_patch();
|
||||||
const diff = dmp.diff_main(w2data, w1data);
|
const diff = dmp.diff_main(w2data, w1data);
|
||||||
dmp.diff_cleanupSemantic(diff);
|
dmp.diff_cleanupSemantic(diff);
|
||||||
for (const v of diff) {
|
if (this.currentDeleted) {
|
||||||
const x1 = v[0];
|
this.appendDeletedNotice();
|
||||||
const x2 = v[1];
|
|
||||||
if (x1 == DIFF_DELETE) {
|
|
||||||
result += "<span class='history-deleted'>" + escapeStringToHTML(x2) + "</span>";
|
|
||||||
} else if (x1 == DIFF_EQUAL) {
|
|
||||||
result += "<span class='history-normal'>" + escapeStringToHTML(x2) + "</span>";
|
|
||||||
} else if (x1 == DIFF_INSERT) {
|
|
||||||
result += "<span class='history-added'>" + escapeStringToHTML(x2) + "</span>";
|
|
||||||
}
|
}
|
||||||
|
this.appendTextDiff(diff);
|
||||||
|
rendered = true;
|
||||||
}
|
}
|
||||||
result = result.replace(/\n/g, "<br>");
|
|
||||||
} else if (isImage(this.file)) {
|
} else if (isImage(this.file)) {
|
||||||
const src = this.generateBlobURL("base", w1data);
|
const src = this.generateBlobURL("base", w1data);
|
||||||
const overlay = this.generateBlobURL(
|
const overlay = this.generateBlobURL(
|
||||||
"overlay",
|
"overlay",
|
||||||
readDocument(w2) as Uint8Array<ArrayBuffer>
|
readDocument(w2) as Uint8Array<ArrayBuffer>
|
||||||
);
|
);
|
||||||
result = `<div class='ls-imgdiff-wrap'>
|
this.prepareContentView(false);
|
||||||
<div class='overlay'>
|
if (this.currentDeleted) {
|
||||||
<img class='img-base' src="${src}">
|
this.appendDeletedNotice(false);
|
||||||
<img class='img-overlay' src='${overlay}'>
|
}
|
||||||
</div>
|
this.appendImageDiff(src, overlay);
|
||||||
</div>`;
|
rendered = true;
|
||||||
this.contentView.removeClass("op-pre");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result == undefined) {
|
if (!rendered) {
|
||||||
if (typeof w1data != "string") {
|
if (typeof w1data != "string") {
|
||||||
if (isImage(this.file)) {
|
if (isImage(this.file)) {
|
||||||
const src = this.generateBlobURL("base", w1data);
|
const src = this.generateBlobURL("base", w1data);
|
||||||
result = `<div class='ls-imgdiff-wrap'>
|
this.prepareContentView(false);
|
||||||
<div class='overlay'>
|
if (this.currentDeleted) {
|
||||||
<img class='img-base' src="${src}">
|
this.appendDeletedNotice(false);
|
||||||
</div>
|
}
|
||||||
</div>`;
|
this.appendImageDiff(src);
|
||||||
this.contentView.removeClass("op-pre");
|
} else {
|
||||||
|
if (this.currentDeleted) {
|
||||||
|
this.appendDeletedNotice();
|
||||||
|
}
|
||||||
|
this.contentView.appendText("Binary file");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result = escapeStringToHTML(w1data);
|
if (this.currentDeleted) {
|
||||||
|
this.appendDeletedNotice();
|
||||||
|
}
|
||||||
|
this.contentView.appendText(w1data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result == undefined) result = typeof w1data == "string" ? escapeStringToHTML(w1data) : "Binary file";
|
|
||||||
this.contentView.innerHTML =
|
|
||||||
(this.currentDeleted ? "(At this revision, the file has been deleted)\n" : "") + result;
|
|
||||||
}
|
}
|
||||||
// Reset diff navigation after content changes
|
// Reset diff navigation after content changes
|
||||||
this.resetDiffNavigation();
|
this.resetDiffNavigation();
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { App, Modal } from "../../../deps.ts";
|
import { App, Modal } from "../../../deps.ts";
|
||||||
import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT } from "diff-match-patch";
|
import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT } from "diff-match-patch";
|
||||||
import { CANCELLED, LEAVE_TO_SUBSEQUENT, type diff_result } from "../../../lib/src/common/types.ts";
|
import { CANCELLED, LEAVE_TO_SUBSEQUENT, type diff_result } from "../../../lib/src/common/types.ts";
|
||||||
import { escapeStringToHTML } from "../../../lib/src/string_and_binary/convert.ts";
|
|
||||||
import { delay } from "../../../lib/src/common/utils.ts";
|
import { delay } from "../../../lib/src/common/utils.ts";
|
||||||
import { eventHub } from "../../../common/events.ts";
|
import { eventHub } from "../../../common/events.ts";
|
||||||
import { globalSlipBoard } from "../../../lib/src/bureau/bureau.ts";
|
import { globalSlipBoard } from "../../../lib/src/bureau/bureau.ts";
|
||||||
@@ -44,6 +43,25 @@ export class ConflictResolveModal extends Modal {
|
|||||||
// sendValue("close-resolve-conflict:" + this.filename, false);
|
// sendValue("close-resolve-conflict:" + this.filename, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appendDiffFragment(container: HTMLDivElement, text: string, cls: string) {
|
||||||
|
const lines = text.split("\n");
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
const span = container.createSpan({ cls });
|
||||||
|
span.textContent = line;
|
||||||
|
if (index < lines.length - 1) {
|
||||||
|
container.createSpan({ cls: "ls-mark-cr" });
|
||||||
|
container.createEl("br");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
appendVersionInfo(container: HTMLDivElement, cls: string, name: string, date: string) {
|
||||||
|
const line = container.createSpan({ cls });
|
||||||
|
line.createSpan({ text: name, cls: "conflict-dev-name" });
|
||||||
|
line.appendText(`: ${date}`);
|
||||||
|
container.createEl("br");
|
||||||
|
}
|
||||||
|
|
||||||
override onOpen() {
|
override onOpen() {
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
// Send cancel signal for the previous merge dialogue
|
// Send cancel signal for the previous merge dialogue
|
||||||
@@ -64,25 +82,21 @@ export class ConflictResolveModal extends Modal {
|
|||||||
const div = contentEl.createDiv("");
|
const div = contentEl.createDiv("");
|
||||||
div.addClass("op-scrollable");
|
div.addClass("op-scrollable");
|
||||||
div.addClass("ls-dialog");
|
div.addClass("ls-dialog");
|
||||||
let diff = "";
|
let diffLength = 0;
|
||||||
for (const v of this.result.diff) {
|
for (const v of this.result.diff) {
|
||||||
const x1 = v[0];
|
const x1 = v[0];
|
||||||
const x2 = v[1];
|
const x2 = v[1];
|
||||||
|
diffLength += x2.length;
|
||||||
|
if (diffLength > 100 * 1024) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (x1 == DIFF_DELETE) {
|
if (x1 == DIFF_DELETE) {
|
||||||
diff +=
|
this.appendDiffFragment(div, x2, "deleted");
|
||||||
"<span class='deleted'>" +
|
div.createEl("span", { text: x2, cls: "deleted normal conflict-dev-name" });
|
||||||
escapeStringToHTML(x2).replace(/\n/g, "<span class='ls-mark-cr'></span>\n") +
|
|
||||||
"</span>";
|
|
||||||
} else if (x1 == DIFF_EQUAL) {
|
} else if (x1 == DIFF_EQUAL) {
|
||||||
diff +=
|
this.appendDiffFragment(div, x2, "normal");
|
||||||
"<span class='normal'>" +
|
|
||||||
escapeStringToHTML(x2).replace(/\n/g, "<span class='ls-mark-cr'></span>\n") +
|
|
||||||
"</span>";
|
|
||||||
} else if (x1 == DIFF_INSERT) {
|
} else if (x1 == DIFF_INSERT) {
|
||||||
diff +=
|
this.appendDiffFragment(div, x2, "added");
|
||||||
"<span class='added'>" +
|
|
||||||
escapeStringToHTML(x2).replace(/\n/g, "<span class='ls-mark-cr'></span>\n") +
|
|
||||||
"</span>";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,8 +106,8 @@ export class ConflictResolveModal extends Modal {
|
|||||||
new Date(this.result.left.mtime).toLocaleString() + (this.result.left.deleted ? " (Deleted)" : "");
|
new Date(this.result.left.mtime).toLocaleString() + (this.result.left.deleted ? " (Deleted)" : "");
|
||||||
const date2 =
|
const date2 =
|
||||||
new Date(this.result.right.mtime).toLocaleString() + (this.result.right.deleted ? " (Deleted)" : "");
|
new Date(this.result.right.mtime).toLocaleString() + (this.result.right.deleted ? " (Deleted)" : "");
|
||||||
div2.innerHTML = `<span class='deleted'><span class='conflict-dev-name'>${this.localName}</span>: ${date1}</span><br>
|
this.appendVersionInfo(div2, "deleted", this.localName, date1);
|
||||||
<span class='added'><span class='conflict-dev-name'>${this.remoteName}</span>: ${date2}</span><br>`;
|
this.appendVersionInfo(div2, "added", this.remoteName, date2);
|
||||||
contentEl.createEl("button", { text: `Use ${this.localName}` }, (e) =>
|
contentEl.createEl("button", { text: `Use ${this.localName}` }, (e) =>
|
||||||
e.addEventListener("click", () => this.sendResponse(this.result.right.rev))
|
e.addEventListener("click", () => this.sendResponse(this.result.right.rev))
|
||||||
).style.marginRight = "4px";
|
).style.marginRight = "4px";
|
||||||
@@ -108,11 +122,9 @@ export class ConflictResolveModal extends Modal {
|
|||||||
contentEl.createEl("button", { text: !this.pluginPickMode ? "Not now" : "Cancel" }, (e) =>
|
contentEl.createEl("button", { text: !this.pluginPickMode ? "Not now" : "Cancel" }, (e) =>
|
||||||
e.addEventListener("click", () => this.sendResponse(CANCELLED))
|
e.addEventListener("click", () => this.sendResponse(CANCELLED))
|
||||||
).style.marginRight = "4px";
|
).style.marginRight = "4px";
|
||||||
diff = diff.replace(/\n/g, "<br>");
|
if (diffLength > 100 * 1024) {
|
||||||
if (diff.length > 100 * 1024) {
|
div.empty();
|
||||||
div.innerText = "(Too large diff to display)";
|
div.innerText = "(Too large diff to display)";
|
||||||
} else {
|
|
||||||
div.innerHTML = diff;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,10 +43,13 @@ export function paneChangeLog(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElem
|
|||||||
// tmpDiv.addClass("sls-header-button");
|
// tmpDiv.addClass("sls-header-button");
|
||||||
tmpDiv.addClass("op-warn-info");
|
tmpDiv.addClass("op-warn-info");
|
||||||
|
|
||||||
tmpDiv.innerHTML = `<p>${$msg("obsidianLiveSyncSettingTab.msgNewVersionNote")}</p><button>${$msg("obsidianLiveSyncSettingTab.optionOkReadEverything")}</button>`;
|
tmpDiv.createEl("p", { text: $msg("obsidianLiveSyncSettingTab.msgNewVersionNote") });
|
||||||
|
const readEverythingButton = tmpDiv.createEl("button", {
|
||||||
|
text: $msg("obsidianLiveSyncSettingTab.optionOkReadEverything"),
|
||||||
|
});
|
||||||
if (lastVersion > (this.editingSettings?.lastReadUpdates || 0)) {
|
if (lastVersion > (this.editingSettings?.lastReadUpdates || 0)) {
|
||||||
const informationButtonDiv = informationDivEl.appendChild(tmpDiv);
|
const informationButtonDiv = informationDivEl.appendChild(tmpDiv);
|
||||||
informationButtonDiv.querySelector("button")?.addEventListener("click", () => {
|
readEverythingButton.addEventListener("click", () => {
|
||||||
fireAndForget(async () => {
|
fireAndForget(async () => {
|
||||||
this.editingSettings.lastReadUpdates = lastVersion;
|
this.editingSettings.lastReadUpdates = lastVersion;
|
||||||
await this.saveAllDirtySettings();
|
await this.saveAllDirtySettings();
|
||||||
|
|||||||
@@ -125,8 +125,13 @@ export function paneSetup(
|
|||||||
paneEl,
|
paneEl,
|
||||||
"div",
|
"div",
|
||||||
"",
|
"",
|
||||||
(el) =>
|
(el) => {
|
||||||
(el.innerHTML = `<a href='https://github.com/${repo}/blob/main${topPath}' target="_blank">${$msg("obsidianLiveSyncSettingTab.linkOpenInBrowser")}</a>`)
|
el.createEl("a", { text: $msg("obsidianLiveSyncSettingTab.linkOpenInBrowser") }, (anchor) => {
|
||||||
|
anchor.href = `https://github.com/${repo}/blob/main${topPath}`;
|
||||||
|
anchor.target = "_blank";
|
||||||
|
anchor.rel = "noopener";
|
||||||
|
});
|
||||||
|
}
|
||||||
);
|
);
|
||||||
const troubleShootEl = this.createEl(paneEl, "div", {
|
const troubleShootEl = this.createEl(paneEl, "div", {
|
||||||
text: "",
|
text: "",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const checkConfig = async (
|
|||||||
Logger($msg("obsidianLiveSyncSettingTab.logCheckingDbConfig"), LOG_LEVEL_INFO);
|
Logger($msg("obsidianLiveSyncSettingTab.logCheckingDbConfig"), LOG_LEVEL_INFO);
|
||||||
let isSuccessful = true;
|
let isSuccessful = true;
|
||||||
const emptyDiv = createDiv();
|
const emptyDiv = createDiv();
|
||||||
emptyDiv.innerHTML = "<span></span>";
|
emptyDiv.createSpan();
|
||||||
checkResultDiv?.replaceChildren(...[emptyDiv]);
|
checkResultDiv?.replaceChildren(...[emptyDiv]);
|
||||||
const addResult = (msg: string, classes?: string[]) => {
|
const addResult = (msg: string, classes?: string[]) => {
|
||||||
const tmpDiv = createDiv();
|
const tmpDiv = createDiv();
|
||||||
@@ -21,7 +21,7 @@ export const checkConfig = async (
|
|||||||
if (classes) {
|
if (classes) {
|
||||||
tmpDiv.addClasses(classes);
|
tmpDiv.addClasses(classes);
|
||||||
}
|
}
|
||||||
tmpDiv.innerHTML = `${msg}`;
|
tmpDiv.textContent = msg;
|
||||||
checkResultDiv?.appendChild(tmpDiv);
|
checkResultDiv?.appendChild(tmpDiv);
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
@@ -47,9 +47,10 @@ export const checkConfig = async (
|
|||||||
if (!checkResultDiv) return;
|
if (!checkResultDiv) return;
|
||||||
const tmpDiv = createDiv();
|
const tmpDiv = createDiv();
|
||||||
tmpDiv.addClass("ob-btn-config-fix");
|
tmpDiv.addClass("ob-btn-config-fix");
|
||||||
tmpDiv.innerHTML = `<label>${title}</label><button>${$msg("obsidianLiveSyncSettingTab.btnFix")}</button>`;
|
tmpDiv.createEl("label", { text: title });
|
||||||
|
const fixButton = tmpDiv.createEl("button", { text: $msg("obsidianLiveSyncSettingTab.btnFix") });
|
||||||
const x = checkResultDiv.appendChild(tmpDiv);
|
const x = checkResultDiv.appendChild(tmpDiv);
|
||||||
x.querySelector("button")?.addEventListener("click", () => {
|
fixButton.addEventListener("click", () => {
|
||||||
fireAndForget(async () => {
|
fireAndForget(async () => {
|
||||||
Logger($msg("obsidianLiveSyncSettingTab.logCouchDbConfigSet", { title, key, value }));
|
Logger($msg("obsidianLiveSyncSettingTab.logCouchDbConfigSet", { title, key, value }));
|
||||||
const res = await requestToCouchDBWithCredentials(
|
const res = await requestToCouchDBWithCredentials(
|
||||||
|
|||||||
Reference in New Issue
Block a user