From 9791ff1170356c9313edea507b87629fe810f9a3 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Thu, 11 Jun 2026 09:04:38 -0400 Subject: [PATCH] fix: Prevent selecting word-joiner characters around multiplayer cursor (#12660) * Possible fix for word-joiner characters copied on Chrome+Windows * simplify --- app/editor/extensions/Multiplayer.ts | 3 ++- shared/editor/components/Styles.ts | 11 ++++++----- shared/editor/styles/EditorStyleHelper.ts | 8 ++++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/editor/extensions/Multiplayer.ts b/app/editor/extensions/Multiplayer.ts index 4aa2000bfe..8bfe608f0f 100644 --- a/app/editor/extensions/Multiplayer.ts +++ b/app/editor/extensions/Multiplayer.ts @@ -13,6 +13,7 @@ import { import * as Y from "yjs"; import Extension from "@shared/editor/lib/Extension"; import { isRemoteTransaction } from "@shared/editor/lib/multiplayer"; +import { EditorStyleHelper } from "@shared/editor/styles/EditorStyleHelper"; import { Second } from "@shared/utils/time"; type UserAwareness = { @@ -107,7 +108,7 @@ export default class Multiplayer extends Extension { return { style: `background-color: ${u.color}${opacity}`, - class: "ProseMirror-yjs-selection", + class: EditorStyleHelper.multiplayerSelection, }; }; diff --git a/shared/editor/components/Styles.ts b/shared/editor/components/Styles.ts index a199663ccd..16dfb5529a 100644 --- a/shared/editor/components/Styles.ts +++ b/shared/editor/components/Styles.ts @@ -596,7 +596,7 @@ width: 100%; padding: ${props.editorStyle?.padding ?? "initial"}; margin: ${props.editorStyle?.margin ?? "initial"}; - & > .ProseMirror-yjs-cursor { + & > .${EditorStyleHelper.multiplayerCursor} { display: none; } @@ -670,11 +670,11 @@ width: 100%; h5 { font-size: var(--font-size-h5); } h6 { font-size: var(--font-size-h6); } - .ProseMirror-yjs-selection { + .${EditorStyleHelper.multiplayerSelection} { transition: background-color 500ms ease-in-out; } - .ProseMirror-yjs-cursor { + .${EditorStyleHelper.multiplayerCursor} { position: relative; margin-left: -1px; margin-right: -1px; @@ -682,6 +682,7 @@ width: 100%; border-right: 1px solid black; height: 1em; word-break: normal; + user-select: none; &::after { content: ""; @@ -719,7 +720,7 @@ width: 100%; } } -&.show-cursor-names .ProseMirror-yjs-cursor > div { +&.show-cursor-names .${EditorStyleHelper.multiplayerCursor} > div { opacity: 1; } @@ -998,7 +999,7 @@ img.ProseMirror-separator { .${EditorStyleHelper.headingPositionAnchor}:first-child, // Edge case where multiplayer cursor is between start of cell and heading -.${EditorStyleHelper.headingPositionAnchor}:first-child + .ProseMirror-yjs-cursor, +.${EditorStyleHelper.headingPositionAnchor}:first-child + .${EditorStyleHelper.multiplayerCursor}, // Edge case where table grips are between start of cell and heading .${EditorStyleHelper.headingPositionAnchor}:first-child + [role=button] + [role=button] { & + h1, diff --git a/shared/editor/styles/EditorStyleHelper.ts b/shared/editor/styles/EditorStyleHelper.ts index a77f34e033..bbb5abf1c8 100644 --- a/shared/editor/styles/EditorStyleHelper.ts +++ b/shared/editor/styles/EditorStyleHelper.ts @@ -22,6 +22,14 @@ export class EditorStyleHelper { static readonly comment = "comment-marker"; + // Multiplayer + + /** Remote collaborator's cursor */ + static readonly multiplayerCursor = "ProseMirror-yjs-cursor"; + + /** Remote collaborator's selection */ + static readonly multiplayerSelection = "ProseMirror-yjs-selection"; + // Code static readonly codeBlock = "code-block";