mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-17 00:16:06 +03:00
feat: add diff block navigation to Document History modal
Add prev/next buttons to jump between diff blocks in the Document History view. Includes position indicator and auto-scroll with visual focus highlighting.
This commit is contained in:
@@ -66,6 +66,11 @@ export class DocumentHistoryModal extends Modal {
|
|||||||
currentDeleted = false;
|
currentDeleted = false;
|
||||||
initialRev?: string;
|
initialRev?: string;
|
||||||
|
|
||||||
|
// Diff navigation state
|
||||||
|
currentDiffIndex = -1;
|
||||||
|
diffNavContainer!: HTMLDivElement;
|
||||||
|
diffNavIndicator!: HTMLSpanElement;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
app: App,
|
app: App,
|
||||||
core: LiveSyncBaseCore,
|
core: LiveSyncBaseCore,
|
||||||
@@ -216,6 +221,61 @@ export class DocumentHistoryModal extends Modal {
|
|||||||
this.contentView.innerHTML =
|
this.contentView.innerHTML =
|
||||||
(this.currentDeleted ? "(At this revision, the file has been deleted)\n" : "") + result;
|
(this.currentDeleted ? "(At this revision, the file has been deleted)\n" : "") + result;
|
||||||
}
|
}
|
||||||
|
// Reset diff navigation after content changes
|
||||||
|
this.resetDiffNavigation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the previous or next diff block in the content view.
|
||||||
|
* Only effective when diff highlighting is enabled.
|
||||||
|
*/
|
||||||
|
navigateDiff(direction: "prev" | "next") {
|
||||||
|
const diffElements = this.contentView.querySelectorAll(".history-added, .history-deleted");
|
||||||
|
if (diffElements.length === 0) return;
|
||||||
|
|
||||||
|
// Remove previous focus highlight
|
||||||
|
const prevFocused = this.contentView.querySelector(".diff-focused");
|
||||||
|
if (prevFocused) {
|
||||||
|
prevFocused.classList.remove("diff-focused");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction === "next") {
|
||||||
|
this.currentDiffIndex = (this.currentDiffIndex + 1) % diffElements.length;
|
||||||
|
} else {
|
||||||
|
this.currentDiffIndex =
|
||||||
|
this.currentDiffIndex <= 0 ? diffElements.length - 1 : this.currentDiffIndex - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = diffElements[this.currentDiffIndex];
|
||||||
|
target.classList.add("diff-focused");
|
||||||
|
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||||
|
|
||||||
|
this.diffNavIndicator.textContent = `${this.currentDiffIndex + 1}/${diffElements.length}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the diff navigation index and update the indicator.
|
||||||
|
*/
|
||||||
|
resetDiffNavigation() {
|
||||||
|
this.currentDiffIndex = -1;
|
||||||
|
if (this.diffNavIndicator) {
|
||||||
|
if (this.showDiff) {
|
||||||
|
const diffElements = this.contentView.querySelectorAll(".history-added, .history-deleted");
|
||||||
|
this.diffNavIndicator.textContent = diffElements.length > 0 ? `0/${diffElements.length}` : "\u2014";
|
||||||
|
} else {
|
||||||
|
this.diffNavIndicator.textContent = "\u2014";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.updateDiffNavVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show or hide the diff navigation buttons based on the showDiff state.
|
||||||
|
*/
|
||||||
|
updateDiffNavVisibility() {
|
||||||
|
if (this.diffNavContainer) {
|
||||||
|
this.diffNavContainer.style.display = this.showDiff ? "flex" : "none";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override onOpen() {
|
override onOpen() {
|
||||||
@@ -236,25 +296,47 @@ export class DocumentHistoryModal extends Modal {
|
|||||||
void scheduleOnceIfDuplicated("loadRevs", () => this.loadRevs());
|
void scheduleOnceIfDuplicated("loadRevs", () => this.loadRevs());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
contentEl
|
const diffOptionsRow = contentEl.createDiv("");
|
||||||
.createDiv("", (e) => {
|
diffOptionsRow.addClass("op-info");
|
||||||
e.createEl("label", {}, (label) => {
|
diffOptionsRow.addClass("diff-options-row");
|
||||||
label.appendChild(
|
|
||||||
createEl("input", { type: "checkbox" }, (checkbox) => {
|
diffOptionsRow.createEl("label", {}, (label) => {
|
||||||
if (this.showDiff) {
|
label.appendChild(
|
||||||
checkbox.checked = true;
|
createEl("input", { type: "checkbox" }, (checkbox) => {
|
||||||
}
|
if (this.showDiff) {
|
||||||
checkbox.addEventListener("input", (evt: any) => {
|
checkbox.checked = true;
|
||||||
this.showDiff = checkbox.checked;
|
}
|
||||||
localStorage.setItem("ols-history-highlightdiff", this.showDiff == true ? "1" : "");
|
checkbox.addEventListener("input", (evt: any) => {
|
||||||
void scheduleOnceIfDuplicated("loadRevs", () => this.loadRevs());
|
this.showDiff = checkbox.checked;
|
||||||
});
|
localStorage.setItem("ols-history-highlightdiff", this.showDiff == true ? "1" : "");
|
||||||
})
|
this.updateDiffNavVisibility();
|
||||||
);
|
void scheduleOnceIfDuplicated("loadRevs", () => this.loadRevs());
|
||||||
label.appendText("Highlight diff");
|
});
|
||||||
});
|
})
|
||||||
})
|
);
|
||||||
.addClass("op-info");
|
label.appendText("Highlight diff");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Diff navigation buttons
|
||||||
|
this.diffNavContainer = diffOptionsRow.createDiv("");
|
||||||
|
this.diffNavContainer.addClass("diff-nav");
|
||||||
|
this.diffNavContainer.style.display = this.showDiff ? "flex" : "none";
|
||||||
|
|
||||||
|
this.diffNavContainer.createEl("button", { text: "\u25B2 Prev" }, (e) => {
|
||||||
|
e.addClass("diff-nav-btn");
|
||||||
|
e.addEventListener("click", () => {
|
||||||
|
this.navigateDiff("prev");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.diffNavContainer.createEl("button", { text: "\u25BC Next" }, (e) => {
|
||||||
|
e.addClass("diff-nav-btn");
|
||||||
|
e.addEventListener("click", () => {
|
||||||
|
this.navigateDiff("next");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.diffNavIndicator = this.diffNavContainer.createEl("span", { text: "\u2014" });
|
||||||
|
this.diffNavIndicator.addClass("diff-nav-indicator");
|
||||||
|
|
||||||
this.info = contentEl.createDiv("");
|
this.info = contentEl.createDiv("");
|
||||||
this.info.addClass("op-info");
|
this.info.addClass("op-info");
|
||||||
fireAndForget(async () => await this.loadFile(this.initialRev));
|
fireAndForget(async () => await this.loadFile(this.initialRev));
|
||||||
|
|||||||
41
styles.css
41
styles.css
@@ -484,4 +484,45 @@ div.workspace-leaf-content[data-type=bases] .livesync-status {
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Diff navigation */
|
||||||
|
.diff-options-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-nav-btn {
|
||||||
|
padding: 2px 8px;
|
||||||
|
font-size: 0.85em;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid var(--background-modifier-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--background-secondary);
|
||||||
|
color: var(--text-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-nav-btn:hover {
|
||||||
|
background-color: var(--background-modifier-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-nav-indicator {
|
||||||
|
font-size: 0.85em;
|
||||||
|
color: var(--text-muted);
|
||||||
|
min-width: 3em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-focused {
|
||||||
|
outline: 2px solid var(--interactive-accent);
|
||||||
|
outline-offset: 1px;
|
||||||
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user