prosemirror-math 0.0.1 → 0.2.0

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.
@@ -0,0 +1,75 @@
1
+ import type { Node } from 'prosemirror-model'
2
+ import type { Decoration } from 'prosemirror-view'
3
+
4
+ import { hasCursorInsideDecoration } from './cursor-inside-plugin.ts'
5
+
6
+ type RenderMath = (text: string, element: HTMLElement) => void
7
+
8
+ export function createMathViewRender(
9
+ renderMath: RenderMath,
10
+ source: HTMLElement,
11
+ display: HTMLElement,
12
+ inline: boolean,
13
+ ) {
14
+ let prevNode: Node | undefined
15
+ let prevText: string | undefined
16
+ let prevSelected: boolean | undefined
17
+
18
+ function updateDisplay(node: Node) {
19
+ if (node === prevNode) return
20
+ prevNode = node
21
+
22
+ const text = node.textContent
23
+ if (text === prevText) return
24
+ prevText = text
25
+
26
+ renderMath(text, display)
27
+ }
28
+
29
+ function updateStyle(decorations: readonly Decoration[]): void {
30
+ const selected = hasCursorInsideDecoration(decorations)
31
+ if (selected === prevSelected) return
32
+ prevSelected = selected
33
+
34
+ // When the math node is selected, show the source code.
35
+ // Otherwise, show the rendered result.
36
+ display.style.display = selected ? 'none' : ''
37
+ if (!inline) {
38
+ source.style.display = selected ? '' : 'none'
39
+ } else {
40
+ // For inline source code, we don't use `display: none` because we need
41
+ // the source text rendered in the DOM to ensure the text cursor can be
42
+ // placed correctly.
43
+ Object.assign(
44
+ source.style,
45
+ selected ? visibleInlineSourceStyle : hiddenInlineSourceStyle,
46
+ )
47
+ }
48
+ }
49
+
50
+ return function updateMathView(
51
+ node: Node,
52
+ decorations: readonly Decoration[],
53
+ ): void {
54
+ updateDisplay(node)
55
+ updateStyle(decorations)
56
+ }
57
+ }
58
+
59
+ const hiddenInlineSourceStyle: Partial<CSSStyleDeclaration> = {
60
+ display: 'inline-flex',
61
+ opacity: '0',
62
+ pointerEvents: 'none',
63
+ maxWidth: '0',
64
+ maxHeight: '0',
65
+ overflow: 'hidden',
66
+ }
67
+
68
+ const visibleInlineSourceStyle: Partial<CSSStyleDeclaration> = {
69
+ display: 'inline-flex',
70
+ opacity: '1',
71
+ pointerEvents: '',
72
+ maxWidth: '',
73
+ maxHeight: '',
74
+ overflow: '',
75
+ }
package/src/mathjax.ts ADDED
@@ -0,0 +1,28 @@
1
+ import '@mathjax/src/js/input/tex/base/BaseConfiguration.js'
2
+
3
+ import { liteAdaptor } from '@mathjax/src/js/adaptors/liteAdaptor.js'
4
+ import type { MmlNode } from '@mathjax/src/js/core/MmlTree/MmlNode.js'
5
+ import { SerializedMmlVisitor } from '@mathjax/src/js/core/MmlTree/SerializedMmlVisitor.js'
6
+ import { RegisterHTMLHandler } from '@mathjax/src/js/handlers/html.js'
7
+ import { TeX } from '@mathjax/src/js/input/tex.js'
8
+ import { mathjax } from '@mathjax/src/js/mathjax.js'
9
+
10
+ const adaptor = liteAdaptor()
11
+ RegisterHTMLHandler(adaptor)
12
+
13
+ const tex = new TeX({ packages: ['base'] })
14
+ const htmlDocument = mathjax.document('', { InputJax: tex })
15
+ const visitor = new SerializedMmlVisitor()
16
+
17
+ function texToMathML(text: string, display: boolean): string {
18
+ const mathML = htmlDocument.convert(text, { display }) as MmlNode
19
+ return visitor.visitTree(mathML)
20
+ }
21
+
22
+ export function renderMathJaxMathBlock(text: string, element: HTMLElement) {
23
+ element.innerHTML = texToMathML(text, true)
24
+ }
25
+
26
+ export function renderMathJaxMathInline(text: string, element: HTMLElement) {
27
+ element.innerHTML = texToMathML(text, false)
28
+ }
package/src/temml.ts ADDED
@@ -0,0 +1,9 @@
1
+ import Temml from 'temml'
2
+
3
+ export function renderTemmlMathBlock(text: string, element: HTMLElement) {
4
+ Temml.render(text, element, { displayMode: true, annotate: true, throwOnError: false })
5
+ }
6
+
7
+ export function renderTemmlMathInline(text: string, element: HTMLElement) {
8
+ Temml.render(text, element, { displayMode: false, annotate: true, throwOnError: false })
9
+ }
package/src/testing.ts CHANGED
@@ -1,4 +1,4 @@
1
- import '@prosekit/pm/view/style/prosemirror.css'
1
+ import 'prosemirror-view/style/prosemirror.css'
2
2
 
