@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.
Files changed (45) hide show
  1. package/package.json +1 -1
  2. package/src/CollapsePanel.tsx +35 -35
  3. package/src/DocumentDiffView.tsx +130 -130
  4. package/src/DocumentEditor.test.ts +118 -118
  5. package/src/DocumentEditor.tsx +2651 -2631
  6. package/src/alignedLineDiff.test.ts +52 -52
  7. package/src/alignedLineDiff.ts +67 -67
  8. package/src/collectionAssetSrc.ts +2 -2
  9. package/src/commentUtils.test.ts +350 -350
  10. package/src/commentUtils.ts +546 -546
  11. package/src/constants/toolKeys.ts +14 -14
  12. package/src/contentFormat.test.ts +27 -27
  13. package/src/contentFormat.ts +18 -18
  14. package/src/editorExtensions.ts +155 -155
  15. package/src/extensions/appliedChangeMark.ts +115 -115
  16. package/src/extensions/autoReviewPrompts.test.ts +529 -529
  17. package/src/extensions/autoReviewPrompts.ts +1442 -1442
  18. package/src/extensions/collectionImage.ts +26 -26
  19. package/src/extensions/collectionImagePaste.ts +215 -215
  20. package/src/extensions/commentMark.ts +223 -223
  21. package/src/extensions/documentAssetIdentity.ts +54 -54
  22. package/src/extensions/figure.ts +92 -92
  23. package/src/extensions/footnote.ts +186 -186
  24. package/src/extensions/indexMarker.ts +88 -88
  25. package/src/extensions/mathEditing.ts +239 -239
  26. package/src/extensions/mermaidBlock.tsx +297 -297
  27. package/src/extensions/slashCommands.test.ts +170 -170
  28. package/src/extensions/slashCommands.ts +746 -746
  29. package/src/extensions/smartTypography.test.ts +74 -74
  30. package/src/extensions/smartTypography.ts +120 -120
  31. package/src/index.ts +138 -137
  32. package/src/insertCollectionImage.test.ts +60 -60
  33. package/src/insertCollectionImage.ts +63 -63
  34. package/src/insertInlineAssetKind.test.ts +67 -67
  35. package/src/insertInlineAssetKind.ts +49 -49
  36. package/src/mermaidPreview.test.ts +54 -54
  37. package/src/mermaidPreview.ts +115 -115
  38. package/src/styled.ts +697 -676
  39. package/src/toolbar/ToolBtn.tsx +77 -63
  40. package/src/toolbar/Toolbar.test.tsx +325 -325
  41. package/src/toolbar/Toolbar.tsx +662 -624
  42. package/src/toolbar/groups/HeadingGroup.tsx +175 -153
  43. package/src/toolbar/overlayTypes.ts +28 -28
  44. package/src/useEditorExtensions.ts +22 -22
  45. package/src/utils/markdownUtils.ts +331 -331
