Improve editor sync status line

This commit is contained in:
Andras Schmelczer 2025-08-28 21:55:43 +01:00
parent 47f4ddfc63
commit 376008de54
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
7 changed files with 209 additions and 148 deletions

View file

@ -1,10 +1,10 @@
import type {
MarkdownView,
Editor,
MarkdownFileInfo,
TAbstractFile,
WorkspaceLeaf
} from "obsidian";
import type { MarkdownView } from "obsidian";
import { Platform, Plugin, TFile } from "obsidian";
import "../manifest.json";
import { HistoryView } from "./views/history/history-view";
@ -15,7 +15,7 @@ import { SyncClient, rateLimit, DEFAULT_SETTINGS, Logger } from "sync-client";
import { ObsidianFileSystemOperations } from "./obsidian-file-system";
import { SyncSettingsTab } from "./views/settings/settings-tab";
import { logToConsole } from "./utils/log-to-console";
import { updateEditorStatusDisplay } from "./views/editor-sync-line/editor-sync-line";
import { EditorStatusDisplayManager } from "./views/editor-status-display-manager/editor-status-display-manager";
import { remoteCursorsTheme } from "./views/cursors/remote-cursor-theme";
import {
remoteCursorsPlugin,
@ -124,17 +124,23 @@ export default class VaultLinkPlugin extends Plugin {
this.registerEditorEvents();
await this.client.start();
const interval = setInterval(() => {
updateEditorStatusDisplay(this.app.workspace, this.client);
}, 200);
const editorStatusDisplayManager = new EditorStatusDisplayManager(
this,
this.app.workspace,
this.client
);
this.disposables.push(() => {
clearInterval(interval);
editorStatusDisplayManager.stop();
});
});
}
public onunload(): void {
this.client.stop();
this.client.waitAndStop().catch((err: unknown) => {
this.client.logger.error(
`Error while stopping the sync client: ${err}`
);
});
this.disposables.forEach((disposable) => {
disposable();
});

View file

@ -0,0 +1,97 @@
import type { Workspace } from "obsidian";
import { FileView, setIcon } from "obsidian";
import type { SyncClient } from "sync-client";
import { DocumentSyncStatus } from "sync-client";
import "./editor-status-display-manager.scss";
import type VaultLinkPlugin from "src/vault-link-plugin";
import { HistoryView } from "../history/history-view";
export class EditorStatusDisplayManager {
private static readonly UPDATE_INTERVAL_IN_MS = 100;
private readonly intervalId: NodeJS.Timeout;
private readonly lastStatuses = new Map<string, DocumentSyncStatus>();
public constructor(
private readonly plugin: VaultLinkPlugin,
private readonly workspace: Workspace,
private readonly client: SyncClient
) {
this.intervalId = setInterval(() => {
this.updateEditorStatusDisplay();
}, EditorStatusDisplayManager.UPDATE_INTERVAL_IN_MS);
}
public stop(): void {
clearInterval(this.intervalId);
}
private updateEditorStatusDisplay(): void {
this.workspace.iterateAllLeaves((leaf) => {
if (leaf.view instanceof FileView) {
const filePath = leaf.view.file?.path;
if (filePath == null) {
return;
}
const element = this.getElementFromLeaf(leaf.view);
if (element == null) {
return;
}
const previousStatus = this.lastStatuses.get(filePath);
const currentStatus =
this.client.getDocumentSyncingStatus(filePath);
if (previousStatus === currentStatus) {
return;
}
this.lastStatuses.set(filePath, currentStatus);
if (currentStatus == DocumentSyncStatus.SYNCING_IS_DISABLED) {
element.remove();
return;
}
if (currentStatus == DocumentSyncStatus.SYNCING) {
element.classList.add("loading");
} else {
element.classList.remove("loading");
}
const iconContainer = element.querySelector(".icon");
if (iconContainer != null) {
setIcon(
iconContainer as HTMLElement, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
currentStatus == DocumentSyncStatus.SYNCING
? "loader"
: "circle-check"
);
}
}
});
}
private getElementFromLeaf(fileView: FileView): Element | undefined {
const parent = fileView.contentEl.querySelector(".cm-editor");
if (parent == null) {
return;
}
return (
parent.querySelector(".vault-link-sync-status") ??
parent.createDiv(
{
cls: "vault-link-sync-status"
},
(el) => {
el.createSpan({ text: "VaultLink sync state" });
el.createDiv({
cls: "icon"
});
el.onclick = async (): Promise<void> =>
this.plugin.activateView(HistoryView.TYPE);
}
)
);
}
}

View file

@ -1,55 +0,0 @@
import type { Workspace } from "obsidian";
import { FileView, setIcon } from "obsidian";
import type { SyncClient } from "sync-client";
import { DocumentSyncStatus } from "sync-client";
import "./editor-sync-line.scss";
export function updateEditorStatusDisplay(
workspace: Workspace,
client: SyncClient
): void {
workspace.iterateAllLeaves((leaf) => {
if (leaf.view instanceof FileView) {
const filePath = leaf.view.file?.path;
if (filePath == null) {
return;
}
const parent = leaf.view.contentEl.querySelector(".cm-editor");
if (parent == null) {
return;
}
const element =
parent.querySelector(".vault-link-sync-status") ??
parent.createDiv(
{
cls: "vault-link-sync-status"
},
(el) => {
el.createSpan({ text: "VaultLink sync state" });
el.createDiv({
cls: "icon"
});
}
);
const isLoading =
client.getDocumentSyncingStatus(filePath) ==
DocumentSyncStatus.SYNCING;
if (isLoading) {
element.classList.add("loading");
} else {
element.classList.remove("loading");
}
const iconContainer = element.querySelector(".icon");
if (iconContainer != null) {
setIcon(
iconContainer as HTMLElement, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
isLoading ? "loader" : "circle-check"
);
}
}
});
}