style(rich-md): show pointer cursor on links only while modifier held (#814)

Rich markdown links open on Cmd/Ctrl-click (plain click must place the
caret inside contenteditable). Toggle a root class on keydown/keyup of
the platform modifier so the pointer cursor appears only while the
modifier is held, matching VS Code's link affordance.
This commit is contained in:
Jinjing 2026-04-18 21:13:00 -07:00 committed by GitHub
parent 58b43e0d17
commit 159081c2e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 55 additions and 0 deletions

View file

@ -490,6 +490,15 @@
text-decoration: underline;
text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
text-underline-offset: 2px;
}
/* Why: plain click places the caret inside the contenteditable region, so the
link only activates on Cmd/Ctrl-click. Surface the pointer affordance only
while that modifier is held so users can discover it without being misled
into expecting a plain click to open. Class is toggled by RichMarkdownEditor
in response to keydown/keyup of the platform modifier. */
.rich-markdown-mod-held .rich-markdown-editor a,
.rich-markdown-mod-held .rich-markdown-editor .ProseMirror a {
cursor: pointer;
}

View file

@ -18,6 +18,7 @@ import {
} from './RichMarkdownLinkBubble'
import { useLinkBubble } from './useLinkBubble'
import { useEditorScrollRestore } from './useEditorScrollRestore'
import { useModifierHeldClass } from './useModifierHeldClass'
import { registerPendingEditorFlush } from './editor-pending-flush'
import { createRichMarkdownKeyHandler } from './rich-markdown-key-handler'
import { normalizeSoftBreaks } from './rich-markdown-normalize'
@ -314,6 +315,8 @@ export default function RichMarkdownEditor({
useEditorScrollRestore(scrollContainerRef, scrollCacheKey, editor)
useModifierHeldClass(rootRef, isMac)
// Why: the custom Image extension reads filePath from editor.storage to resolve
// relative image src values to file:// URLs for display. After updating the
// stored path we dispatch a no-op transaction so ProseMirror re-renders image

View file

@ -0,0 +1,43 @@
import { useEffect, type RefObject } from 'react'
// Why: plain click inside a contenteditable places the caret, so markdown links
// only open on Cmd/Ctrl-click. Toggling this class while the platform modifier
// is held lets CSS surface a pointer cursor only at that moment — matching
// VS Code's link affordance without misleading the user into expecting a plain
// click to open.
export function useModifierHeldClass(
targetRef: RefObject<HTMLElement | null>,
isMac: boolean,
className = 'rich-markdown-mod-held'
): void {
useEffect(() => {
const target = targetRef.current
if (!target) {
return
}
const modKey = isMac ? 'Meta' : 'Control'
const update = (pressed: boolean): void => {
target.classList.toggle(className, pressed)
}
const onKeyDown = (e: KeyboardEvent): void => {
if (e.key === modKey) {
update(true)
}
}
const onKeyUp = (e: KeyboardEvent): void => {
if (e.key === modKey) {
update(false)
}
}
const onBlur = (): void => update(false)
window.addEventListener('keydown', onKeyDown)
window.addEventListener('keyup', onKeyUp)
window.addEventListener('blur', onBlur)
return () => {
window.removeEventListener('keydown', onKeyDown)
window.removeEventListener('keyup', onKeyUp)
window.removeEventListener('blur', onBlur)
update(false)
}
}, [targetRef, isMac, className])
}