@@ -1,115 +1,115 @@
1
- import type mermaidType from 'mermaid'
2
-
3
- export type MermaidPreviewTheme = 'default' | 'dark'
4
-
5
- export interface RenderMermaidPreviewSvgOptions {
6
- theme?: MermaidPreviewTheme
7
- }
8
-
9
- export interface ExpandMermaidBlocksOptions {
10
- theme?: MermaidPreviewTheme
11
- wrapperClass?: string
12
- }
13
-
14
- let mermaidPromise: Promise<typeof mermaidType> | null = null
15
- let mermaidInitializedTheme: MermaidPreviewTheme | null = null
16
-
17
- async function loadMermaid(
18
- theme: MermaidPreviewTheme,
19
- ): Promise<typeof mermaidType> {
20
- mermaidPromise ??= import('mermaid').then(module => module.default)
21
- const mermaid = await mermaidPromise
22
- if (mermaidInitializedTheme !== theme) {
23
- mermaid.initialize({
24
- startOnLoad: false,
25
- securityLevel: 'strict',
26
- theme,
27
- suppressErrorRendering: true,
28
- })
29
- mermaidInitializedTheme = theme
30
- }
31
- return mermaid
32
- }
33
-
34
- function platformThemeIsDark(): boolean {
35
- if (typeof document === 'undefined') return false
36
- return document.documentElement.dataset.platformTheme === 'dark'
37
- }
38
-
39
- function decodeHtmlEntities(text: string): string {
40
- return text
41
- .replace(/&lt;/g, '<')
42
- .replace(/&gt;/g, '>')
43
- .replace(/&amp;/g, '&')
44
- .replace(/&quot;/g, '"')
45
- .replace(/&#39;/g, "'")
46
- }
47
-
48
- const MERMAID_CODE_PATTERN =
49
- /<code\b[^>]*class=["'][^"']*language-mermaid[^"']*["'][^>]*>([\s\S]*?)<\/code>/i
50
-
51
- const MERMAID_BLOCK_PATTERN =
52
- /<(?:figure|div)\b[^>]*data-type=["']mermaid["'][^>]*>[\s\S]*?<\/(?:figure|div)>/gi
53
-
54
- /** Extract mermaid source from serialized editor/shell HTML (regex — safe for `-->`). */
55
- export function extractMermaidSourceFromHtml(html: string): string {
56
- const match = html.match(MERMAID_CODE_PATTERN)
57
- return decodeHtmlEntities(match?.[1] ?? '').trim()
58
- }
59
-
60
- export async function renderMermaidPreviewSvg(
61
- sourceCode: string,
62
- options: RenderMermaidPreviewSvgOptions = {},
63
- ): Promise<string> {
64
- const theme = options.theme ?? (platformThemeIsDark() ? 'dark' : 'default')
65
- const mermaid = await loadMermaid(theme)
66
- const diagram = sourceCode.trim() || 'flowchart TD\n A[Empty diagram]'
67
- const result = await mermaid.render(
68
- `platform-mermaid-${Date.now()}-${Math.random().toString(36).slice(2)}`,
69
- diagram,
70
- )
71
- return result.svg
72
- }
73
-
74
- /** Replace stored mermaid blocks with rendered SVG (print/PDF/static HTML). */
75
- export async function expandMermaidBlocksForPrint(
76
- html: string,
77
- options: ExpandMermaidBlocksOptions = {},
78
- ): Promise<string> {
79
- const theme = options.theme ?? 'default'
80
- const wrapperClass = options.wrapperClass ?? 'platform-print-mermaid'
81
- const matches = [...html.matchAll(MERMAID_BLOCK_PATTERN)]
82
- if (matches.length === 0) return html
83
-
84
- const replacements: Array<{ start: number; end: number; text: string }> = []
85
-
86
- for (const match of matches) {
87
- const block = match[0]
88
- const start = match.index ?? html.indexOf(block)
89
- if (start < 0) continue
90
- const source = extractMermaidSourceFromHtml(block)
91
- if (!source) continue
92
- try {
93
- const svg = await renderMermaidPreviewSvg(source, { theme })
94
- replacements.push({
95
- start,
96
- end: start + block.length,
97
- text: `<figure data-type="mermaid"><div class="${wrapperClass}">${svg}</div></figure>`,
98
- })
99
- } catch {
100
- // Keep source markup when render fails.
101
- }
102
- }
103
-
104
- if (replacements.length === 0) return html
105
-
106
- replacements.sort((a, b) => b.start - a.start)
107
- let result = html
108
- for (const replacement of replacements) {
109
- result =
110
- result.slice(0, replacement.start) +
111
- replacement.text +
112
- result.slice(replacement.end)
113
- }
114
- return result
115
- }
1
+ import type mermaidType from 'mermaid'
2
+
3
+ export type MermaidPreviewTheme = 'default' | 'dark'
4
+
5
+ export interface RenderMermaidPreviewSvgOptions {
6
+ theme?: MermaidPreviewTheme
7
+ }
8
+
9
+ export interface ExpandMermaidBlocksOptions {
10
+ theme?: MermaidPreviewTheme
11
+ wrapperClass?: string
12
+ }
13
+
14
+ let mermaidPromise: Promise<typeof mermaidType> | null = null
15
+ let mermaidInitializedTheme: MermaidPreviewTheme | null = null
16
+
17
+ async function loadMermaid(
18
+ theme: MermaidPreviewTheme,
19
+ ): Promise<typeof mermaidType> {
20
+ mermaidPromise ??= import('mermaid').then(module => module.default)
21
+ const mermaid = await mermaidPromise
22
+ if (mermaidInitializedTheme !== theme) {
23
+ mermaid.initialize({
24
+ startOnLoad: false,
25
+ securityLevel: 'strict',
26
+ theme,
27
+ suppressErrorRendering: true,
28
+ })
29
+ mermaidInitializedTheme = theme
30
+ }
31
+ return mermaid
32
+ }
33
+
34
+ function platformThemeIsDark(): boolean {
35
+ if (typeof document === 'undefined') return false
36
+ return document.documentElement.dataset.platformTheme === 'dark'
37
+ }
38
+
39
+ function decodeHtmlEntities(text: string): string {
40
+ return text
41
+ .replace(/&lt;/g, '<')
42
+ .replace(/&gt;/g, '>')
43
+ .replace(/&amp;/g, '&')
44
+ .replace(/&quot;/g, '"')
45
+ .replace(/&#39;/g, "'")
46
+ }
47
+
48
+ const MERMAID_CODE_PATTERN =
49
+ /<code\b[^>]*class=["'][^"']*language-mermaid[^"']*["'][^>]*>([\s\S]*?)<\/code>/i
50
+
51
+ const MERMAID_BLOCK_PATTERN =
52
+ /<(?:figure|div)\b[^>]*data-type=["']mermaid["'][^>]*>[\s\S]*?<\/(?:figure|div)>/gi
53
+
54
+ /** Extract mermaid source from serialized editor/shell HTML (regex — safe for `-->`). */
55
+ export function extractMermaidSourceFromHtml(html: string): string {
56
+ const match = html.match(MERMAID_CODE_PATTERN)
57
+ return decodeHtmlEntities(match?.[1] ?? '').trim()
58
+ }
59
+
60
+ export async function renderMermaidPreviewSvg(
61
+ sourceCode: string,
62
+ options: RenderMermaidPreviewSvgOptions = {},
63
+ ): Promise<string> {
64
+ const theme = options.theme ?? (platformThemeIsDark() ? 'dark' : 'default')
65
+ const mermaid = await loadMermaid(theme)
66
+ const diagram = sourceCode.trim() || 'flowchart TD\n A[Empty diagram]'
67
+ const result = await mermaid.render(
68
+ `platform-mermaid-${Date.now()}-${Math.random().toString(36).slice(2)}`,
69
+ diagram,
70
+ )
71
+ return result.svg
72
+ }
73
+
74
+ /** Replace stored mermaid blocks with rendered SVG (print/PDF/static HTML). */
75
+ export async function expandMermaidBlocksForPrint(
76
+ html: string,
77
+ options: ExpandMermaidBlocksOptions = {},
78
+ ): Promise<string> {
79
+ const theme = options.theme ?? 'default'
80
+ const wrapperClass = options.wrapperClass ?? 'platform-print-mermaid'
81
+ const matches = [...html.matchAll(MERMAID_BLOCK_PATTERN)]
82
+ if (matches.length === 0) return html
83
+
84
+ const replacements: Array<{ start: number; end: number; text: string }> = []
85
+
86
+ for (const match of matches) {
87
+ const block = match[0]
88
+ const start = match.index ?? html.indexOf(block)
89
+ if (start < 0) continue
90
+ const source = extractMermaidSourceFromHtml(block)
91
+ if (!source) continue
92
+ try {
93
+ const svg = await renderMermaidPreviewSvg(source, { theme })
94
+ replacements.push({
95
+ start,
96
+ end: start + block.length,
97
+ text: `<figure data-type="mermaid"><div class="${wrapperClass}">${svg}</div></figure>`,
98
+ })
99
+ } catch {
100
+ // Keep source markup when render fails.
101
+ }
102
+ }
103
+
104
+ if (replacements.length === 0) return html
105
+
106
+ replacements.sort((a, b) => b.start - a.start)
107
+ let result = html
108
+ for (const replacement of replacements) {
109
+ result =
110
+ result.slice(0, replacement.start) +
111
+ replacement.text +
112
+ result.slice(replacement.end)
113
+ }
114
+ return result
115
+ }