@@ -2,11 +2,14 @@ import { type Selection, Plugin, PluginKey, TextSelection } from "@tiptap/pm/sta
2
2
import { type EditorView , Decoration , DecorationSet } from "@tiptap/pm/view" ;
3
3
4
4
export const PROSEMIRROR_SMOOTH_CURSOR_CLASS = "prosemirror-smooth-cursor" ;
5
+ const BLINK_DELAY = 750 ; // Time in ms before cursor starts blinking
5
6
6
7
export function smoothCursorPlugin ( ) : Plugin {
7
8
let smoothCursor : HTMLElement | null = typeof document === "undefined" ? null : document . createElement ( "div" ) ;
8
9
let rafId : number | undefined ;
10
+ let blinkTimeoutId : number | undefined ;
9
11
let isEditorFocused = false ;
12
+ let lastCursorPosition = { x : 0 , y : 0 } ;
10
13
11
14
function updateCursor ( view ?: EditorView , cursor ?: HTMLElement ) {
12
15
if ( ! view || ! view . dom || view . isDestroyed || ! cursor ) return ;
@@ -30,13 +33,31 @@ export function smoothCursorPlugin(): Plugin {
30
33
31
34
const className = PROSEMIRROR_SMOOTH_CURSOR_CLASS ;
32
35
33
- cursor . className = className ;
34
- cursor . style . height = `${ cursorRect . bottom - cursorRect . top } px` ;
35
-
36
36
// Calculate the exact position
37
37
const x = cursorRect . left - editorRect . left ;
38
38
const y = cursorRect . top - editorRect . top ;
39
39
40
+ // Check if cursor position has changed
41
+ if ( x !== lastCursorPosition . x || y !== lastCursorPosition . y ) {
42
+ lastCursorPosition = { x, y } ;
43
+ cursor . classList . remove ( `${ className } --blinking` ) ;
44
+
45
+ // Clear existing timeout
46
+ if ( blinkTimeoutId ) {
47
+ window . clearTimeout ( blinkTimeoutId ) ;
48
+ }
49
+
50
+ // Set new timeout for blinking
51
+ blinkTimeoutId = window . setTimeout ( ( ) => {
52
+ if ( cursor && isEditorFocused ) {
53
+ cursor . classList . add ( `${ className } --blinking` ) ;
54
+ }
55
+ } , BLINK_DELAY ) ;
56
+ }
57
+
58
+ cursor . className = className ;
59
+ cursor . style . height = `${ cursorRect . bottom - cursorRect . top } px` ;
60
+
40
61
rafId = requestAnimationFrame ( ( ) => {
41
62
cursor . style . transform = `translate3d(${ x } px, ${ y } px, 0)` ;
42
63
} ) ;
@@ -63,6 +84,10 @@ export function smoothCursorPlugin(): Plugin {
63
84
64
85
const handleBlur = ( ) => {
65
86
isEditorFocused = false ;
87
+ if ( blinkTimeoutId ) {
88
+ window . clearTimeout ( blinkTimeoutId ) ;
89
+ }
90
+ cursor . classList . remove ( `${ PROSEMIRROR_SMOOTH_CURSOR_CLASS } --blinking` ) ;
66
91
update ( ) ;
67
92
} ;
68
93
@@ -83,10 +108,13 @@ export function smoothCursorPlugin(): Plugin {
83
108
view . dom . removeEventListener ( "focus" , handleFocus ) ;
84
109
view . dom . removeEventListener ( "blur" , handleBlur ) ;
85
110
observer ?. unobserve ( view . dom ) ;
86
- // Clean up any pending animation frame
87
111
if ( rafId !== undefined ) {
88
112
cancelAnimationFrame ( rafId ) ;
89
113
}
114
+ if ( blinkTimeoutId ) {
115
+ window . clearTimeout ( blinkTimeoutId ) ;
116
+ }
117
+ style . remove ( ) ;
90
118
} ,
91
119
} ;
92
120
} ,
0 commit comments