From b2f4e0c038c81560a8de75ae4939b88fd0efeadd Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Wed, 27 Aug 2025 22:31:29 +0100 Subject: [PATCH] Show files open by other users --- .../obsidian-plugin/src/vault-link-plugin.ts | 2 + .../src/views/cursors/file-explorer.scss | 15 ++++++ .../src/views/cursors/file-explorer.ts | 52 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 frontend/obsidian-plugin/src/views/cursors/file-explorer.scss create mode 100644 frontend/obsidian-plugin/src/views/cursors/file-explorer.ts diff --git a/frontend/obsidian-plugin/src/vault-link-plugin.ts b/frontend/obsidian-plugin/src/vault-link-plugin.ts index 7e0eff1b..90ab1a73 100644 --- a/frontend/obsidian-plugin/src/vault-link-plugin.ts +++ b/frontend/obsidian-plugin/src/vault-link-plugin.ts @@ -24,6 +24,7 @@ import { import { LocalCursorUpdateListener } from "./views/cursors/local-cursor-update-listener"; import { slowFetchFactory } from "./debugging/slow-fetch-factory"; import { flakyWebSocketFactory } from "./debugging/flaky-websocket-factory"; +import { renderCursorsInFileExplorer } from "./views/cursors/file-explorer"; const MIN_WAIT_BETWEEN_UPDATES_IN_MS = 250; @@ -94,6 +95,7 @@ export default class VaultLinkPlugin extends Plugin { this.client.addRemoteCursorsUpdateListener((cursors) => { RemoteCursorsPluginValue.setCursors(cursors, this.app); + renderCursorsInFileExplorer(cursors, this.app); }); const cursorListener = new LocalCursorUpdateListener( diff --git a/frontend/obsidian-plugin/src/views/cursors/file-explorer.scss b/frontend/obsidian-plugin/src/views/cursors/file-explorer.scss new file mode 100644 index 00000000..45759019 --- /dev/null +++ b/frontend/obsidian-plugin/src/views/cursors/file-explorer.scss @@ -0,0 +1,15 @@ +.remote-users { + display: flex; + flex-wrap: wrap; + gap: var(--size-4-2); + margin-left: var(--size-4-2); + + span { + border-radius: var(--radius-l); + padding: 0 var(--size-4-1); + border-width: 1px; + border-style: solid; + font-size: var(--font-smallest); + font-style: italic; + } +} \ No newline at end of file diff --git a/frontend/obsidian-plugin/src/views/cursors/file-explorer.ts b/frontend/obsidian-plugin/src/views/cursors/file-explorer.ts new file mode 100644 index 00000000..cfeb11f5 --- /dev/null +++ b/frontend/obsidian-plugin/src/views/cursors/file-explorer.ts @@ -0,0 +1,52 @@ +import "./file-explorer.scss"; + +import type { App, View } from "obsidian"; +import { getRandomColor } from "src/utils/get-random-color"; +import type { MaybeOutdatedClientCursors, RelativePath } from "sync-client"; + +const REMOTE_USER_CONTAINER_CLASS = "remote-users"; + +export function renderCursorsInFileExplorer( + cursors: MaybeOutdatedClientCursors[], + app: App +): void { + const fileExplorers = app.workspace.getLeavesOfType("file-explorer"); + if (fileExplorers.length == 0) return; + + const [fileExplorer] = fileExplorers; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const fileExplorerView: View & { + fileItems: Record; // it's an internal API + } = fileExplorer.view as any; // eslint-disable-line + + for (const key in fileExplorerView.fileItems) { + const element = + fileExplorerView.fileItems[key].el.querySelector(".tree-item-self"); + + const customElement = createDiv( + { + cls: REMOTE_USER_CONTAINER_CLASS + }, + (parent) => { + cursors.forEach((cursor) => { + cursor.documentsWithCursors.forEach((document) => { + if (document.relative_path === key) { + parent.appendChild( + createSpan({ + text: cursor.userName, + attr: { + style: `border-color: ${getRandomColor(cursor.userName)}` + } + }) + ); + } + }); + }); + } + ); + + element?.querySelector("." + REMOTE_USER_CONTAINER_CLASS)?.remove(); + element?.appendChild(customElement); + } +}