فهرست منبع

improve cursor design

Yuki Takei 1 هفته پیش
والد
کامیت
053d8fb17f
1فایلهای تغییر یافته به همراه36 افزوده شده و 24 حذف شده
  1. 36 24
      packages/editor/src/client/services-internal/extensions/y-rich-cursors/theme.ts

+ 36 - 24
packages/editor/src/client/services-internal/extensions/y-rich-cursors/theme.ts

@@ -1,24 +1,33 @@
 import { EditorView } from '@codemirror/view';
 
+// Shared design tokens
+const AVATAR_SIZE = '20px';
+const IDLE_OPACITY = '0.6';
+
 export const richCursorsTheme = EditorView.baseTheme({
-  // Caret line
+  // Caret line — negative margins cancel out border width to avoid text shift.
+  // Modeled after yRemoteSelectionsTheme in y-codemirror.next.
   '.cm-yRichCaret': {
     position: 'relative',
-    borderLeft: '2px solid',
+    borderLeft: '1px solid',
+    borderRight: '1px solid',
+    marginLeft: '-1px',
+    marginRight: '-1px',
+    boxSizing: 'border-box',
+    display: 'inline',
   },
 
-  // Overlay flag — positioned below the caret
+  // Overlay flag — positioned below the caret.
+  // pointer-events: auto so the avatar itself is a hover/click target.
   '.cm-yRichCursorFlag': {
     position: 'absolute',
     top: '100%',
-    left: '-8px',
+    left: '-9px', // center the avatar on the 1px caret
     zIndex: '10',
-    pointerEvents: 'none',
-    opacity: '0.4',
+    opacity: IDLE_OPACITY,
     transition: 'opacity 0.3s ease',
   },
-  '.cm-yRichCaret:hover .cm-yRichCursorFlag': {
-    pointerEvents: 'auto',
+  '.cm-yRichCaret:hover .cm-yRichCursorFlag, .cm-yRichCursorFlag:hover': {
     opacity: '1',
   },
   '.cm-yRichCursorFlag.cm-yRichCursorActive': {
@@ -27,37 +36,40 @@ export const richCursorsTheme = EditorView.baseTheme({
 
   // Avatar image
   '.cm-yRichCursorAvatar': {
-    width: '16px',
-    height: '16px',
+    width: AVATAR_SIZE,
+    height: AVATAR_SIZE,
     borderRadius: '50%',
     display: 'block',
   },
 
   // Initials fallback
   '.cm-yRichCursorInitials': {
-    width: '16px',
-    height: '16px',
+    width: AVATAR_SIZE,
+    height: AVATAR_SIZE,
     borderRadius: '50%',
     display: 'flex',
     alignItems: 'center',
     justifyContent: 'center',
     color: 'white',
-    fontSize: '8px',
+    fontSize: '9px',
     fontWeight: 'bold',
   },
 
-  // Name label — hidden by default, shown on hover
+  // Name label — hidden by default, shown on hover.
+  // Sits behind the avatar; left border-radius matches the avatar circle.
   '.cm-yRichCursorInfo': {
     display: 'none',
     position: 'absolute',
     top: '0',
-    left: '20px',
+    left: '0',
+    zIndex: '-1',
     whiteSpace: 'nowrap',
-    padding: '2px 6px',
-    borderRadius: '3px',
+    padding: `0 6px 0 calc(${AVATAR_SIZE} + 4px)`,
+    borderRadius: `calc(${AVATAR_SIZE} / 2) 3px 3px calc(${AVATAR_SIZE} / 2)`,
     color: 'white',
     fontSize: '12px',
-    lineHeight: '16px',
+    height: AVATAR_SIZE,
+    lineHeight: AVATAR_SIZE,
   },
   '.cm-yRichCursorFlag:hover .cm-yRichCursorInfo': {
     display: 'block',
@@ -86,7 +98,7 @@ export const richCursorsTheme = EditorView.baseTheme({
     display: 'flex',
     alignItems: 'center',
     gap: '2px',
-    opacity: '0.4',
+    opacity: IDLE_OPACITY,
     transition: 'opacity 0.3s ease',
   },
   '.cm-offScreenIndicator.cm-yRichCursorActive': {
@@ -97,19 +109,19 @@ export const richCursorsTheme = EditorView.baseTheme({
     lineHeight: '1',
   },
   '.cm-offScreenAvatar': {
-    width: '16px',
-    height: '16px',
+    width: AVATAR_SIZE,
+    height: AVATAR_SIZE,
     borderRadius: '50%',
   },
   '.cm-offScreenInitials': {
-    width: '16px',
-    height: '16px',
+    width: AVATAR_SIZE,
+    height: AVATAR_SIZE,
     borderRadius: '50%',
     display: 'flex',
     alignItems: 'center',
     justifyContent: 'center',
     color: 'white',
-    fontSize: '8px',
+    fontSize: '9px',
     fontWeight: 'bold',
   },
 });