3
3
  import {
4
4
  defineBaseCommands,
@@ -15,18 +15,48 @@ import {
15
15
  type PlainExtension,
16
16
  } from '@prosekit/core'
17
17
  import { createTestEditor, type TestEditor } from '@prosekit/core/test'
18
- import { inputRules } from '@prosekit/pm/inputrules'
19
- import type { Attrs } from '@prosekit/pm/model'
20
18
  import { createEnterRuleCommand, type EnterRule } from 'prosemirror-enter-rules'
21
- import Temml from 'temml'
19
+ import { inputRules } from 'prosemirror-inputrules'
20
+ import type { Attrs } from 'prosemirror-model'
21
+
22
+ import { createCursorInsidePlugin } from './cursor-inside-plugin.ts'
23
+ import { renderKaTeXMathBlock, renderKaTeXMathInline } from './katex.ts'
24
+ import { mathBlockEnterRule } from './math-block-enter-rule.ts'
25
+ import { mathBlockSpec } from './math-block-spec.ts'
26
+ import { createMathBlockView } from './math-block-view.ts'
27
+ import { createMathInlineInputRule } from './math-inline-input-rule.ts'
28
+ import { mathInlineSpec } from './math-inline-spec.ts'
29
+ import { createMathInlineView } from './math-inline-view.ts'
30
+ import { renderMathJaxMathBlock, renderMathJaxMathInline } from './mathjax.ts'
31
+ import { renderTemmlMathBlock, renderTemmlMathInline } from './temml.ts'
32
+
33
+ type RenderMath = (text: string, element: HTMLElement) => void
34
+
35
+ interface MathRenderer {
36
+ renderBlock: RenderMath
37
+ renderInline: RenderMath
38
+ }
39
+
40
+ export const temmlRenderer: MathRenderer = {
41
+ renderBlock: renderTemmlMathBlock,
42
+ renderInline: renderTemmlMathInline,
43
+ }
44
+
45
+ export const katexRenderer: MathRenderer = {
46
+ renderBlock: renderKaTeXMathBlock,
47
+ renderInline: renderKaTeXMathInline,
48
+ }
49
+
50
+ export const mathjaxRenderer: MathRenderer = {
51
+ renderBlock: renderMathJaxMathBlock,
52
+ renderInline: renderMathJaxMathInline,
53
+ }
22
54
 
23
- import { createCursorInsidePlugin } from './cursor-inside-plugin'
24
- import { mathBlockEnterRule } from './math-block-enter-rule'
25
- import { mathBlockSpec } from './math-block-spec'
26
- import { createMathBlockView } from './math-block-view'
27
- import { createMathInlineInputRule } from './math-inline-input-rule'
28
- import { mathInlineSpec } from './math-inline-spec'
29
- import { createMathInlineView } from './math-inline-view'
55
+ export const renderers = {
56
+ temml: temmlRenderer,
57
+ katex: katexRenderer,
58
+ mathjax: mathjaxRenderer,
59
+ } as const
30
60
 
31
61
  type DocExtension = Extension<{ Nodes: { doc: Attrs } }>
32
62
 
@@ -101,24 +131,28 @@ function defineMathInlineSpec(): MathInlineExtension {
101
131
  })
102
132
  }
103
133
 
104
- function defineMathBlockView(): Extension {
134
+ function defineMathBlockView(renderer: MathRenderer): Extension {
105
135
  return defineNodeView({
106
136
  name: 'mathBlock',
107
- constructor: (node) => {
108
- return createMathBlockView(node, (text, element) => {
109
- Temml.render(text, element, { displayMode: true })
110
- })
137
+ constructor: (node, _view, _getPos, decorations) => {
138
+ return createMathBlockView(
139
+ renderer.renderBlock,
140
+ node,
141
+ decorations,
142
+ )
111
143
  },
112
144
  })
113
145
  }
114
146
 
115
- function defineMathInlineView(): Extension {
147
+ function defineMathInlineView(renderer: MathRenderer): Extension {
116
148
  return defineNodeView({
117
149
  name: 'mathInline',
118
- constructor: (node) => {
119
- return createMathInlineView(node, (text, element) => {
120
- Temml.render(text, element, { displayMode: false })
121
- })
150
+ constructor: (node, _view, _getPos, decorations) => {
151
+ return createMathInlineView(
152
+ renderer.renderInline,
153
+ node,
154
+ decorations,
155
+ )
122
156
  },
123
157
  })
124
158
  }
@@ -128,7 +162,7 @@ function defineEnterRule(rules: EnterRule[]): PlainExtension {
128
162
  return defineKeymap({ Enter: command })
129
163
  }
130
164
 
131
- function defineTestExtension() {
165
+ function defineTestExtension(renderer: MathRenderer) {
132
166
  return union(
133
167
  defineDoc(),
134
168
  defineText(),
@@ -136,11 +170,11 @@ function defineTestExtension() {
136
170
  defineCodeBlock(),
137
171
  defineMathBlockSpec(),
138
172
  defineMathInlineSpec(),
139
- defineMathBlockView(),
140
- defineMathInlineView(),
173
+ defineMathBlockView(renderer),
174
+ defineMathInlineView(renderer),
141
175
  defineEnterRule([mathBlockEnterRule]),
142
176
  definePlugin(inputRules({ rules: [createMathInlineInputRule('mathInline')] })),
143
- definePlugin(createCursorInsidePlugin(['mathBlock', 'mathInline'])),
177
+ definePlugin(createCursorInsidePlugin()),
144
178
  defineBaseCommands(),
145
179
  defineBaseKeymap(),
146
180
  )
@@ -164,8 +198,8 @@ function setupTestFromExtension<E extends Extension>(
164
198
  return { editor, n }
165
199
  }
166
200
 
167
- export function setupTest() {
168
- const { editor, n } = setupTestFromExtension(defineTestExtension())
201
+ export function setupTest(renderer: MathRenderer = temmlRenderer) {
202
+ const { editor, n } = setupTestFromExtension(defineTestExtension(renderer))
169
203
 
170
204
  return {
171
205
  editor,