@milkdown/preset-commonmark 7.3.1 → 7.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/lib/__internal__/serialize-text.d.ts +1 -1
  2. package/lib/__internal__/serialize-text.d.ts.map +1 -1
  3. package/lib/__internal__/with-meta.d.ts +1 -1
  4. package/lib/__internal__/with-meta.d.ts.map +1 -1
  5. package/lib/composed/inputrules.d.ts +2 -1
  6. package/lib/composed/inputrules.d.ts.map +1 -1
  7. package/lib/composed/plugins.d.ts.map +1 -1
  8. package/lib/composed/schema.d.ts.map +1 -1
  9. package/lib/index.d.ts.map +1 -1
  10. package/lib/index.es.js +615 -703
  11. package/lib/index.es.js.map +1 -1
  12. package/lib/mark/emphasis.d.ts +2 -0
  13. package/lib/mark/emphasis.d.ts.map +1 -1
  14. package/lib/mark/inline-code.d.ts +1 -0
  15. package/lib/mark/inline-code.d.ts.map +1 -1
  16. package/lib/mark/link.d.ts +2 -2
  17. package/lib/mark/link.d.ts.map +1 -1
  18. package/lib/mark/strong.d.ts +1 -0
  19. package/lib/mark/strong.d.ts.map +1 -1
  20. package/lib/node/heading.d.ts +3 -1
  21. package/lib/node/heading.d.ts.map +1 -1
  22. package/lib/node/hr.d.ts.map +1 -1
  23. package/lib/node/image.d.ts +2 -2
  24. package/lib/node/image.d.ts.map +1 -1
  25. package/lib/node/list-item.d.ts.map +1 -1
  26. package/lib/plugin/index.d.ts +0 -1
  27. package/lib/plugin/index.d.ts.map +1 -1
  28. package/package.json +9 -9
  29. package/src/__internal__/serialize-text.ts +1 -1
  30. package/src/__internal__/with-meta.ts +1 -4
  31. package/src/composed/inputrules.ts +10 -1
  32. package/src/composed/plugins.ts +2 -6
  33. package/src/composed/schema.ts +2 -1
  34. package/src/index.ts +2 -2
  35. package/src/mark/emphasis.ts +28 -1
  36. package/src/mark/inline-code.ts +12 -1
  37. package/src/mark/link.ts +1 -1
  38. package/src/mark/strong.ts +12 -1
  39. package/src/node/code-block.ts +1 -1
  40. package/src/node/heading.ts +3 -2
  41. package/src/node/hr.ts +2 -1
  42. package/src/node/image.ts +1 -1
  43. package/src/node/list-item.ts +24 -22
  44. package/src/plugin/hardbreak-clear-mark-plugin.ts +1 -1
  45. package/src/plugin/index.ts +0 -2
  46. package/src/plugin/remark-html-transformer.ts +1 -1
  47. package/lib/plugin/inline-sync-plugin/config.d.ts +0 -24
  48. package/lib/plugin/inline-sync-plugin/config.d.ts.map +0 -1
  49. package/lib/plugin/inline-sync-plugin/context.d.ts +0 -11
  50. package/lib/plugin/inline-sync-plugin/context.d.ts.map +0 -1
  51. package/lib/plugin/inline-sync-plugin/index.d.ts +0 -3
  52. package/lib/plugin/inline-sync-plugin/index.d.ts.map +0 -1
  53. package/lib/plugin/inline-sync-plugin/inline-sync-plugin.d.ts +0 -2
  54. package/lib/plugin/inline-sync-plugin/inline-sync-plugin.d.ts.map +0 -1
  55. package/lib/plugin/inline-sync-plugin/regexp.d.ts +0 -9
  56. package/lib/plugin/inline-sync-plugin/regexp.d.ts.map +0 -1
  57. package/lib/plugin/inline-sync-plugin/replacer.d.ts +0 -5
  58. package/lib/plugin/inline-sync-plugin/replacer.d.ts.map +0 -1
  59. package/lib/plugin/inline-sync-plugin/utils.d.ts +0 -9
  60. package/lib/plugin/inline-sync-plugin/utils.d.ts.map +0 -1
  61. package/src/plugin/inline-sync-plugin/config.ts +0 -78
  62. package/src/plugin/inline-sync-plugin/context.ts +0 -121
  63. package/src/plugin/inline-sync-plugin/index.ts +0 -4
  64. package/src/plugin/inline-sync-plugin/inline-sync-plugin.ts +0 -72
  65. package/src/plugin/inline-sync-plugin/regexp.ts +0 -15
  66. package/src/plugin/inline-sync-plugin/replacer.ts +0 -56
  67. package/src/plugin/inline-sync-plugin/utils.ts +0 -92
