Fix remote cursor duplication

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

View file

@ -9,6 +9,7 @@ import type {
ViewUpdate
} from "@codemirror/view";
import { RemoteCursorWidget } from "./remote-cursor-widget";
import type { RelativePath } from "sync-client";
import {
utils,
type CursorSpan,
@ -32,12 +33,14 @@ export class RemoteCursorsPluginValue implements PluginValue {
isOutdated: boolean;
}[] = [];
private static app: App;
public decorations: DecorationSet = RangeSet.of([]);
public static setCursors(
clients: MaybeOutdatedClientCursors[],
app: App
): void {
RemoteCursorsPluginValue.app = app;
RemoteCursorsPluginValue.cursors = [
...RemoteCursorsPluginValue.cursors.filter(({ deviceId }) =>
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(
original: string,
edited: string
@ -155,9 +182,12 @@ export class RemoteCursorsPluginValue implements PluginValue {
);
const decorations: Range<Decoration>[] = [];
RemoteCursorsPluginValue.cursors.forEach(
({ name, span: { start, end } }) => {
const relative_path = RemoteCursorsPluginValue.findFileForEditor(
update.view
);
RemoteCursorsPluginValue.cursors
.filter(({ path }) => path == relative_path)
.forEach(({ name, span: { start, end } }) => {
const color = utils.getRandomColor(name);
const startLine = update.view.state.doc.lineAt(start);
const endLine = update.view.state.doc.lineAt(end);
@ -221,8 +251,7 @@ export class RemoteCursorsPluginValue implements PluginValue {
widget: new RemoteCursorWidget(color, name)
})
});
}
);
});
this.decorations = Decoration.set(decorations, true);
}

View file

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

View file

@ -106,7 +106,19 @@ async fn websocket(
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>(())