Fix remote cursor duplication

This commit is contained in:
Andras Schmelczer 2025-10-18 21:30:45 +01:00
commit 088f474a2e
3 changed files with 48 additions and 7 deletions

View file

@ -9,6 +9,7 @@ import type {
ViewUpdate ViewUpdate
} from "@codemirror/view"; } from "@codemirror/view";
import { RemoteCursorWidget } from "./remote-cursor-widget"; import { RemoteCursorWidget } from "./remote-cursor-widget";
import type { RelativePath } from "sync-client";
import { import {
utils, utils,
type CursorSpan, type CursorSpan,
@ -32,12 +33,14 @@ export class RemoteCursorsPluginValue implements PluginValue {
isOutdated: boolean; isOutdated: boolean;
}[] = []; }[] = [];
private static app: App;
public decorations: DecorationSet = RangeSet.of([]); public decorations: DecorationSet = RangeSet.of([]);
public static setCursors( public static setCursors(
clients: MaybeOutdatedClientCursors[], clients: MaybeOutdatedClientCursors[],
app: App app: App
): void { ): void {
RemoteCursorsPluginValue.app = app;
RemoteCursorsPluginValue.cursors = [ RemoteCursorsPluginValue.cursors = [
...RemoteCursorsPluginValue.cursors.filter(({ deviceId }) => ...RemoteCursorsPluginValue.cursors.filter(({ deviceId }) =>
clients.some( clients.some(
@ -82,6 +85,30 @@ export class RemoteCursorsPluginValue implements PluginValue {
}); });
} }
private static findFileForEditor(
editor: EditorView
): RelativePath | undefined {
return RemoteCursorsPluginValue.app.workspace
.getLeavesOfType("markdown")
.map((leaf) => leaf.view)
.filter((view) => view instanceof MarkdownView)
.flatMap((view) => {
// @ts-expect-error, not typed
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
if ((view.editor.cm as EditorView) !== editor) {
return [];
}
const { file } = view;
if (!file) {
return;
}
return [file.path];
})
.first();
}
private static interpolateRemoteCursorPositions( private static interpolateRemoteCursorPositions(
original: string, original: string,
edited: string edited: string
@ -155,9 +182,12 @@ export class RemoteCursorsPluginValue implements PluginValue {
); );
const decorations: Range<Decoration>[] = []; const decorations: Range<Decoration>[] = [];
const relative_path = RemoteCursorsPluginValue.findFileForEditor(
RemoteCursorsPluginValue.cursors.forEach( update.view
({ name, span: { start, end } }) => { );
RemoteCursorsPluginValue.cursors
.filter(({ path }) => path == relative_path)
.forEach(({ name, span: { start, end } }) => {
const color = utils.getRandomColor(name); const color = utils.getRandomColor(name);
const startLine = update.view.state.doc.lineAt(start); const startLine = update.view.state.doc.lineAt(start);
const endLine = update.view.state.doc.lineAt(end); const endLine = update.view.state.doc.lineAt(end);
@ -221,8 +251,7 @@ export class RemoteCursorsPluginValue implements PluginValue {
widget: new RemoteCursorWidget(color, name) widget: new RemoteCursorWidget(color, name)
}) })
}); });
} });
);
this.decorations = Decoration.set(decorations, true); this.decorations = Decoration.set(decorations, true);
} }

View file

@ -27,4 +27,4 @@
"prettier": "^3.6.2", "prettier": "^3.6.2",
"typescript-eslint": "8.41.0" "typescript-eslint": "8.41.0"
} }
} }

View file

@ -106,7 +106,19 @@ async fn websocket(
continue; continue;
} }
send_update_over_websocket(&update.message, &mut sender).await?; let message = match update.message {
WebSocketServerMessage::CursorPositions(CursorPositionFromServer { clients }) => {
WebSocketServerMessage::CursorPositions(CursorPositionFromServer {
clients: clients
.into_iter()
.filter(|client| client.device_id != device_id)
.collect(),
})
}
WebSocketServerMessage::VaultUpdate(_) => update.message,
};
send_update_over_websocket(&message, &mut sender).await?;
} }
Ok::<(), SyncServerError>(()) Ok::<(), SyncServerError>(())