@puredesktop/platform-editor 1.0.0-beta.1 → 2.1.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.
- package/package.json +1 -1
- package/src/CollapsePanel.tsx +35 -35
- package/src/DocumentDiffView.tsx +130 -130
- package/src/DocumentEditor.test.ts +118 -118
- package/src/DocumentEditor.tsx +2651 -2631
- package/src/alignedLineDiff.test.ts +52 -52
- package/src/alignedLineDiff.ts +67 -67
- package/src/collectionAssetSrc.ts +2 -2
- package/src/commentUtils.test.ts +350 -350
- package/src/commentUtils.ts +546 -546
- package/src/constants/toolKeys.ts +14 -14
- package/src/contentFormat.test.ts +27 -27
- package/src/contentFormat.ts +18 -18
- package/src/editorExtensions.ts +155 -155
- package/src/extensions/appliedChangeMark.ts +115 -115
- package/src/extensions/autoReviewPrompts.test.ts +529 -529
- package/src/extensions/autoReviewPrompts.ts +1442 -1442
- package/src/extensions/collectionImage.ts +26 -26
- package/src/extensions/collectionImagePaste.ts +215 -215
- package/src/extensions/commentMark.ts +223 -223
- package/src/extensions/documentAssetIdentity.ts +54 -54
- package/src/extensions/figure.ts +92 -92
- package/src/extensions/footnote.ts +186 -186
- package/src/extensions/indexMarker.ts +88 -88
- package/src/extensions/mathEditing.ts +239 -239
- package/src/extensions/mermaidBlock.tsx +297 -297
- package/src/extensions/slashCommands.test.ts +170 -170
- package/src/extensions/slashCommands.ts +746 -746
- package/src/extensions/smartTypography.test.ts +74 -74
- package/src/extensions/smartTypography.ts +120 -120
- package/src/index.ts +138 -137
- package/src/insertCollectionImage.test.ts +60 -60
- package/src/insertCollectionImage.ts +63 -63
- package/src/insertInlineAssetKind.test.ts +67 -67
- package/src/insertInlineAssetKind.ts +49 -49
- package/src/mermaidPreview.test.ts +54 -54
- package/src/mermaidPreview.ts +115 -115
- package/src/styled.ts +697 -676
- package/src/toolbar/ToolBtn.tsx +77 -63
- package/src/toolbar/Toolbar.test.tsx +325 -325
- package/src/toolbar/Toolbar.tsx +662 -624
- package/src/toolbar/groups/HeadingGroup.tsx +175 -153
- package/src/toolbar/overlayTypes.ts +28 -28
- package/src/useEditorExtensions.ts +22 -22
- package/src/utils/markdownUtils.ts +331 -331
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
export const TOOL_KEYS = {
|
|
2
|
-
LINK: 'link',
|
|
3
|
-
BULLET_LIST: 'bulletList',
|
|
4
|
-
ORDERED_LIST: 'orderedList',
|
|
5
|
-
CODE: 'code',
|
|
6
|
-
BOLD: 'bold',
|
|
7
|
-
ITALIC: 'italic',
|
|
8
|
-
UNDERLINE: 'underline',
|
|
9
|
-
TASK_LIST: 'taskList',
|
|
10
|
-
BLOCKQUOTE: 'blockquote',
|
|
11
|
-
CODE_BLOCK: 'codeBlock',
|
|
12
|
-
INDEX_MARKER: 'indexMarker',
|
|
13
|
-
TABLE: 'table',
|
|
14
|
-
} as const
|
|
1
|
+
export const TOOL_KEYS = {
|
|
2
|
+
LINK: 'link',
|
|
3
|
+
BULLET_LIST: 'bulletList',
|
|
4
|
+
ORDERED_LIST: 'orderedList',
|
|
5
|
+
CODE: 'code',
|
|
6
|
+
BOLD: 'bold',
|
|
7
|
+
ITALIC: 'italic',
|
|
8
|
+
UNDERLINE: 'underline',
|
|
9
|
+
TASK_LIST: 'taskList',
|
|
10
|
+
BLOCKQUOTE: 'blockquote',
|
|
11
|
+
CODE_BLOCK: 'codeBlock',
|
|
12
|
+
INDEX_MARKER: 'indexMarker',
|
|
13
|
+
TABLE: 'table',
|
|
14
|
+
} as const
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { resolveEditorContent } from './contentFormat.js'
|
|
3
|
-
|
|
4
|
-
describe('resolveEditorContent', () => {
|
|
5
|
-
it('parses markdown with inline index-marker HTML when source is markdown', () => {
|
|
6
|
-
const input = `# The Permissions Question
|
|
7
|
-
|
|
8
|
-
Every product eventually faces a <a class="ix" data-term="permissions">permissions</a> question.
|
|
9
|
-
|
|
10
|
-
## A test that doesn't exist`
|
|
11
|
-
|
|
12
|
-
const resolved = resolveEditorContent(input, 'markdown')
|
|
13
|
-
|
|
14
|
-
expect(resolved).toContain('<h1>The Permissions Question</h1>')
|
|
15
|
-
expect(resolved).toContain('<h2>A test that doesn't exist</h2>')
|
|
16
|
-
expect(resolved).toContain(
|
|
17
|
-
'<p>Every product eventually faces a <a class="ix" data-term="permissions">permissions</a> question.</p>',
|
|
18
|
-
)
|
|
19
|
-
expect(resolved).not.toContain('# The Permissions Question')
|
|
20
|
-
expect(resolved).not.toContain("## A test that doesn't exist")
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('keeps real html fragments untouched when source is html', () => {
|
|
24
|
-
const input = '<h1>Introduction</h1><p>Already html</p>'
|
|
25
|
-
expect(resolveEditorContent(input, 'html')).toBe(input)
|
|
26
|
-
})
|
|
27
|
-
})
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { resolveEditorContent } from './contentFormat.js'
|
|
3
|
+
|
|
4
|
+
describe('resolveEditorContent', () => {
|
|
5
|
+
it('parses markdown with inline index-marker HTML when source is markdown', () => {
|
|
6
|
+
const input = `# The Permissions Question
|
|
7
|
+
|
|
8
|
+
Every product eventually faces a <a class="ix" data-term="permissions">permissions</a> question.
|
|
9
|
+
|
|
10
|
+
## A test that doesn't exist`
|
|
11
|
+
|
|
12
|
+
const resolved = resolveEditorContent(input, 'markdown')
|
|
13
|
+
|
|
14
|
+
expect(resolved).toContain('<h1>The Permissions Question</h1>')
|
|
15
|
+
expect(resolved).toContain('<h2>A test that doesn't exist</h2>')
|
|
16
|
+
expect(resolved).toContain(
|
|
17
|
+
'<p>Every product eventually faces a <a class="ix" data-term="permissions">permissions</a> question.</p>',
|
|
18
|
+
)
|
|
19
|
+
expect(resolved).not.toContain('# The Permissions Question')
|
|
20
|
+
expect(resolved).not.toContain("## A test that doesn't exist")
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('keeps real html fragments untouched when source is html', () => {
|
|
24
|
+
const input = '<h1>Introduction</h1><p>Already html</p>'
|
|
25
|
+
expect(resolveEditorContent(input, 'html')).toBe(input)
|
|
26
|
+
})
|
|
27
|
+
})
|
package/src/contentFormat.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { toHtml } from './utils/markdownUtils.js'
|
|
2
|
-
|
|
3
|
-
export type EditorInputFormat = 'auto' | 'markdown' | 'html'
|
|
4
|
-
|
|
5
|
-
const looksLikeHtmlDocument = (str: string): boolean =>
|
|
6
|
-
/^\s*<(?:!doctype|html|body|article|section|main|div|p|h[1-6]|ul|ol|li|blockquote|table|figure)\b/i.test(
|
|
7
|
-
str,
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
export function resolveEditorContent(
|
|
11
|
-
value: string,
|
|
12
|
-
inputFormat: EditorInputFormat = 'auto',
|
|
13
|
-
): string {
|
|
14
|
-
if (!value) return value
|
|
15
|
-
if (inputFormat === 'markdown') return toHtml(value)
|
|
16
|
-
if (inputFormat === 'html') return value
|
|
17
|
-
return looksLikeHtmlDocument(value) ? value : toHtml(value)
|
|
18
|
-
}
|
|
1
|
+
import { toHtml } from './utils/markdownUtils.js'
|
|
2
|
+
|
|
3
|
+
export type EditorInputFormat = 'auto' | 'markdown' | 'html'
|
|
4
|
+
|
|
5
|
+
const looksLikeHtmlDocument = (str: string): boolean =>
|
|
6
|
+
/^\s*<(?:!doctype|html|body|article|section|main|div|p|h[1-6]|ul|ol|li|blockquote|table|figure)\b/i.test(
|
|
7
|
+
str,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
export function resolveEditorContent(
|
|
11
|
+
value: string,
|
|
12
|
+
inputFormat: EditorInputFormat = 'auto',
|
|
13
|
+
): string {
|
|
14
|
+
if (!value) return value
|
|
15
|
+
if (inputFormat === 'markdown') return toHtml(value)
|
|
16
|
+
if (inputFormat === 'html') return value
|
|
17
|
+
return looksLikeHtmlDocument(value) ? value : toHtml(value)
|
|
18
|
+
}
|
package/src/editorExtensions.ts
CHANGED
|
@@ -1,155 +1,155 @@
|
|
|
1
|
-
import StarterKit from '@tiptap/starter-kit'
|
|
2
|
-
import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'
|
|
3
|
-
import { CharacterCount } from '@tiptap/extension-character-count'
|
|
4
|
-
import Placeholder from '@tiptap/extension-placeholder'
|
|
5
|
-
import { Mathematics } from '@tiptap/extension-mathematics'
|
|
6
|
-
import {
|
|
7
|
-
Table,
|
|
8
|
-
TableCell,
|
|
9
|
-
TableHeader,
|
|
10
|
-
TableRow,
|
|
11
|
-
} from '@tiptap/extension-table'
|
|
12
|
-
import { Subscript } from '@tiptap/extension-subscript'
|
|
13
|
-
import { Superscript } from '@tiptap/extension-superscript'
|
|
14
|
-
import { TaskList } from '@tiptap/extension-task-list'
|
|
15
|
-
import { TaskItem } from '@tiptap/extension-task-item'
|
|
16
|
-
import { TextAlign } from '@tiptap/extension-text-align'
|
|
17
|
-
import { Highlight } from '@tiptap/extension-highlight'
|
|
18
|
-
import { Image } from '@tiptap/extension-image'
|
|
19
|
-
import type { Extensions } from '@tiptap/react'
|
|
20
|
-
import { common, createLowlight } from 'lowlight'
|
|
21
|
-
import 'katex/dist/katex.min.css'
|
|
22
|
-
import { Figure } from './extensions/figure.js'
|
|
23
|
-
import { IndexMarker } from './extensions/indexMarker.js'
|
|
24
|
-
import { Footnote } from './extensions/footnote.js'
|
|
25
|
-
import { MathEditing } from './extensions/mathEditing.js'
|
|
26
|
-
import { MermaidBlock } from './extensions/mermaidBlock.js'
|
|
27
|
-
import { DocumentAssetIdentity } from './extensions/documentAssetIdentity.js'
|
|
28
|
-
import { SmartTypography } from './extensions/smartTypography.js'
|
|
29
|
-
import { CommentMark } from './extensions/commentMark.js'
|
|
30
|
-
import { AppliedChangeMark } from './extensions/appliedChangeMark.js'
|
|
31
|
-
import { AutoReviewPrompts } from './extensions/autoReviewPrompts.js'
|
|
32
|
-
|
|
33
|
-
const lowlight = createLowlight(common)
|
|
34
|
-
|
|
35
|
-
/** Optional markup capabilities — enable per app; no host or filesystem behavior. */
|
|
36
|
-
export interface EditorFeatures {
|
|
37
|
-
tables?: boolean
|
|
38
|
-
taskList?: boolean
|
|
39
|
-
highlight?: boolean
|
|
40
|
-
math?: boolean
|
|
41
|
-
figures?: boolean
|
|
42
|
-
mermaid?: boolean
|
|
43
|
-
footnotes?: boolean
|
|
44
|
-
smartTypography?: boolean
|
|
45
|
-
/** In-document review marks (toolbar must pass `enableComments` on DocumentEditor). */
|
|
46
|
-
comments?: boolean
|
|
47
|
-
/** Prompt batching and validated auto editorial mark application. */
|
|
48
|
-
autoReviewPrompts?: boolean
|
|
49
|
-
/** Book-style index markers (`a.ix`). */
|
|
50
|
-
indexMarkers?: boolean
|
|
51
|
-
/** `data-asset-id` on math/table nodes for external asset indexes. */
|
|
52
|
-
documentAssetIdentity?: boolean
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const DEFAULT_FEATURES: Required<EditorFeatures> = {
|
|
56
|
-
tables: true,
|
|
57
|
-
taskList: true,
|
|
58
|
-
highlight: true,
|
|
59
|
-
math: true,
|
|
60
|
-
figures: true,
|
|
61
|
-
mermaid: true,
|
|
62
|
-
footnotes: true,
|
|
63
|
-
smartTypography: true,
|
|
64
|
-
comments: false,
|
|
65
|
-
autoReviewPrompts: false,
|
|
66
|
-
indexMarkers: false,
|
|
67
|
-
documentAssetIdentity: false,
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export interface BuildExtensionsOptions {
|
|
71
|
-
placeholder?: string
|
|
72
|
-
features?: EditorFeatures
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Build the shared TipTap extension set. Apps may concat further extensions
|
|
77
|
-
* (e.g. custom image paste) or pass the result to DocumentEditor.
|
|
78
|
-
*/
|
|
79
|
-
export function buildExtensions(opts: BuildExtensionsOptions = {}): Extensions {
|
|
80
|
-
const features = { ...DEFAULT_FEATURES, ...opts.features }
|
|
81
|
-
const extensions: Extensions = [
|
|
82
|
-
StarterKit.configure({
|
|
83
|
-
link: { openOnClick: false },
|
|
84
|
-
heading: { levels: [1, 2, 3] },
|
|
85
|
-
codeBlock: false,
|
|
86
|
-
}),
|
|
87
|
-
CodeBlockLowlight.configure({
|
|
88
|
-
lowlight,
|
|
89
|
-
defaultLanguage: 'plaintext',
|
|
90
|
-
}),
|
|
91
|
-
CharacterCount,
|
|
92
|
-
Subscript,
|
|
93
|
-
Superscript,
|
|
94
|
-
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
|
95
|
-
]
|
|
96
|
-
|
|
97
|
-
if (features.documentAssetIdentity) {
|
|
98
|
-
extensions.push(DocumentAssetIdentity)
|
|
99
|
-
}
|
|
100
|
-
if (features.tables) {
|
|
101
|
-
extensions.push(
|
|
102
|
-
Table.configure({ resizable: true }),
|
|
103
|
-
TableCell,
|
|
104
|
-
TableHeader,
|
|
105
|
-
TableRow,
|
|
106
|
-
)
|
|
107
|
-
}
|
|
108
|
-
if (features.taskList) {
|
|
109
|
-
extensions.push(TaskList, TaskItem.configure({ nested: true }))
|
|
110
|
-
}
|
|
111
|
-
if (features.highlight) {
|
|
112
|
-
extensions.push(Highlight)
|
|
113
|
-
}
|
|
114
|
-
if (features.math) {
|
|
115
|
-
extensions.push(
|
|
116
|
-
Mathematics.configure({
|
|
117
|
-
katexOptions: { throwOnError: false },
|
|
118
|
-
}),
|
|
119
|
-
MathEditing,
|
|
120
|
-
)
|
|
121
|
-
}
|
|
122
|
-
if (features.figures) {
|
|
123
|
-
extensions.push(
|
|
124
|
-
Image.configure({ inline: false, allowBase64: false }),
|
|
125
|
-
Figure,
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
if (features.mermaid) {
|
|
129
|
-
extensions.push(MermaidBlock)
|
|
130
|
-
}
|
|
131
|
-
if (features.indexMarkers) {
|
|
132
|
-
extensions.push(IndexMarker)
|
|
133
|
-
}
|
|
134
|
-
if (features.comments) {
|
|
135
|
-
extensions.push(CommentMark)
|
|
136
|
-
extensions.push(AppliedChangeMark)
|
|
137
|
-
}
|
|
138
|
-
if (features.autoReviewPrompts) {
|
|
139
|
-
extensions.push(AutoReviewPrompts)
|
|
140
|
-
}
|
|
141
|
-
if (features.footnotes) {
|
|
142
|
-
extensions.push(Footnote)
|
|
143
|
-
}
|
|
144
|
-
if (features.smartTypography) {
|
|
145
|
-
extensions.push(SmartTypography)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
extensions.push(
|
|
149
|
-
Placeholder.configure({
|
|
150
|
-
placeholder: opts.placeholder ?? 'Start writing…',
|
|
151
|
-
}),
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
return extensions
|
|
155
|
-
}
|
|
1
|
+
import StarterKit from '@tiptap/starter-kit'
|
|
2
|
+
import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'
|
|
3
|
+
import { CharacterCount } from '@tiptap/extension-character-count'
|
|
4
|
+
import Placeholder from '@tiptap/extension-placeholder'
|
|
5
|
+
import { Mathematics } from '@tiptap/extension-mathematics'
|
|
6
|
+
import {
|
|
7
|
+
Table,
|
|
8
|
+
TableCell,
|
|
9
|
+
TableHeader,
|
|
10
|
+
TableRow,
|
|
11
|
+
} from '@tiptap/extension-table'
|
|
12
|
+
import { Subscript } from '@tiptap/extension-subscript'
|
|
13
|
+
import { Superscript } from '@tiptap/extension-superscript'
|
|
14
|
+
import { TaskList } from '@tiptap/extension-task-list'
|
|
15
|
+
import { TaskItem } from '@tiptap/extension-task-item'
|
|
16
|
+
import { TextAlign } from '@tiptap/extension-text-align'
|
|
17
|
+
import { Highlight } from '@tiptap/extension-highlight'
|
|
18
|
+
import { Image } from '@tiptap/extension-image'
|
|
19
|
+
import type { Extensions } from '@tiptap/react'
|
|
20
|
+
import { common, createLowlight } from 'lowlight'
|
|
21
|
+
import 'katex/dist/katex.min.css'
|
|
22
|
+
import { Figure } from './extensions/figure.js'
|
|
23
|
+
import { IndexMarker } from './extensions/indexMarker.js'
|
|
24
|
+
import { Footnote } from './extensions/footnote.js'
|
|
25
|
+
import { MathEditing } from './extensions/mathEditing.js'
|
|
26
|
+
import { MermaidBlock } from './extensions/mermaidBlock.js'
|
|
27
|
+
import { DocumentAssetIdentity } from './extensions/documentAssetIdentity.js'
|
|
28
|
+
import { SmartTypography } from './extensions/smartTypography.js'
|
|
29
|
+
import { CommentMark } from './extensions/commentMark.js'
|
|
30
|
+
import { AppliedChangeMark } from './extensions/appliedChangeMark.js'
|
|
31
|
+
import { AutoReviewPrompts } from './extensions/autoReviewPrompts.js'
|
|
32
|
+
|
|
33
|
+
const lowlight = createLowlight(common)
|
|
34
|
+
|
|
35
|
+
/** Optional markup capabilities — enable per app; no host or filesystem behavior. */
|
|
36
|
+
export interface EditorFeatures {
|
|
37
|
+
tables?: boolean
|
|
38
|
+
taskList?: boolean
|
|
39
|
+
highlight?: boolean
|
|
40
|
+
math?: boolean
|
|
41
|
+
figures?: boolean
|
|
42
|
+
mermaid?: boolean
|
|
43
|
+
footnotes?: boolean
|
|
44
|
+
smartTypography?: boolean
|
|
45
|
+
/** In-document review marks (toolbar must pass `enableComments` on DocumentEditor). */
|
|
46
|
+
comments?: boolean
|
|
47
|
+
/** Prompt batching and validated auto editorial mark application. */
|
|
48
|
+
autoReviewPrompts?: boolean
|
|
49
|
+
/** Book-style index markers (`a.ix`). */
|
|
50
|
+
indexMarkers?: boolean
|
|
51
|
+
/** `data-asset-id` on math/table nodes for external asset indexes. */
|
|
52
|
+
documentAssetIdentity?: boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const DEFAULT_FEATURES: Required<EditorFeatures> = {
|
|
56
|
+
tables: true,
|
|
57
|
+
taskList: true,
|
|
58
|
+
highlight: true,
|
|
59
|
+
math: true,
|
|
60
|
+
figures: true,
|
|
61
|
+
mermaid: true,
|
|
62
|
+
footnotes: true,
|
|
63
|
+
smartTypography: true,
|
|
64
|
+
comments: false,
|
|
65
|
+
autoReviewPrompts: false,
|
|
66
|
+
indexMarkers: false,
|
|
67
|
+
documentAssetIdentity: false,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface BuildExtensionsOptions {
|
|
71
|
+
placeholder?: string
|
|
72
|
+
features?: EditorFeatures
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Build the shared TipTap extension set. Apps may concat further extensions
|
|
77
|
+
* (e.g. custom image paste) or pass the result to DocumentEditor.
|
|
78
|
+
*/
|
|
79
|
+
export function buildExtensions(opts: BuildExtensionsOptions = {}): Extensions {
|
|
80
|
+
const features = { ...DEFAULT_FEATURES, ...opts.features }
|
|
81
|
+
const extensions: Extensions = [
|
|
82
|
+
StarterKit.configure({
|
|
83
|
+
link: { openOnClick: false },
|
|
84
|
+
heading: { levels: [1, 2, 3] },
|
|
85
|
+
codeBlock: false,
|
|
86
|
+
}),
|
|
87
|
+
CodeBlockLowlight.configure({
|
|
88
|
+
lowlight,
|
|
89
|
+
defaultLanguage: 'plaintext',
|
|
90
|
+
}),
|
|
91
|
+
CharacterCount,
|
|
92
|
+
Subscript,
|
|
93
|
+
Superscript,
|
|
94
|
+
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
if (features.documentAssetIdentity) {
|
|
98
|
+
extensions.push(DocumentAssetIdentity)
|
|
99
|
+
}
|
|
100
|
+
if (features.tables) {
|
|
101
|
+
extensions.push(
|
|
102
|
+
Table.configure({ resizable: true }),
|
|
103
|
+
TableCell,
|
|
104
|
+
TableHeader,
|
|
105
|
+
TableRow,
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
if (features.taskList) {
|
|
109
|
+
extensions.push(TaskList, TaskItem.configure({ nested: true }))
|
|
110
|
+
}
|
|
111
|
+
if (features.highlight) {
|
|
112
|
+
extensions.push(Highlight)
|
|
113
|
+
}
|
|
114
|
+
if (features.math) {
|
|
115
|
+
extensions.push(
|
|
116
|
+
Mathematics.configure({
|
|
117
|
+
katexOptions: { throwOnError: false },
|
|
118
|
+
}),
|
|
119
|
+
MathEditing,
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
if (features.figures) {
|
|
123
|
+
extensions.push(
|
|
124
|
+
Image.configure({ inline: false, allowBase64: false }),
|
|
125
|
+
Figure,
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
if (features.mermaid) {
|
|
129
|
+
extensions.push(MermaidBlock)
|
|
130
|
+
}
|
|
131
|
+
if (features.indexMarkers) {
|
|
132
|
+
extensions.push(IndexMarker)
|
|
133
|
+
}
|
|
134
|
+
if (features.comments) {
|
|
135
|
+
extensions.push(CommentMark)
|
|
136
|
+
extensions.push(AppliedChangeMark)
|
|
137
|
+
}
|
|
138
|
+
if (features.autoReviewPrompts) {
|
|
139
|
+
extensions.push(AutoReviewPrompts)
|
|
140
|
+
}
|
|
141
|
+
if (features.footnotes) {
|
|
142
|
+
extensions.push(Footnote)
|
|
143
|
+
}
|
|
144
|
+
if (features.smartTypography) {
|
|
145
|
+
extensions.push(SmartTypography)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
extensions.push(
|
|
149
|
+
Placeholder.configure({
|
|
150
|
+
placeholder: opts.placeholder ?? 'Start writing…',
|
|
151
|
+
}),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
return extensions
|
|
155
|
+
}
|
|
@@ -1,115 +1,115 @@
|
|
|
1
|
-
import { Mark, mergeAttributes } from '@tiptap/core'
|
|
2
|
-
|
|
3
|
-
declare module '@tiptap/core' {
|
|
4
|
-
interface Commands<ReturnType> {
|
|
5
|
-
appliedChangeMark: {
|
|
6
|
-
setAppliedChangeMark: (attrs: {
|
|
7
|
-
originalText: string
|
|
8
|
-
replacementText: string
|
|
9
|
-
sourceCommentId?: string
|
|
10
|
-
sourceType?: string
|
|
11
|
-
actor?: string
|
|
12
|
-
createdAt?: string
|
|
13
|
-
}) => ReturnType
|
|
14
|
-
unsetAppliedChangeMark: () => ReturnType
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const AppliedChangeMark = Mark.create({
|
|
20
|
-
name: 'appliedChangeMark',
|
|
21
|
-
inclusive: false,
|
|
22
|
-
|
|
23
|
-
addAttributes() {
|
|
24
|
-
return {
|
|
25
|
-
originalText: {
|
|
26
|
-
default: null,
|
|
27
|
-
parseHTML: el => el.getAttribute('data-pm-applied-original'),
|
|
28
|
-
renderHTML: (attrs: { originalText?: string }) =>
|
|
29
|
-
attrs.originalText
|
|
30
|
-
? { 'data-pm-applied-original': attrs.originalText }
|
|
31
|
-
: {},
|
|
32
|
-
},
|
|
33
|
-
replacementText: {
|
|
34
|
-
default: null,
|
|
35
|
-
parseHTML: el => el.getAttribute('data-pm-applied-replacement'),
|
|
36
|
-
renderHTML: (attrs: { replacementText?: string }) =>
|
|
37
|
-
attrs.replacementText
|
|
38
|
-
? { 'data-pm-applied-replacement': attrs.replacementText }
|
|
39
|
-
: {},
|
|
40
|
-
},
|
|
41
|
-
sourceCommentId: {
|
|
42
|
-
default: null,
|
|
43
|
-
parseHTML: el => el.getAttribute('data-pm-applied-source-comment-id'),
|
|
44
|
-
renderHTML: (attrs: { sourceCommentId?: string }) =>
|
|
45
|
-
attrs.sourceCommentId
|
|
46
|
-
? { 'data-pm-applied-source-comment-id': attrs.sourceCommentId }
|
|
47
|
-
: {},
|
|
48
|
-
},
|
|
49
|
-
sourceType: {
|
|
50
|
-
default: null,
|
|
51
|
-
parseHTML: el => el.getAttribute('data-pm-applied-source-type'),
|
|
52
|
-
renderHTML: (attrs: { sourceType?: string }) =>
|
|
53
|
-
attrs.sourceType ? { 'data-pm-applied-source-type': attrs.sourceType } : {},
|
|
54
|
-
},
|
|
55
|
-
actor: {
|
|
56
|
-
default: null,
|
|
57
|
-
parseHTML: el => el.getAttribute('data-pm-applied-actor'),
|
|
58
|
-
renderHTML: (attrs: { actor?: string }) =>
|
|
59
|
-
attrs.actor ? { 'data-pm-applied-actor': attrs.actor } : {},
|
|
60
|
-
},
|
|
61
|
-
createdAt: {
|
|
62
|
-
default: null,
|
|
63
|
-
parseHTML: el => el.getAttribute('data-pm-applied-created-at'),
|
|
64
|
-
renderHTML: (attrs: { createdAt?: string }) =>
|
|
65
|
-
attrs.createdAt ? { 'data-pm-applied-created-at': attrs.createdAt } : {},
|
|
66
|
-
},
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
parseHTML() {
|
|
71
|
-
return [{ tag: 'span[data-pm-applied-change="true"]' }]
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
renderHTML({ HTMLAttributes, mark }) {
|
|
75
|
-
const attrs = mark.attrs as {
|
|
76
|
-
originalText?: string
|
|
77
|
-
replacementText?: string
|
|
78
|
-
actor?: string
|
|
79
|
-
createdAt?: string
|
|
80
|
-
}
|
|
81
|
-
const title = [
|
|
82
|
-
attrs.actor ? `${attrs.actor} applied edit` : 'Applied edit',
|
|
83
|
-
attrs.originalText ? `Original: ${attrs.originalText}` : null,
|
|
84
|
-
attrs.replacementText ? `Replacement: ${attrs.replacementText}` : null,
|
|
85
|
-
attrs.createdAt ?? null,
|
|
86
|
-
]
|
|
87
|
-
.filter(Boolean)
|
|
88
|
-
.join('\n')
|
|
89
|
-
return [
|
|
90
|
-
'span',
|
|
91
|
-
mergeAttributes(HTMLAttributes, {
|
|
92
|
-
'data-pm-applied-change': 'true',
|
|
93
|
-
class: 'applied-change-mark',
|
|
94
|
-
title,
|
|
95
|
-
}),
|
|
96
|
-
0,
|
|
97
|
-
]
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
addCommands() {
|
|
101
|
-
return {
|
|
102
|
-
setAppliedChangeMark:
|
|
103
|
-
attrs =>
|
|
104
|
-
({ commands, state }) => {
|
|
105
|
-
const { from, to } = state.selection
|
|
106
|
-
if (from === to) return false
|
|
107
|
-
return commands.setMark(this.name, attrs)
|
|
108
|
-
},
|
|
109
|
-
unsetAppliedChangeMark:
|
|
110
|
-
() =>
|
|
111
|
-
({ commands }) =>
|
|
112
|
-
commands.unsetMark(this.name),
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
})
|
|
1
|
+
import { Mark, mergeAttributes } from '@tiptap/core'
|
|
2
|
+
|
|
3
|
+
declare module '@tiptap/core' {
|
|
4
|
+
interface Commands<ReturnType> {
|
|
5
|
+
appliedChangeMark: {
|
|
6
|
+
setAppliedChangeMark: (attrs: {
|
|
7
|
+
originalText: string
|
|
8
|
+
replacementText: string
|
|
9
|
+
sourceCommentId?: string
|
|
10
|
+
sourceType?: string
|
|
11
|
+
actor?: string
|
|
12
|
+
createdAt?: string
|
|
13
|
+
}) => ReturnType
|
|
14
|
+
unsetAppliedChangeMark: () => ReturnType
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const AppliedChangeMark = Mark.create({
|
|
20
|
+
name: 'appliedChangeMark',
|
|
21
|
+
inclusive: false,
|
|
22
|
+
|
|
23
|
+
addAttributes() {
|
|
24
|
+
return {
|
|
25
|
+
originalText: {
|
|
26
|
+
default: null,
|
|
27
|
+
parseHTML: el => el.getAttribute('data-pm-applied-original'),
|
|
28
|
+
renderHTML: (attrs: { originalText?: string }) =>
|
|
29
|
+
attrs.originalText
|
|
30
|
+
? { 'data-pm-applied-original': attrs.originalText }
|
|
31
|
+
: {},
|
|
32
|
+
},
|
|
33
|
+
replacementText: {
|
|
34
|
+
default: null,
|
|
35
|
+
parseHTML: el => el.getAttribute('data-pm-applied-replacement'),
|
|
36
|
+
renderHTML: (attrs: { replacementText?: string }) =>
|
|
37
|
+
attrs.replacementText
|
|
38
|
+
? { 'data-pm-applied-replacement': attrs.replacementText }
|
|
39
|
+
: {},
|
|
40
|
+
},
|
|
41
|
+
sourceCommentId: {
|
|
42
|
+
default: null,
|
|
43
|
+
parseHTML: el => el.getAttribute('data-pm-applied-source-comment-id'),
|
|
44
|
+
renderHTML: (attrs: { sourceCommentId?: string }) =>
|
|
45
|
+
attrs.sourceCommentId
|
|
46
|
+
? { 'data-pm-applied-source-comment-id': attrs.sourceCommentId }
|
|
47
|
+
: {},
|
|
48
|
+
},
|
|
49
|
+
sourceType: {
|
|
50
|
+
default: null,
|
|
51
|
+
parseHTML: el => el.getAttribute('data-pm-applied-source-type'),
|
|
52
|
+
renderHTML: (attrs: { sourceType?: string }) =>
|
|
53
|
+
attrs.sourceType ? { 'data-pm-applied-source-type': attrs.sourceType } : {},
|
|
54
|
+
},
|
|
55
|
+
actor: {
|
|
56
|
+
default: null,
|
|
57
|
+
parseHTML: el => el.getAttribute('data-pm-applied-actor'),
|
|
58
|
+
renderHTML: (attrs: { actor?: string }) =>
|
|
59
|
+
attrs.actor ? { 'data-pm-applied-actor': attrs.actor } : {},
|
|
60
|
+
},
|
|
61
|
+
createdAt: {
|
|
62
|
+
default: null,
|
|
63
|
+
parseHTML: el => el.getAttribute('data-pm-applied-created-at'),
|
|
64
|
+
renderHTML: (attrs: { createdAt?: string }) =>
|
|
65
|
+
attrs.createdAt ? { 'data-pm-applied-created-at': attrs.createdAt } : {},
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
parseHTML() {
|
|
71
|
+
return [{ tag: 'span[data-pm-applied-change="true"]' }]
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
renderHTML({ HTMLAttributes, mark }) {
|
|
75
|
+
const attrs = mark.attrs as {
|
|
76
|
+
originalText?: string
|
|
77
|
+
replacementText?: string
|
|
78
|
+
actor?: string
|
|
79
|
+
createdAt?: string
|
|
80
|
+
}
|
|
81
|
+
const title = [
|
|
82
|
+
attrs.actor ? `${attrs.actor} applied edit` : 'Applied edit',
|
|
83
|
+
attrs.originalText ? `Original: ${attrs.originalText}` : null,
|
|
84
|
+
attrs.replacementText ? `Replacement: ${attrs.replacementText}` : null,
|
|
85
|
+
attrs.createdAt ?? null,
|
|
86
|
+
]
|
|
87
|
+
.filter(Boolean)
|
|
88
|
+
.join('\n')
|
|
89
|
+
return [
|
|
90
|
+
'span',
|
|
91
|
+
mergeAttributes(HTMLAttributes, {
|
|
92
|
+
'data-pm-applied-change': 'true',
|
|
93
|
+
class: 'applied-change-mark',
|
|
94
|
+
title,
|
|
95
|
+
}),
|
|
96
|
+
0,
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
addCommands() {
|
|
101
|
+
return {
|
|
102
|
+
setAppliedChangeMark:
|
|
103
|
+
attrs =>
|
|
104
|
+
({ commands, state }) => {
|
|
105
|
+
const { from, to } = state.selection
|
|
106
|
+
if (from === to) return false
|
|
107
|
+
return commands.setMark(this.name, attrs)
|
|
108
|
+
},
|
|
109
|
+
unsetAppliedChangeMark:
|
|
110
|
+
() =>
|
|
111
|
+
({ commands }) =>
|
|
112
|
+
commands.unsetMark(this.name),
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
})
|