@@ -134,23 +134,48 @@ export function RichTextEditor({ content, onChange, isMobile }: RichTextEditorPr
134134 }
135135 } , [ vimModeEnabled , editor ] ) ;
136136
137+ useEffect ( ( ) => {
138+ const handleKeyDown = ( event : KeyboardEvent ) => {
139+ if ( event . ctrlKey && event . key === ';' ) {
140+ event . preventDefault ( ) ;
141+ handleVimModeToggle ( ) ;
142+ }
143+ } ;
144+
145+ document . addEventListener ( 'keydown' , handleKeyDown ) ;
146+
147+ return ( ) => {
148+ document . removeEventListener ( 'keydown' , handleKeyDown ) ;
149+ } ;
150+ } , [ ] ) ;
151+
137152 if ( ! editor ) {
138153 return null ;
139154 }
140155
141- const runCommand = ( callback : ( chain : ReturnType < NonNullable < typeof editor > [ 'chain' ] > ) => void ) => {
142- if ( editor ) {
143- callback ( editor . chain ( ) . focus ( ) ) ;
144- }
145- } ;
146-
147156 const isTableSelected = ( ) => {
148157 return editor . isActive ( 'table' ) ;
149158 } ;
150159
151160 const handleVimModeToggle = ( ) => {
152161 const isEnabled = toggleVimMode ( ) ;
153162 setVimModeEnabled ( isEnabled ) ;
163+
164+ if ( window . vimMode && window . vimMode . statusElement ) {
165+ const statusMessage = isEnabled ? 'Vim mode enabled' : 'Vim mode disabled' ;
166+ window . vimMode . statusElement . textContent = statusMessage ;
167+ window . vimMode . statusElement . style . display = 'block' ;
168+
169+ setTimeout ( ( ) => {
170+ if ( window . vimMode && window . vimMode . statusElement ) {
171+ if ( isEnabled ) {
172+ window . vimMode . statusElement . textContent = window . vimMode . mode . toUpperCase ( ) ;
173+ } else {
174+ window . vimMode . statusElement . style . display = 'none' ;
175+ }
176+ }
177+ } , 1500 ) ;
178+ }
154179 } ;
155180
156181 const ToolbarButton = ( {
@@ -171,6 +196,7 @@ export function RichTextEditor({ content, onChange, isMobile }: RichTextEditorPr
171196 variant = { isActive ? 'filled' : 'subtle' }
172197 color = { isActive ? 'blue' : 'gray' }
173198 onClick = { onClick }
199+ onMouseDown = { ( e ) => e . preventDefault ( ) }
174200 disabled = { disabled }
175201 size = { isMobile ? 'sm' : 'md' }
176202 className = "modern-toolbar-button"
@@ -254,13 +280,13 @@ export function RichTextEditor({ content, onChange, isMobile }: RichTextEditorPr
254280 icon = { < IconBold size = { isMobile ? 14 : 16 } /> }
255281 label = "Bold"
256282 isActive = { editor ?. isActive ( 'bold' ) ?? false }
257- onClick = { ( ) => runCommand ( chain => chain . toggleBold ( ) . run ( ) ) }
283+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . toggleBold ( ) . run ( ) }
258284 />
259285 < ToolbarButton
260286 icon = { < IconItalic size = { isMobile ? 14 : 16 } /> }
261287 label = "Italic"
262288 isActive = { editor ?. isActive ( 'italic' ) ?? false }
263- onClick = { ( ) => runCommand ( chain => chain . toggleItalic ( ) . run ( ) ) }
289+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . toggleItalic ( ) . run ( ) }
264290 />
265291 </ ToolbarGroup >
266292
@@ -272,13 +298,13 @@ export function RichTextEditor({ content, onChange, isMobile }: RichTextEditorPr
272298 icon = { < IconH1 size = { isMobile ? 14 : 16 } /> }
273299 label = "Heading 1"
274300 isActive = { editor ?. isActive ( 'heading' , { level : 1 } ) ?? false }
275- onClick = { ( ) => runCommand ( chain => chain . toggleHeading ( { level : 1 } ) . run ( ) ) }
301+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . toggleHeading ( { level : 1 } ) . run ( ) }
276302 />
277303 < ToolbarButton
278304 icon = { < IconH2 size = { isMobile ? 14 : 16 } /> }
279305 label = "Heading 2"
280306 isActive = { editor ?. isActive ( 'heading' , { level : 2 } ) ?? false }
281- onClick = { ( ) => runCommand ( chain => chain . toggleHeading ( { level : 2 } ) . run ( ) ) }
307+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . toggleHeading ( { level : 2 } ) . run ( ) }
282308 />
283309 </ ToolbarGroup >
284310
@@ -290,13 +316,13 @@ export function RichTextEditor({ content, onChange, isMobile }: RichTextEditorPr
290316 icon = { < IconList size = { isMobile ? 14 : 16 } /> }
291317 label = "Bullet List"
292318 isActive = { editor ?. isActive ( 'bulletList' ) ?? false }
293- onClick = { ( ) => runCommand ( chain => chain . toggleBulletList ( ) . run ( ) ) }
319+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . toggleBulletList ( ) . run ( ) }
294320 />
295321 < ToolbarButton
296322 icon = { < IconListNumbers size = { isMobile ? 14 : 16 } /> }
297323 label = "Numbered List"
298324 isActive = { editor ?. isActive ( 'orderedList' ) ?? false }
299- onClick = { ( ) => runCommand ( chain => chain . toggleOrderedList ( ) . run ( ) ) }
325+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . toggleOrderedList ( ) . run ( ) }
300326 />
301327 </ ToolbarGroup >
302328
@@ -308,14 +334,14 @@ export function RichTextEditor({ content, onChange, isMobile }: RichTextEditorPr
308334 icon = { < IconQuote size = { isMobile ? 14 : 16 } /> }
309335 label = "Quote"
310336 isActive = { editor ?. isActive ( 'blockquote' ) ?? false }
311- onClick = { ( ) => runCommand ( chain => chain . toggleBlockquote ( ) . run ( ) ) }
337+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . toggleBlockquote ( ) . run ( ) }
312338 />
313339 { ! isMobile && (
314340 < ToolbarButton
315341 icon = { < IconCode size = { 16 } /> }
316342 label = "Code Block"
317343 isActive = { editor ?. isActive ( 'codeBlock' ) ?? false }
318- onClick = { ( ) => runCommand ( chain => chain . toggleCodeBlock ( ) . run ( ) ) }
344+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . toggleCodeBlock ( ) . run ( ) }
319345 />
320346 ) }
321347 </ ToolbarGroup >
@@ -328,7 +354,7 @@ export function RichTextEditor({ content, onChange, isMobile }: RichTextEditorPr
328354 < ToolbarButton
329355 icon = { < IconTable size = { 16 } /> }
330356 label = "Insert Table"
331- onClick = { ( ) => runCommand ( chain => chain . insertTable ( { rows : 3 , cols : 3 , withHeaderRow : true } ) . run ( ) ) }
357+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . insertTable ( { rows : 3 , cols : 3 , withHeaderRow : true } ) . run ( ) }
332358 />
333359 </ ToolbarGroup >
334360 </ >
@@ -342,17 +368,17 @@ export function RichTextEditor({ content, onChange, isMobile }: RichTextEditorPr
342368 < ToolbarButton
343369 icon = { < IconRowInsertBottom size = { isMobile ? 14 : 16 } /> }
344370 label = "Add Row"
345- onClick = { ( ) => runCommand ( chain => chain . addRowAfter ( ) . run ( ) ) }
371+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . addRowAfter ( ) . run ( ) }
346372 />
347373 < ToolbarButton
348374 icon = { < IconColumnInsertRight size = { isMobile ? 14 : 16 } /> }
349375 label = "Add Column"
350- onClick = { ( ) => runCommand ( chain => chain . addColumnAfter ( ) . run ( ) ) }
376+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . addColumnAfter ( ) . run ( ) }
351377 />
352378 < ToolbarButton
353379 icon = { < IconTrash size = { isMobile ? 14 : 16 } /> }
354380 label = "Delete Table"
355- onClick = { ( ) => runCommand ( chain => chain . deleteTable ( ) . run ( ) ) }
381+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . deleteTable ( ) . run ( ) }
356382 />
357383 </ ToolbarGroup >
358384 </ >
@@ -367,19 +393,19 @@ export function RichTextEditor({ content, onChange, isMobile }: RichTextEditorPr
367393 icon = { < IconAlignLeft size = { 16 } /> }
368394 label = "Align Left"
369395 isActive = { editor ?. isActive ( { textAlign : 'left' } ) ?? false }
370- onClick = { ( ) => runCommand ( chain => chain . setTextAlign ( 'left' ) . run ( ) ) }
396+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . setTextAlign ( 'left' ) . run ( ) }
371397 />
372398 < ToolbarButton
373399 icon = { < IconAlignCenter size = { 16 } /> }
374400 label = "Align Center"
375401 isActive = { editor ?. isActive ( { textAlign : 'center' } ) ?? false }
376- onClick = { ( ) => runCommand ( chain => chain . setTextAlign ( 'center' ) . run ( ) ) }
402+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . setTextAlign ( 'center' ) . run ( ) }
377403 />
378404 < ToolbarButton
379405 icon = { < IconAlignRight size = { 16 } /> }
380406 label = "Align Right"
381407 isActive = { editor ?. isActive ( { textAlign : 'right' } ) ?? false }
382- onClick = { ( ) => runCommand ( chain => chain . setTextAlign ( 'right' ) . run ( ) ) }
408+ onClick = { ( ) => editor ?. chain ( ) . focus ( ) . setTextAlign ( 'right' ) . run ( ) }
383409 />
384410 </ ToolbarGroup >
385411 </ >
@@ -389,18 +415,20 @@ export function RichTextEditor({ content, onChange, isMobile }: RichTextEditorPr
389415 { ! isMobile && (
390416 < >
391417 < ToolbarDivider />
392- < Switch
393- label = "Vim"
394- checked = { vimModeEnabled }
395- onChange = { handleVimModeToggle }
396- size = "sm"
397- color = "blue"
398- thumbIcon = { vimModeEnabled ? < IconKeyboard size = { 12 } /> : null }
399- styles = { {
400- root : { alignItems : 'center' } ,
401- label : { fontSize : '0.875rem' , fontWeight : 500 }
402- } }
403- />
418+ < Tooltip label = "Vim Mode (Ctrl+;)" position = "bottom" >
419+ < Switch
420+ label = "Vim"
421+ checked = { vimModeEnabled }
422+ onChange = { handleVimModeToggle }
423+ size = "sm"
424+ color = "blue"
425+ thumbIcon = { vimModeEnabled ? < IconKeyboard size = { 12 } /> : null }
426+ styles = { {
427+ root : { alignItems : 'center' } ,
428+ label : { fontSize : '0.875rem' , fontWeight : 500 }
429+ } }
430+ />
431+ </ Tooltip >
404432 </ >
405433 ) }
406434 </ Group >
0 commit comments