Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/core/src/extensions/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { defineMeowdownParagraph } from './paragraph.ts'
import { definePendingReplacement } from './pending-replacement.ts'
import { defineScrollToSelection } from './scroll-to-selection.ts'
import { defineSelectDocBoundary } from './select-doc-boundary.ts'
import { defineSystemSubstitutionGuard } from './system-substitution-guard.ts'
import { defineTable } from './table.ts'
import { defineViewAttributes } from './view-attributes.ts'
import { defineWikilink } from './wikilink.ts'
Expand Down Expand Up @@ -77,6 +78,7 @@ function defineEditorExtensionImpl(options: EditorExtensionOptions) {
defineClipboard(),
defineScrollToSelection(),
defineHiddenRunCaret(),
defineSystemSubstitutionGuard(),
defineAtomMarkNavigation({
marks: ATOM_SOURCE_MARK_NAMES.map((name) => ({ name, modes: ['hide', 'focus', 'show'] })),
}),
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/extensions/spell-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ function createSpellCheckPlugin(spellCheck: boolean) {
})
}

/**
* @deprecated This will be removed in future versions.
*/
export function defineSpellCheckPlugin(spellCheck: boolean): PlainExtension {
return definePlugin(createSpellCheckPlugin(spellCheck))
}
41 changes: 41 additions & 0 deletions packages/core/src/extensions/system-substitution-guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { definePlugin, Priority, withPriority, type PlainExtension } from '@prosekit/core'
import { Plugin } from '@prosekit/pm/state'

const EM_DASH = '\u{2014}'

/**
* Block the macOS "smart dashes" rewrite. With the `spellcheck` attribute on,
* WebKit rewrites already-typed `--` near the caret into an em dash and
* delivers the rewrite as a cancelable `beforeinput` with
* `inputType: 'insertReplacementText'`. That corrupts Markdown such as the
* `-->` closing an image sizing comment, so cancel any replacement that
* inserts an em dash; word-level autocorrect passes through.
*/
export function defineSystemSubstitutionGuard(): PlainExtension {
const plugin = new Plugin({
props: {
handleDOMEvents: {
beforeinput: (view, event) => {
if (event.inputType !== 'insertReplacementText') return false

const replacement = event.dataTransfer?.getData('text/plain') || event.data || ''
if (!replacement.includes(EM_DASH)) return false

event.preventDefault()

// Restore the selection after the cancelation. See WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=321420
const { doc, selection } = view.state
requestAnimationFrame(() => {
const { state } = view
if (view.isDestroyed || state.doc !== doc || state.selection.eq(selection)) return
view.dispatch(state.tr.setSelection(selection))
})

return true
},
},
},
})

return withPriority(definePlugin(plugin), Priority.highest)
}
14 changes: 7 additions & 7 deletions packages/react/src/components/editor-extensions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
definePlaceholder,
defineReadonly,
defineSearchStatusHandler,
defineSpellCheckPlugin,
defineSubstitution,
defineTagClickHandler,
defineViewAttributes,
Expand Down Expand Up @@ -106,6 +105,13 @@ export function EditorExtensions({
return editor.use(extension)
}, [editor, editorClassName])

// Set spellCheck
useLayoutEffect(() => {
if (spellCheck == null) return
const extension = defineViewAttributes({ spellcheck: spellCheck ? 'true' : 'false' })
return editor.use(extension)
}, [editor, spellCheck])

// Search has no latency requirement, so a slow device may skip the
// intermediate values of a fast typist entirely. `literal` turns off
// prosemirror-search's escape-sequence handling: a find bar takes text, so a
Expand Down Expand Up @@ -242,11 +248,5 @@ export function EditorExtensions({
}, [wikilinkEnabled]),
)

useExtension(
useMemo(() => {
return spellCheck == null ? null : defineSpellCheckPlugin(spellCheck)
}, [spellCheck]),
)

return null
}
4 changes: 2 additions & 2 deletions website/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import {
import { getId } from '@ocavue/utils'
import { clsx } from 'clsx/lite'
import {
type CSSProperties,
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
type CSSProperties,
} from 'react'

import { DemoEditor } from './components/demo-editor.tsx'
Expand Down Expand Up @@ -160,7 +160,7 @@ Outline your thoughts with nested bullets. Hover a bullet that has children and

Drop in an image and it renders right where you wrote it. Paste or drag one in to upload your own:

Small images flow inline ![](https://static.photos/yellow/16x16/3) with the surrounding text.
Small images flow inline ![](https://static.photos/yellow/16x16/3)<!--{"width":128,"height":128}--> with the surrounding text.

Paste a YouTube or tweet link and it embeds itself. Undo once to get the plain link back:

Expand Down
Loading