@@ -1,78 +0,0 @@
1
- /* Copyright 2021, Milkdown by Mirone. */
2
- import type { Ctx } from '@milkdown/ctx'
3
- import type { Node, NodeType } from '@milkdown/prose/model'
4
- import type { Transaction } from '@milkdown/prose/state'
5
- import { $ctx } from '@milkdown/utils'
6
-
7
- import { withMeta } from '../../__internal__'
8
- import { swap } from './utils'
9
-
10
- /// @internal
11
- export type ShouldSyncNode = (context: {
12
- prevNode: Node
13
- nextNode: Node
14
- ctx: Ctx
15
- tr: Transaction
16
- text: string
17
- }) => boolean
18
-
19
- /// @internal
20
- export interface SyncNodePlaceholder {
21
- hole: string
22
- punctuation: string
23
- char: string
24
- }
25
-
26
- /// @internal
27
- export interface InlineSyncConfig {
28
- placeholderConfig: SyncNodePlaceholder
29
- shouldSyncNode: ShouldSyncNode
30
- globalNodes: Array<NodeType | string>
31
- movePlaceholder: (placeholderToMove: string, text: string) => string
32
- }
33
-
34
- /// @internal
35
- export const defaultConfig: InlineSyncConfig = {
36
- placeholderConfig: {
37
- hole: '∅',
38
- punctuation: '⁂',
39
- char: '∴',
40
- },
41
- globalNodes: ['footnote_definition'],
42
- shouldSyncNode: ({ prevNode, nextNode }) =>
43
- prevNode.inlineContent
44
- && nextNode
45
- // if node type changes, do not sync
46
- && prevNode.type === nextNode.type
47
- // if two node fully equal, we don't modify them
48
- && !prevNode.eq(nextNode),
49
- movePlaceholder: (placeholderToMove: string, text: string) => {
50
- const symbolsNeedToMove = ['*', '_']
51
-
52
- let index = text.indexOf(placeholderToMove)
53
- while (symbolsNeedToMove.includes(text[index - 1] || '') && symbolsNeedToMove.includes(text[index + 1] || '')) {
54
- text = swap(text, index, index + 1)
55
- index = index + 1
56
- }
57
-
58
- return text
59
- },
60
- }
61
-
62
- /// A slice that contains the inline sync config.
63
- /// You can set value to this slice to change the config.
64
- ///
65
- /// ```typescript
66
- /// ctx.update(inlineSyncConfigCtx, (prevCfg) => ({
67
- /// ...prevCfg,
68
- /// // your config
69
- /// }));
70
- /// ```
71
- ///
72
- /// You can find the default config [here](https://github.com/Milkdown/milkdown/blob/main/packages/preset-commonmark/src/plugin/inline-sync-plugin/config.ts).
73
- export const inlineSyncConfig = $ctx<InlineSyncConfig, 'inlineSyncConfig'>(defaultConfig, 'inlineSyncConfig')
74
-
75
- withMeta(inlineSyncConfig, {
76
- displayName: 'Ctx<inlineSyncConfig>',
77
- group: 'Prose',
78
- })
@@ -1,121 +0,0 @@
1
- /* Copyright 2021, Milkdown by Mirone. */
2
- import type { Ctx } from '@milkdown/ctx'
3
- import { parserCtx, serializerCtx } from '@milkdown/core'
4
- import type { Node } from '@milkdown/prose/model'
5
- import type { EditorState } from '@milkdown/prose/state'
6
- import { pipe } from '@milkdown/utils'
7
-
8
- import { inlineSyncConfig } from './config'
9
- import { calculatePlaceholder, keepLink, mergeSlash, replacePunctuation } from './utils'
10
- import { asterisk, asteriskHolder, underline, underlineHolder } from './regexp'
11
-
12
- export interface InlineSyncContext {
13
- text: string
14
- prevNode: Node
15
- nextNode: Node
16
- placeholder: string
17
- }
18
-
19
- const getNodeFromSelection = (state: EditorState) => {
20
- return state.selection.$from.node()
21
- }
22
-
23
- const getMarkdown = (ctx: Ctx, state: EditorState, node: Node, globalNode: Node[]) => {
24
- const serializer = ctx.get(serializerCtx)
25
- const doc = state.schema.topNodeType.create(undefined, [node, ...globalNode])
26
-
27
- return serializer(doc)
28
- }
29
-
30
- const addPlaceholder = (ctx: Ctx, markdown: string) => {
31
- const config = ctx.get(inlineSyncConfig.key)
32
- const holePlaceholder = config.placeholderConfig.hole
33
-
34
- const [firstLine = '', ...rest] = markdown.split('\n\n')
35
-
36
- const movePlaceholder = (text: string) => config.movePlaceholder(holePlaceholder, text)
37
-
38
- const handleText = pipe(replacePunctuation(holePlaceholder), movePlaceholder, keepLink, mergeSlash)
39
-
40
- let text = handleText(firstLine)
41
- const placeholder = calculatePlaceholder(config.placeholderConfig)(text)
42
-
43
- text = text.replace(holePlaceholder, placeholder)
44
-
45
- text = [text, ...rest].join('\n\n')
46
-
47
- return [text, placeholder] as [markdown: string, placeholder: string]
48
- }
49
-
50
- const getNewNode = (ctx: Ctx, text: string) => {
51
- const parser = ctx.get(parserCtx)
52
- const parsed = parser(text)
53
-
54
- if (!parsed)
55
- return null
56
-
57
- return parsed.firstChild
58
- }
59
-
60
- const collectGlobalNodes = (ctx: Ctx, state: EditorState) => {
61
- const { globalNodes } = ctx.get(inlineSyncConfig.key)
62
- const nodes: Node[] = []
63
-
64
- state.doc.descendants((node) => {
65
- if (globalNodes.includes(node.type.name) || globalNodes.includes(node.type)) {
66
- nodes.push(node)
67
- return false
68
- }
69
-
70
- return undefined
71
- })
72
-
73
- return nodes
74
- }
75
-
76
- const removeGlobalFromText = (text: string) => text.split('\n\n')[0] || ''
77
-
78
- const onlyHTML = (node: Node) => {
79
- return node.childCount === 1 && node.child(0).type.name === 'html'
80
- }
81
-
82
- export const getContextByState = (ctx: Ctx, state: EditorState): InlineSyncContext | null => {
83
- try {
84
- const globalNode = collectGlobalNodes(ctx, state)
85
- const node = getNodeFromSelection(state)
86
-
87
- const markdown = getMarkdown(ctx, state, node, globalNode)
88
- const [text, placeholder] = addPlaceholder(ctx, markdown)
89
-
90
- const newNode = getNewNode(ctx, text)
91
-
92
- if (!newNode || node.type !== newNode.type || onlyHTML(newNode))
93
- return null
94
-
95
- // @ts-expect-error hijack the node attribute
96
- newNode.attrs = { ...node.attrs }
97
-
98
- newNode.descendants((node) => {
99
- const marks = node.marks
100
- const link = marks.find(mark => mark.type.name === 'link')
101
- if (link && node.text?.includes(placeholder) && link.attrs.href.includes(placeholder)) {
102
- // @ts-expect-error hijack the mark attribute
103
- link.attrs.href = link.attrs.href.replace(placeholder, '')
104
- }
105
- if (node.text?.includes(asteriskHolder) || node.text?.includes(underlineHolder)) {
106
- // @ts-expect-error hijack the attribute
107
- node.text = node.text.replaceAll(asteriskHolder, asterisk).replaceAll(underlineHolder, underline)
108
- }
109
- })
110
-
111
- return {
112
- text: removeGlobalFromText(text),
113
- prevNode: node,
114
- nextNode: newNode,
115
- placeholder,
116
- }
117
- }
118
- catch {
119
- return null
120
- }
121
- }
@@ -1,4 +0,0 @@
1
- /* Copyright 2021, Milkdown by Mirone. */
2
-
3
- export * from './config'
4
- export * from './inline-sync-plugin'
@@ -1,72 +0,0 @@
1
- /* Copyright 2021, Milkdown by Mirone. */
2
- import type { Ctx } from '@milkdown/ctx'
3
- import { editorViewCtx } from '@milkdown/core'
4
- import { Plugin, PluginKey } from '@milkdown/prose/state'
5
- import { $prose } from '@milkdown/utils'
6
-
7
- import { withMeta } from '../../__internal__'
8
- import { inlineSyncConfig } from './config'
9
- import { getContextByState } from './context'
10
- import { runReplacer } from './replacer'
11
-
12
- /// This plugin is used to sync the inline mark.
13
- /// It will create and remove marks automatically according to the user input.
14
- ///
15
- /// When users type something, the plugin will transform the line (for better performance) to real markdown AST by serializer
16
- /// and render the AST to dom by parser, thus the input texts can be displayed correctly.
17
- export const inlineSyncPlugin = $prose((ctx: Ctx) => {
18
- let requestId: number | null = null
19
- const inlineSyncPluginKey = new PluginKey('MILKDOWN_INLINE_SYNC')
20
-
21
- return new Plugin<null>({
22
- key: inlineSyncPluginKey,
23
- state: {
24
- init: () => {
25
- return null
26
- },
27
- apply: (tr, _value, _oldState, newState) => {
28
- const view = ctx.get(editorViewCtx)
29
- if (!view.hasFocus?.() || !view.editable)
30
- return null
31
-
32
- if (!tr.docChanged)
33
- return null
34
-
35
- const meta = tr.getMeta(inlineSyncPluginKey)
36
- if (meta)
37
- return null
38
-
39
- const context = getContextByState(ctx, newState)
40
- if (!context)
41
- return null
42
-
43
- if (requestId) {
44
- cancelAnimationFrame(requestId)
45
- requestId = null
46
- }
47
-
48
- const { prevNode, nextNode, text } = context
49
-
50
- const { shouldSyncNode } = ctx.get(inlineSyncConfig.key)
51
-
52
- if (!shouldSyncNode({ prevNode, nextNode, ctx, tr, text }))
53
- return null
54
-
55
- requestId = requestAnimationFrame(() => {
56
- requestId = null
57
-
58
- const { dispatch, state } = ctx.get(editorViewCtx)
59
-
60
- runReplacer(ctx, inlineSyncPluginKey, state, dispatch, prevNode.attrs)
61
- })
62
-
63
- return null
64
- },
65
- },
66
- })
67
- })
68
-
69
- withMeta(inlineSyncPlugin, {
70
- displayName: 'Prose<inlineSyncPlugin>',
71
- group: 'Prose',
72
- })
@@ -1,15 +0,0 @@
1
- /* Copyright 2021, Milkdown by Mirone. */
2
-
3
- export const linkRegexp = /\[([^\]]+)]\([^\s\]]+\)/
4
-
5
- export const keepLinkRegexp = /\[(?<span>((www|https:\/\/|http:\/\/)[^\s\]]+))]\((?<url>[^\s\]]+)\)/
6
-
7
- export const punctuationRegexp = (holePlaceholder: string) =>
8
- new RegExp(`\\\\(?=[^\\w\\s${holePlaceholder}\\\\]|_)`, 'g')
9
-
10
- export const ZERO_WIDTH_SPACE = '\u200B'
11
-
12
- export const asterisk = `${ZERO_WIDTH_SPACE}*`
13
- export const asteriskHolder = `${ZERO_WIDTH_SPACE}*`
14
- export const underline = `${ZERO_WIDTH_SPACE}_`
15
- export const underlineHolder = `${ZERO_WIDTH_SPACE}⎽`
@@ -1,56 +0,0 @@
1
- /* Copyright 2021, Milkdown by Mirone. */
2
- import type { Ctx } from '@milkdown/ctx'
3
- import type { Attrs } from '@milkdown/prose/model'
4
- import type { EditorState, PluginKey, Transaction } from '@milkdown/prose/state'
5
- import { TextSelection } from '@milkdown/prose/state'
6
-
7
- import { inlineSyncConfig } from './config'
8
- import { getContextByState } from './context'
9
- import { calcOffset } from './utils'
10
- import { linkRegexp } from './regexp'
11
-
12
- export const runReplacer = (
13
- ctx: Ctx,
14
- key: PluginKey,
15
- state: EditorState,
16
- dispatch: (tr: Transaction) => void,
17
- attrs: Attrs,
18
- ) => {
19
- const { placeholderConfig } = ctx.get(inlineSyncConfig.key)
20
- const holePlaceholder = placeholderConfig.hole
21
- // insert a placeholder to restore the selection
22
- let tr = state.tr.setMeta(key, true).insertText(holePlaceholder, state.selection.from)
23
-
24
- const nextState = state.apply(tr)
25
- const context = getContextByState(ctx, nextState)
26
-
27
- if (!context)
28
- return
29
-
30
- const lastUserInput = context.text.slice(0, context.text.indexOf(context.placeholder))
31
-
32
- const { $from } = nextState.selection
33
- const from = $from.before()
34
- const to = $from.after()
35
-
36
- const offset = calcOffset(context.nextNode, from, context.placeholder)
37
-
38
- tr = tr
39
- .replaceWith(from, to, context.nextNode)
40
- .setNodeMarkup(from, undefined, attrs)
41
- // delete the placeholder
42
- .delete(offset + 1, offset + 2)
43
-
44
- // restore the selection
45
- tr = tr.setSelection(TextSelection.near(tr.doc.resolve(offset + 1)))
46
-
47
- const needsRestoreMark = linkRegexp.test(lastUserInput) || ['*', '_', '~'].includes(lastUserInput.at(-1) || '')
48
- if (needsRestoreMark && tr.selection instanceof TextSelection) {
49
- const marks = tr.selection.$cursor?.marks() ?? []
50
- marks.forEach((mark) => {
51
- tr = tr.removeStoredMark(mark.type)
52
- })
53
- }
54
-
55
- dispatch(tr)
56
- }
@@ -1,92 +0,0 @@
1
- /* Copyright 2021, Milkdown by Mirone. */
2
-
3
- import type { Node } from '@milkdown/prose/model'
4
-
5
- import type { SyncNodePlaceholder } from './config'
6
- import {
7
- asterisk,
8
- asteriskHolder,
9
- keepLinkRegexp,
10
- punctuationRegexp,
11
- underline,
12
- underlineHolder,
13
- } from './regexp'
14
-
15
- export const keepLink = (str: string) => {
16
- let text = str
17
- let match = text.match(keepLinkRegexp)
18
- while (match && match.groups) {
19
- const { span } = match.groups
20
- text = text.replace(keepLinkRegexp, span as string)
21
-
22
- match = text.match(keepLinkRegexp)
23
- }
24
- return text
25
- }
26
-
27
- export const mergeSlash = (str: string) => {
28
- return str
29
- .replaceAll(/\\\\\*/g, asterisk)
30
- .replaceAll(/\\\\_/g, underline)
31
- .replaceAll(asterisk, asteriskHolder)
32
- .replaceAll(underline, underlineHolder)
33
- }
34
-
35
- export const swap = (text: string, first: number, last: number) => {
36
- const arr = text.split('')
37
- const temp = arr[first]
38
- if (arr[first] && arr[last]) {
39
- arr[first] = arr[last] as string
40
- arr[last] = temp as string
41
- }
42
- return arr.join('').toString()
43
- }
44
-
45
- export const replacePunctuation = (holePlaceholder: string) => (text: string) =>
46
- text.replace(punctuationRegexp(holePlaceholder), '')
47
-
48
- export const calculatePlaceholder = (placeholder: SyncNodePlaceholder) => (text: string) => {
49
- const index = text.indexOf(placeholder.hole)
50
- const left = text.charAt(index - 1)
51
- const right = text.charAt(index + 1)
52
- const notAWord = /[^\w]|_/
53
-
54
- // cursor on the right
55
- if (!right)
56
- return placeholder.punctuation
57
-
58
- // cursor on the left
59
- if (!left)
60
- return placeholder.char
61
-
62
- if (notAWord.test(left) && notAWord.test(right))
63
- return placeholder.punctuation
64
-
65
- return placeholder.char
66
- }
67
-
68
- export const calcOffset = (node: Node, from: number, placeholder: string) => {
69
- let offset = from
70
- let find = false
71
- node.descendants((n) => {
72
- if (find)
73
- return false
74
- if (!n.textContent.includes(placeholder)) {
75
- offset += n.nodeSize
76
- return false
77
- }
78
- if (n.isText) {
79
- const i = n.text?.indexOf(placeholder)
80
- if (i != null && i >= 0) {
81
- find = true
82
- offset += i
83
- return false
84
- }
85
- }
86
-
87
- // enter the node
88
- offset += 1
89
- return true
90
- })
91
- return offset
92
- }