@sveltia/ui 0.31.10 → 0.31.12
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/LICENSE.txt +1 -1
- package/dist/components/text-editor/core.js +12 -16
- package/dist/components/text-editor/markdown.d.ts +3 -0
- package/dist/components/text-editor/markdown.js +45 -0
- package/dist/components/text-editor/markdown.test.d.ts +1 -0
- package/dist/components/text-editor/markdown.test.js +178 -0
- package/package.json +17 -17
package/LICENSE.txt
CHANGED
|
@@ -53,6 +53,11 @@ import {
|
|
|
53
53
|
} from 'lexical';
|
|
54
54
|
import prismComponents from 'prismjs/components';
|
|
55
55
|
import { BLOCK_BUTTON_TYPES, TEXT_FORMAT_BUTTON_TYPES } from './constants.js';
|
|
56
|
+
import {
|
|
57
|
+
fixMarkdownFormatting,
|
|
58
|
+
increaseListIndentation,
|
|
59
|
+
splitMultilineFormatting,
|
|
60
|
+
} from './markdown.js';
|
|
56
61
|
import { TABLE } from './transformers/table.js';
|
|
57
62
|
|
|
58
63
|
/**
|
|
@@ -410,22 +415,13 @@ export const convertMarkdownToLexical = async (editor, value) => {
|
|
|
410
415
|
);
|
|
411
416
|
|
|
412
417
|
// Split multiline formatting into separate lines to prevent Markdown parsing issues
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
// Increase list indentation levels to prevent Markdown parsing issues: Slate uses 2 spaces for
|
|
421
|
-
// each indentation level, whereas Lexical uses 4 spaces
|
|
422
|
-
// @see https://github.com/sveltia/sveltia-cms/issues/549
|
|
423
|
-
if (value.match(/^\s{2}(?:-|\+|\*|\d+\.)\s/m)) {
|
|
424
|
-
value = value.replace(
|
|
425
|
-
/^(\s+)(-|\+|\*|\d+\.)/gm,
|
|
426
|
-
(_match, p1, p2) => `${' '.repeat(p1.length * 2)}${p2}`,
|
|
427
|
-
);
|
|
428
|
-
}
|
|
418
|
+
value = splitMultilineFormatting(value);
|
|
419
|
+
|
|
420
|
+
// Fix unclosed formatting markers (work around Lexical bug)
|
|
421
|
+
value = fixMarkdownFormatting(value);
|
|
422
|
+
|
|
423
|
+
// Increase list indentation levels to prevent Markdown parsing issues
|
|
424
|
+
value = increaseListIndentation(value);
|
|
429
425
|
|
|
430
426
|
return new Promise((resolve, reject) => {
|
|
431
427
|
editor.update(() => {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split multiline formatting into separate lines to prevent Markdown parsing issues.
|
|
3
|
+
* @param {string} value Markdown string to process.
|
|
4
|
+
* @returns {string} Processed Markdown string.
|
|
5
|
+
* @see https://github.com/sveltia/sveltia-cms/issues/548
|
|
6
|
+
*/
|
|
7
|
+
export const splitMultilineFormatting = (value) =>
|
|
8
|
+
value
|
|
9
|
+
.replace(/(\s+)_([^_\n]+?)\n([^_\n]+?)_(\s+)/gm, '$1_$2_\n_$3_$4')
|
|
10
|
+
.replace(/(\s+)\*\*([^*\n]+?)\n([^*\n]+?)\*\*(\s+)/gm, '$1**$2**\n**$3**$4')
|
|
11
|
+
.replace(/(\s+)~~([^~\n]+?)\n([^~\n]+?)~~(\s+)/gm, '$1~~$2~~\n~~$3~~$4')
|
|
12
|
+
.replace(/(\s+)`([^`\n]+?)\n([^`\n]+?)`(\s+)/gm, '$1`$2`\n`$3`$4');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fix malformed Markdown formatting markers.
|
|
16
|
+
* Converts unclosed formatting markers like `**foo **bar` to `**foo** bar`.
|
|
17
|
+
* This works around a Lexical bug with certain formatting patterns.
|
|
18
|
+
* @param {string} value Markdown string to fix.
|
|
19
|
+
* @returns {string} Fixed Markdown string.
|
|
20
|
+
* @see https://github.com/sveltia/sveltia-cms/issues/599
|
|
21
|
+
*/
|
|
22
|
+
export const fixMarkdownFormatting = (value) =>
|
|
23
|
+
value
|
|
24
|
+
.replace(/\*\*(\S+?) \*\*/gm, '**$1** ')
|
|
25
|
+
.replace(/_(\S+?) _/gm, '_$1_ ')
|
|
26
|
+
.replace(/~~(\S+?) ~~/gm, '~~$1~~ ');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Increase list indentation levels to prevent Markdown parsing issues.
|
|
30
|
+
* Slate uses 2 spaces per indentation level, whereas Lexical uses 4 spaces.
|
|
31
|
+
* This function doubles the indentation to match Lexical's expectations.
|
|
32
|
+
* @param {string} value Markdown string to process.
|
|
33
|
+
* @returns {string} Processed Markdown string.
|
|
34
|
+
* @see https://github.com/sveltia/sveltia-cms/issues/549
|
|
35
|
+
*/
|
|
36
|
+
export const increaseListIndentation = (value) => {
|
|
37
|
+
if (!value.match(/^\s{2}(?:-|\+|\*|\d+\.)\s/m)) {
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return value.replace(
|
|
42
|
+
/^(\s+)(-|\+|\*|\d+\.)/gm,
|
|
43
|
+
(_match, p1, p2) => `${' '.repeat(p1.length * 2)}${p2}`,
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
fixMarkdownFormatting,
|
|
4
|
+
increaseListIndentation,
|
|
5
|
+
splitMultilineFormatting,
|
|
6
|
+
} from './markdown.js';
|
|
7
|
+
|
|
8
|
+
describe('splitMultilineFormatting', () => {
|
|
9
|
+
it('should split italic formatting across lines', () => {
|
|
10
|
+
expect(splitMultilineFormatting(' _foo\nbar_ ')).toBe(' _foo_\n_bar_ ');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should split bold formatting across lines', () => {
|
|
14
|
+
expect(splitMultilineFormatting(' **foo\nbar** ')).toBe(' **foo**\n**bar** ');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should split strikethrough formatting across lines', () => {
|
|
18
|
+
expect(splitMultilineFormatting(' ~~foo\nbar~~ ')).toBe(' ~~foo~~\n~~bar~~ ');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should split code formatting across lines', () => {
|
|
22
|
+
expect(splitMultilineFormatting(' `foo\nbar` ')).toBe(' `foo`\n`bar` ');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should handle multiple formatting types', () => {
|
|
26
|
+
expect(
|
|
27
|
+
splitMultilineFormatting(' _italic\ntext_ **bold\ntext** ~~strike\nthrough~~ '),
|
|
28
|
+
).toBe(' _italic_\n_text_ **bold**\n**text** ~~strike~~\n~~through~~ ');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should not affect properly formatted single-line text', () => {
|
|
32
|
+
expect(splitMultilineFormatting('_italic_ **bold** ~~strike~~ `code`')).toBe(
|
|
33
|
+
'_italic_ **bold** ~~strike~~ `code`',
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should only split when surrounded by whitespace', () => {
|
|
38
|
+
expect(splitMultilineFormatting('_foo\nbar_')).toBe('_foo\nbar_');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should handle formatting at different indentation levels', () => {
|
|
42
|
+
expect(splitMultilineFormatting(' _foo\nbar_ ')).toBe(' _foo_\n_bar_ ');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('fixMarkdownFormatting', () => {
|
|
47
|
+
it('should fix unclosed bold markers with space in between', () => {
|
|
48
|
+
expect(fixMarkdownFormatting('**foo **bar')).toBe('**foo** bar');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should fix unclosed italic markers with space in between', () => {
|
|
52
|
+
expect(fixMarkdownFormatting('_foo _bar')).toBe('_foo_ bar');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should fix multiple bold markers', () => {
|
|
56
|
+
expect(fixMarkdownFormatting('**foo **bar **baz **qux')).toBe('**foo** bar **baz** qux');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should fix multiple italic markers', () => {
|
|
60
|
+
expect(fixMarkdownFormatting('_foo _bar _baz _qux')).toBe('_foo_ bar _baz_ qux');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should fix mixed bold and italic markers', () => {
|
|
64
|
+
expect(fixMarkdownFormatting('**foo **bar _baz _qux')).toBe('**foo** bar _baz_ qux');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should not affect properly closed bold markers', () => {
|
|
68
|
+
expect(fixMarkdownFormatting('**foo** bar')).toBe('**foo** bar');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should not affect properly closed italic markers', () => {
|
|
72
|
+
expect(fixMarkdownFormatting('_foo_ bar')).toBe('_foo_ bar');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should preserve other formatting', () => {
|
|
76
|
+
expect(fixMarkdownFormatting('~~strikethrough~~ **bold** _italic_')).toBe(
|
|
77
|
+
'~~strikethrough~~ **bold** _italic_',
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should handle bold and italic in combination', () => {
|
|
82
|
+
expect(fixMarkdownFormatting('**foo **bar _baz _qux **test **end')).toBe(
|
|
83
|
+
'**foo** bar _baz_ qux **test** end',
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should handle markers at the start of a line', () => {
|
|
88
|
+
expect(fixMarkdownFormatting('**foo **bar\n_baz _qux')).toBe('**foo** bar\n_baz_ qux');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should handle markers at the end of a line', () => {
|
|
92
|
+
expect(fixMarkdownFormatting('line 1 **foo **bar\nline 2 _baz _qux')).toBe(
|
|
93
|
+
'line 1 **foo** bar\nline 2 _baz_ qux',
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should work with global flag for multiple occurrences', () => {
|
|
98
|
+
expect(fixMarkdownFormatting('**a **b **c **d _e _f')).toBe('**a** b **c** d _e_ f');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should not break when markers are not present', () => {
|
|
102
|
+
expect(fixMarkdownFormatting('regular text without formatting')).toBe(
|
|
103
|
+
'regular text without formatting',
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should handle edge case with only formatting markers', () => {
|
|
108
|
+
expect(fixMarkdownFormatting('**foo **')).toBe('**foo** ');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should handle underscores in words (not formatting)', () => {
|
|
112
|
+
expect(fixMarkdownFormatting('snake_case_variable **foo **bar')).toBe(
|
|
113
|
+
'snake_case_variable **foo** bar',
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should fix unclosed strikethrough markers with space in between', () => {
|
|
118
|
+
expect(fixMarkdownFormatting('~~foo ~~bar')).toBe('~~foo~~ bar');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should fix multiple strikethrough markers', () => {
|
|
122
|
+
expect(fixMarkdownFormatting('~~foo ~~bar ~~baz ~~qux')).toBe('~~foo~~ bar ~~baz~~ qux');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should fix mixed formatting with strikethrough', () => {
|
|
126
|
+
expect(fixMarkdownFormatting('**bold **text _italic _text ~~strike ~~text')).toBe(
|
|
127
|
+
'**bold** text _italic_ text ~~strike~~ text',
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should not affect properly closed strikethrough markers', () => {
|
|
132
|
+
expect(fixMarkdownFormatting('~~foo~~ bar')).toBe('~~foo~~ bar');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('increaseListIndentation', () => {
|
|
137
|
+
it('should double indentation for bullet lists', () => {
|
|
138
|
+
expect(increaseListIndentation(' - item')).toBe(' - item');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should double indentation for numbered lists', () => {
|
|
142
|
+
expect(increaseListIndentation(' 1. item')).toBe(' 1. item');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should handle different list markers', () => {
|
|
146
|
+
expect(increaseListIndentation(' - a\n + b\n * c')).toBe(' - a\n + b\n * c');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should double different indentation levels', () => {
|
|
150
|
+
expect(increaseListIndentation(' - level 1\n - level 2')).toBe(
|
|
151
|
+
' - level 1\n - level 2',
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should not affect non-list content', () => {
|
|
156
|
+
expect(increaseListIndentation('regular text')).toBe('regular text');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should not affect lists without preceding spaces', () => {
|
|
160
|
+
expect(increaseListIndentation('- item')).toBe('- item');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should handle mixed content', () => {
|
|
164
|
+
expect(increaseListIndentation('paragraph\n - item\nmore text')).toBe(
|
|
165
|
+
'paragraph\n - item\nmore text',
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should handle lists with various indentation levels', () => {
|
|
170
|
+
expect(increaseListIndentation(' - a\n - b\n - c')).toBe(
|
|
171
|
+
' - a\n - b\n - c',
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should return unchanged string if no matching lists', () => {
|
|
176
|
+
expect(increaseListIndentation('paragraph with - no list')).toBe('paragraph with - no list');
|
|
177
|
+
});
|
|
178
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltia/ui",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.12",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -28,31 +28,31 @@
|
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@sveltejs/adapter-auto": "^7.0.0",
|
|
31
|
-
"@sveltejs/kit": "^2.
|
|
31
|
+
"@sveltejs/kit": "^2.50.0",
|
|
32
32
|
"@sveltejs/package": "^2.5.7",
|
|
33
|
-
"@sveltejs/vite-plugin-svelte": "^6.2.
|
|
34
|
-
"cspell": "^9.
|
|
33
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
34
|
+
"cspell": "^9.6.0",
|
|
35
35
|
"eslint": "^8.57.1",
|
|
36
36
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
37
37
|
"eslint-config-prettier": "^10.1.8",
|
|
38
38
|
"eslint-plugin-import": "^2.32.0",
|
|
39
|
-
"eslint-plugin-jsdoc": "^
|
|
39
|
+
"eslint-plugin-jsdoc": "^62.2.0",
|
|
40
40
|
"eslint-plugin-svelte": "^2.46.1",
|
|
41
|
-
"oxlint": "^1.
|
|
41
|
+
"oxlint": "^1.41.0",
|
|
42
42
|
"postcss": "^8.5.6",
|
|
43
|
-
"postcss-html": "^1.8.
|
|
44
|
-
"prettier": "^3.
|
|
45
|
-
"prettier-plugin-svelte": "^3.4.
|
|
46
|
-
"sass": "^1.
|
|
47
|
-
"stylelint": "^
|
|
48
|
-
"stylelint-config-recommended-scss": "^
|
|
49
|
-
"stylelint-scss": "^
|
|
50
|
-
"svelte": "^5.
|
|
51
|
-
"svelte-check": "^4.3.
|
|
43
|
+
"postcss-html": "^1.8.1",
|
|
44
|
+
"prettier": "^3.8.0",
|
|
45
|
+
"prettier-plugin-svelte": "^3.4.1",
|
|
46
|
+
"sass": "^1.97.2",
|
|
47
|
+
"stylelint": "^17.0.0",
|
|
48
|
+
"stylelint-config-recommended-scss": "^17.0.0",
|
|
49
|
+
"stylelint-scss": "^7.0.0",
|
|
50
|
+
"svelte": "^5.47.1",
|
|
51
|
+
"svelte-check": "^4.3.5",
|
|
52
52
|
"svelte-preprocess": "^6.0.3",
|
|
53
53
|
"tslib": "^2.8.1",
|
|
54
|
-
"vite": "^7.
|
|
55
|
-
"vitest": "^4.0.
|
|
54
|
+
"vite": "^7.3.1",
|
|
55
|
+
"vitest": "^4.0.17"
|
|
56
56
|
},
|
|
57
57
|
"exports": {
|
|
58
58
|
".": {
|