@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Kohei Yoshino.
3
+ Copyright (c) 2026 Kohei Yoshino.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -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
- // @see https://github.com/sveltia/sveltia-cms/issues/548
414
- value = value
415
- .replace(/(\s+)_([^_\n]+?)\n([^_\n]+?)_(\s+)/gm, '$1_$2_\n_$3_$4')
416
- .replace(/(\s+)\*\*([^*\n]+?)\n([^*\n]+?)\*\*(\s+)/gm, '$1**$2**\n**$3**$4')
417
- .replace(/(\s+)~~([^~\n]+?)\n([^~\n]+?)~~(\s+)/gm, '$1~~$2~~\n~~$3~~$4')
418
- .replace(/(\s+)`([^`\n]+?)\n([^`\n]+?)`(\s+)/gm, '$1`$2`\n`$3`$4');
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,3 @@
1
+ export function splitMultilineFormatting(value: string): string;
2
+ export function fixMarkdownFormatting(value: string): string;
3
+ export function increaseListIndentation(value: string): string;
@@ -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.10",
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.49.2",
31
+ "@sveltejs/kit": "^2.50.0",
32
32
  "@sveltejs/package": "^2.5.7",
33
- "@sveltejs/vite-plugin-svelte": "^6.2.1",
34
- "cspell": "^9.4.0",
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": "^61.5.0",
39
+ "eslint-plugin-jsdoc": "^62.2.0",
40
40
  "eslint-plugin-svelte": "^2.46.1",
41
- "oxlint": "^1.32.0",
41
+ "oxlint": "^1.41.0",
42
42
  "postcss": "^8.5.6",
43
- "postcss-html": "^1.8.0",
44
- "prettier": "^3.7.4",
45
- "prettier-plugin-svelte": "^3.4.0",
46
- "sass": "^1.96.0",
47
- "stylelint": "^16.26.1",
48
- "stylelint-config-recommended-scss": "^16.0.2",
49
- "stylelint-scss": "^6.13.0",
50
- "svelte": "^5.46.0",
51
- "svelte-check": "^4.3.4",
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.2.7",
55
- "vitest": "^4.0.15"
54
+ "vite": "^7.3.1",
55
+ "vitest": "^4.0.17"
56
56
  },
57
57
  "exports": {
58
58
  ".": {