@puredesktop/platform-editor 1.0.0-beta.1

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 (45) hide show
  1. package/package.json +63 -0
  2. package/src/CollapsePanel.tsx +35 -0
  3. package/src/DocumentDiffView.tsx +130 -0
  4. package/src/DocumentEditor.test.ts +118 -0
  5. package/src/DocumentEditor.tsx +2631 -0
  6. package/src/alignedLineDiff.test.ts +52 -0
  7. package/src/alignedLineDiff.ts +67 -0
  8. package/src/collectionAssetSrc.ts +2 -0
  9. package/src/commentUtils.test.ts +350 -0
  10. package/src/commentUtils.ts +546 -0
  11. package/src/constants/toolKeys.ts +14 -0
  12. package/src/contentFormat.test.ts +27 -0
  13. package/src/contentFormat.ts +18 -0
  14. package/src/editorExtensions.ts +155 -0
  15. package/src/extensions/appliedChangeMark.ts +115 -0
  16. package/src/extensions/autoReviewPrompts.test.ts +529 -0
  17. package/src/extensions/autoReviewPrompts.ts +1442 -0
  18. package/src/extensions/collectionImage.ts +26 -0
  19. package/src/extensions/collectionImagePaste.ts +215 -0
  20. package/src/extensions/commentMark.ts +223 -0
  21. package/src/extensions/documentAssetIdentity.ts +54 -0
  22. package/src/extensions/figure.ts +92 -0
  23. package/src/extensions/footnote.ts +186 -0
  24. package/src/extensions/indexMarker.ts +88 -0
  25. package/src/extensions/mathEditing.ts +239 -0
  26. package/src/extensions/mermaidBlock.tsx +297 -0
  27. package/src/extensions/slashCommands.test.ts +170 -0
  28. package/src/extensions/slashCommands.ts +746 -0
  29. package/src/extensions/smartTypography.test.ts +74 -0
  30. package/src/extensions/smartTypography.ts +120 -0
  31. package/src/index.ts +137 -0
  32. package/src/insertCollectionImage.test.ts +60 -0
  33. package/src/insertCollectionImage.ts +63 -0
  34. package/src/insertInlineAssetKind.test.ts +67 -0
  35. package/src/insertInlineAssetKind.ts +49 -0
  36. package/src/mermaidPreview.test.ts +54 -0
  37. package/src/mermaidPreview.ts +115 -0
  38. package/src/styled.ts +676 -0
  39. package/src/toolbar/ToolBtn.tsx +63 -0
  40. package/src/toolbar/Toolbar.test.tsx +325 -0
  41. package/src/toolbar/Toolbar.tsx +624 -0
  42. package/src/toolbar/groups/HeadingGroup.tsx +153 -0
  43. package/src/toolbar/overlayTypes.ts +28 -0
  44. package/src/useEditorExtensions.ts +22 -0
  45. package/src/utils/markdownUtils.ts +331 -0
@@ -0,0 +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
+ }
@@ -0,0 +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
+ })