testomatio-editor-blocks 0.4.0 → 0.4.6
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/editor/blocks/snippet.js +32 -138
- package/package/editor/blocks/step.d.ts +6 -0
- package/package/editor/blocks/step.js +119 -52
- package/package/editor/blocks/stepField.d.ts +4 -1
- package/package/editor/blocks/stepField.js +651 -39
- package/package/editor/blocks/stepHorizontalView.d.ts +14 -0
- package/package/editor/blocks/stepHorizontalView.js +7 -0
- package/package/editor/customMarkdownConverter.d.ts +1 -0
- package/package/editor/customMarkdownConverter.js +153 -41
- package/package/editor/customSchema.d.ts +6 -0
- package/package/styles.css +569 -122
- package/package.json +5 -1
- package/src/editor/blocks/snippet.tsx +92 -212
- package/src/editor/blocks/step.tsx +268 -123
- package/src/editor/blocks/stepField.tsx +907 -95
- package/src/editor/blocks/stepHorizontalView.tsx +90 -0
- package/src/editor/customMarkdownConverter.test.ts +594 -29
- package/src/editor/customMarkdownConverter.ts +183 -42
- package/src/editor/markdownToBlocks.test.ts +2 -0
- package/src/editor/styles.css +565 -133
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
type StepHorizontalViewProps = {
|
|
3
|
+
blockId: string;
|
|
4
|
+
stepNumber: number;
|
|
5
|
+
stepValue: string;
|
|
6
|
+
expectedResult: string;
|
|
7
|
+
onStepChange: (next: string) => void;
|
|
8
|
+
onExpectedChange: (next: string) => void;
|
|
9
|
+
onInsertNextStep: () => void;
|
|
10
|
+
onFieldFocus: () => void;
|
|
11
|
+
viewToggle?: ReactNode;
|
|
12
|
+
};
|
|
13
|
+
export declare function StepHorizontalView({ blockId, stepNumber, stepValue, expectedResult, onStepChange, onExpectedChange, onInsertNextStep, onFieldFocus, viewToggle, }: StepHorizontalViewProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { StepField } from "./stepField";
|
|
3
|
+
const STEP_PLACEHOLDER = "Enter step name";
|
|
4
|
+
const EXPECTED_RESULT_PLACEHOLDER = "Enter expected result";
|
|
5
|
+
export function StepHorizontalView({ blockId, stepNumber, stepValue, expectedResult, onStepChange, onExpectedChange, onInsertNextStep, onFieldFocus, viewToggle, }) {
|
|
6
|
+
return (_jsxs("div", { className: "bn-teststep bn-teststep--horizontal", "data-block-id": blockId, children: [_jsxs("div", { className: "bn-teststep__timeline", children: [_jsx("span", { className: "bn-teststep__number", children: stepNumber }), _jsx("div", { className: "bn-teststep__line" })] }), _jsxs("div", { className: "bn-teststep__content", children: [_jsxs("div", { className: "bn-teststep__horizontal-fields", children: [_jsxs("div", { className: "bn-teststep__horizontal-col", children: [_jsx("div", { className: "bn-teststep__header", children: _jsx("span", { className: "bn-teststep__title", children: "Step" }) }), _jsx(StepField, { label: "Step", showLabel: false, value: stepValue, onChange: onStepChange, placeholder: STEP_PLACEHOLDER, enableAutocomplete: true, fieldName: "title", suggestionFilter: (suggestion) => suggestion.isSnippet !== true, onFieldFocus: onFieldFocus, multiline: true, enableImageUpload: true, showFormattingButtons: true, showImageButton: true })] }), _jsxs("div", { className: "bn-teststep__horizontal-col", children: [_jsxs("div", { className: "bn-teststep__header", children: [_jsx("span", { className: "bn-teststep__title", children: "Expected result" }), viewToggle] }), _jsx(StepField, { label: "Expected result", showLabel: false, value: expectedResult, onChange: onExpectedChange, placeholder: EXPECTED_RESULT_PLACEHOLDER, multiline: true, enableAutocomplete: true, enableImageUpload: true, showFormattingButtons: true, showImageButton: true, onFieldFocus: onFieldFocus })] })] }), _jsx("div", { className: "bn-step-actions", children: _jsxs("button", { type: "button", className: "bn-step-action-btn", onClick: onInsertNextStep, children: [_jsx("svg", { className: "bn-step-action-btn__icon", width: "16", height: "16", viewBox: "0 0 13.334 13.334", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M6.667 0a6.667 6.667 0 1 1 0 13.334A6.667 6.667 0 0 1 6.667 0Zm0 1.334a5.333 5.333 0 1 0 0 10.666 5.333 5.333 0 0 0 0-10.666ZM7.334 3.334V6H10v1.334H7.334V10H6V7.334H3.334V6H6V3.334h1.334Z", fill: "currentColor" }) }), "Add new step"] }) })] })] }));
|
|
7
|
+
}
|
|
@@ -4,5 +4,6 @@ type Schema = typeof customSchema;
|
|
|
4
4
|
export type CustomEditorBlock = Block<Schema["blockSchema"], Schema["inlineContentSchema"], Schema["styleSchema"]>;
|
|
5
5
|
export type CustomPartialBlock = PartialBlock<Schema["blockSchema"], Schema["inlineContentSchema"], Schema["styleSchema"]>;
|
|
6
6
|
export declare function blocksToMarkdown(blocks: CustomEditorBlock[]): string;
|
|
7
|
+
export declare function fixMalformedImageBlocks(blocks: CustomPartialBlock[]): CustomPartialBlock[];
|
|
7
8
|
export declare function markdownToBlocks(markdown: string): CustomPartialBlock[];
|
|
8
9
|
export {};
|
|
@@ -23,7 +23,7 @@ const headingPrefixes = {
|
|
|
23
23
|
const SPECIAL_CHAR_REGEX = /([*_`~\[\]()<>\\])/g;
|
|
24
24
|
const HTML_SPAN_REGEX = /<\/?span[^>]*>/g;
|
|
25
25
|
const HTML_UNDERLINE_REGEX = /<\/?u>/g;
|
|
26
|
-
const EXPECTED_LABEL_REGEX = /^(?:[*_`]*\s*)?(expected(?:\s+result)?)\s*(?:[*_`]*\s*)
|
|
26
|
+
const EXPECTED_LABEL_REGEX = /^(?:[*_`]*\s*)?(expected(?:\s+result)?)\s*(?:[*_`]*\s*)?\s*[:\-–—]\s*/i;
|
|
27
27
|
// Matches any non-empty line that falls between the step title and the expected result line.
|
|
28
28
|
const STEP_DATA_LINE_REGEX = /^(?!\s*(?:[*_`]*\s*)?(?:expected(?:\s+result)?)\b).+/i;
|
|
29
29
|
const NUMBERED_STEP_REGEX = /^\d+[.)]\s+/;
|
|
@@ -124,22 +124,39 @@ function inlineToMarkdown(content) {
|
|
|
124
124
|
if (!content || !Array.isArray(content)) {
|
|
125
125
|
return "";
|
|
126
126
|
}
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
const result = [];
|
|
128
|
+
let i = 0;
|
|
129
|
+
while (i < content.length) {
|
|
130
|
+
const item = content[i];
|
|
129
131
|
if (isStyledTextInlineContent(item)) {
|
|
130
|
-
|
|
132
|
+
// Check if this is a "!" followed by a link (image syntax)
|
|
133
|
+
if (item.text === "!" && i + 1 < content.length && isLinkInlineContent(content[i + 1])) {
|
|
134
|
+
const link = content[i + 1];
|
|
135
|
+
const inner = inlineToMarkdown(link.content);
|
|
136
|
+
const safeHref = escapeMarkdown(link.href);
|
|
137
|
+
result.push(``);
|
|
138
|
+
i += 2;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
result.push(applyTextStyles(escapeMarkdown(item.text), item.styles));
|
|
142
|
+
i += 1;
|
|
143
|
+
continue;
|
|
131
144
|
}
|
|
132
145
|
if (isLinkInlineContent(item)) {
|
|
133
146
|
const inner = inlineToMarkdown(item.content);
|
|
134
147
|
const safeHref = escapeMarkdown(item.href);
|
|
135
|
-
|
|
148
|
+
result.push(`[${inner}](${safeHref})`);
|
|
149
|
+
i += 1;
|
|
150
|
+
continue;
|
|
136
151
|
}
|
|
137
152
|
if (Array.isArray(item.content)) {
|
|
138
|
-
|
|
153
|
+
result.push(inlineToMarkdown(item.content));
|
|
154
|
+
i += 1;
|
|
155
|
+
continue;
|
|
139
156
|
}
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
157
|
+
i += 1;
|
|
158
|
+
}
|
|
159
|
+
return result.join("");
|
|
143
160
|
}
|
|
144
161
|
function inlineContentToPlainText(content) {
|
|
145
162
|
if (!Array.isArray(content)) {
|
|
@@ -173,8 +190,8 @@ function flattenWithBlankLine(lines, appendBlank = false) {
|
|
|
173
190
|
}
|
|
174
191
|
return lines;
|
|
175
192
|
}
|
|
176
|
-
function serializeBlock(block, ctx, orderedIndex) {
|
|
177
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
193
|
+
function serializeBlock(block, ctx, orderedIndex, stepIndex) {
|
|
194
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
178
195
|
const lines = [];
|
|
179
196
|
const indent = ctx.listDepth > 0 ? " ".repeat(ctx.listDepth) : "";
|
|
180
197
|
switch (block.type) {
|
|
@@ -275,7 +292,9 @@ function serializeBlock(block, ctx, orderedIndex) {
|
|
|
275
292
|
.filter((segment) => segment.length > 0)
|
|
276
293
|
.join(" ");
|
|
277
294
|
if (normalizedTitle.length > 0) {
|
|
278
|
-
|
|
295
|
+
const listStyle = (_m = block.props.listStyle) !== null && _m !== void 0 ? _m : "bullet";
|
|
296
|
+
const prefix = listStyle === "ordered" ? `${(stepIndex !== null && stepIndex !== void 0 ? stepIndex : 0) + 1}.` : "*";
|
|
297
|
+
lines.push(`${prefix} ${normalizedTitle}`);
|
|
279
298
|
}
|
|
280
299
|
}
|
|
281
300
|
if (stepData.length > 0) {
|
|
@@ -331,7 +350,7 @@ function serializeBlock(block, ctx, orderedIndex) {
|
|
|
331
350
|
return flattenWithBlankLine(lines, true);
|
|
332
351
|
}
|
|
333
352
|
const headerRowCount = rows.length
|
|
334
|
-
? Math.min(rows.length, Math.max((
|
|
353
|
+
? Math.min(rows.length, Math.max((_o = tableContent.headerRows) !== null && _o !== void 0 ? _o : 1, 1))
|
|
335
354
|
: 0;
|
|
336
355
|
const columnAlignments = new Array(columnCount).fill("left");
|
|
337
356
|
const getCellAlignment = (cell) => {
|
|
@@ -408,6 +427,7 @@ function serializeBlock(block, ctx, orderedIndex) {
|
|
|
408
427
|
function serializeBlocks(blocks, ctx) {
|
|
409
428
|
const lines = [];
|
|
410
429
|
let orderedIndex = null;
|
|
430
|
+
let stepIndex = 0;
|
|
411
431
|
for (const block of blocks) {
|
|
412
432
|
if (block.type === "numberedListItem") {
|
|
413
433
|
if (typeof block.props.start === "number") {
|
|
@@ -420,6 +440,13 @@ function serializeBlocks(blocks, ctx) {
|
|
|
420
440
|
orderedIndex += 1;
|
|
421
441
|
continue;
|
|
422
442
|
}
|
|
443
|
+
if (block.type === "testStep") {
|
|
444
|
+
lines.push(...serializeBlock(block, ctx, undefined, stepIndex));
|
|
445
|
+
stepIndex += 1;
|
|
446
|
+
orderedIndex = null;
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
stepIndex = 0;
|
|
423
450
|
orderedIndex = null;
|
|
424
451
|
lines.push(...serializeBlock(block, ctx));
|
|
425
452
|
}
|
|
@@ -497,10 +524,13 @@ function parseInlineMarkdown(text) {
|
|
|
497
524
|
pushPlain();
|
|
498
525
|
const label = cleaned.slice(i + 1, endLabel);
|
|
499
526
|
const href = cleaned.slice(startLink + 1, endLink);
|
|
527
|
+
const parsedLabel = parseInlineMarkdown(label);
|
|
528
|
+
// Ensure link content is never undefined - if empty, add empty text
|
|
529
|
+
const linkContent = parsedLabel.length > 0 ? parsedLabel : [{ type: "text", text: "", styles: {} }];
|
|
500
530
|
result.push({
|
|
501
531
|
type: "link",
|
|
502
532
|
href: unescapeMarkdown(href),
|
|
503
|
-
content:
|
|
533
|
+
content: linkContent,
|
|
504
534
|
});
|
|
505
535
|
i = endLink + 1;
|
|
506
536
|
continue;
|
|
@@ -593,14 +623,21 @@ function parseList(lines, startIndex, listType, indentLevel, allowEmptySteps = f
|
|
|
593
623
|
if (detectedType !== listType) {
|
|
594
624
|
break;
|
|
595
625
|
}
|
|
596
|
-
// Only try to parse as testStep for top-level
|
|
597
|
-
//
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
626
|
+
// Only try to parse as testStep for top-level items (indentLevel === 0)
|
|
627
|
+
// when we're under a Steps heading AND the list type is bullet
|
|
628
|
+
// Numbered lists under Steps heading are only parsed as test steps if they look like test steps
|
|
629
|
+
if (indentLevel === 0 && (allowEmptySteps || listType === "bullet")) {
|
|
630
|
+
// For bullet lists, always try to parse as test steps
|
|
631
|
+
// For numbered lists, only try if they have step-like characteristics
|
|
632
|
+
const looksLikeTestStep = listType === "bullet" ||
|
|
633
|
+
(listType === "numbered" && (isLikelyStep(lines, index)));
|
|
634
|
+
if (looksLikeTestStep) {
|
|
635
|
+
const nextStep = parseTestStep(lines, index, allowEmptySteps);
|
|
636
|
+
if (nextStep) {
|
|
637
|
+
items.push(nextStep.block);
|
|
638
|
+
index = nextStep.nextIndex;
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
604
641
|
}
|
|
605
642
|
}
|
|
606
643
|
if (listType === "check") {
|
|
@@ -639,13 +676,46 @@ function parseList(lines, startIndex, listType, indentLevel, allowEmptySteps = f
|
|
|
639
676
|
}
|
|
640
677
|
return { items, nextIndex: index };
|
|
641
678
|
}
|
|
679
|
+
function isLikelyStep(lines, index) {
|
|
680
|
+
// Look ahead to see if there's indented content or expected result
|
|
681
|
+
if (index + 1 >= lines.length)
|
|
682
|
+
return false;
|
|
683
|
+
const nextLine = lines[index + 1];
|
|
684
|
+
const hasIndent = /^\s{2,}/.test(nextLine);
|
|
685
|
+
// Check if the next line contains expected result markers
|
|
686
|
+
const nextTrimmed = nextLine.trim();
|
|
687
|
+
const hasExpectedResult = EXPECTED_LABEL_REGEX.test(nextTrimmed);
|
|
688
|
+
// Only consider it a test step if:
|
|
689
|
+
// 1. It has an expected result, OR
|
|
690
|
+
// 2. The next line is indented but doesn't start with a numbered or bullet list
|
|
691
|
+
if (hasExpectedResult)
|
|
692
|
+
return true;
|
|
693
|
+
if (hasIndent && !/^\d+[.)]/.test(nextTrimmed) && !/^[-*+]/.test(nextTrimmed))
|
|
694
|
+
return true;
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
642
697
|
function parseTestStep(lines, index, allowEmpty = false, snippetId) {
|
|
643
698
|
const current = lines[index];
|
|
644
699
|
const trimmed = current.trim();
|
|
645
|
-
|
|
700
|
+
const isBullet = trimmed.startsWith("* ") || trimmed.startsWith("- ");
|
|
701
|
+
const isNumbered = /^\d+[.)]\s+/.test(trimmed);
|
|
702
|
+
if (!isBullet && !isNumbered) {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
// For numbered lists, only parse as test steps if called from parseList with allowEmpty=true
|
|
706
|
+
// The first call to parseTestStep from markdownToBlocks uses allowEmpty=stepsHeadingLevel !== null
|
|
707
|
+
// which should be false unless we're under a Steps heading
|
|
708
|
+
if (isNumbered && !allowEmpty) {
|
|
646
709
|
return null;
|
|
647
710
|
}
|
|
648
|
-
let rawTitle
|
|
711
|
+
let rawTitle;
|
|
712
|
+
if (isBullet) {
|
|
713
|
+
rawTitle = unescapeMarkdown(trimmed.slice(2)).trim();
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
716
|
+
// For numbered lists, remove the number and delimiter
|
|
717
|
+
rawTitle = unescapeMarkdown(trimmed.replace(/^\d+[.)]\s+/, "")).trim();
|
|
718
|
+
}
|
|
649
719
|
let blockType = "testStep";
|
|
650
720
|
const snippetMatch = rawTitle.match(/^snippet\s*[:\-–—]?\s*(.*)$/i);
|
|
651
721
|
if (snippetMatch) {
|
|
@@ -667,7 +737,6 @@ function parseTestStep(lines, index, allowEmpty = false, snippetId) {
|
|
|
667
737
|
let expectedResult = "";
|
|
668
738
|
let next = index + 1;
|
|
669
739
|
let inExpectedResult = false;
|
|
670
|
-
let foundFirstExpected = false;
|
|
671
740
|
while (next < lines.length) {
|
|
672
741
|
const line = lines[next];
|
|
673
742
|
const hasIndent = /^\s{2,}/.test(line);
|
|
@@ -701,7 +770,6 @@ function parseTestStep(lines, index, allowEmpty = false, snippetId) {
|
|
|
701
770
|
const expectedStarMatch = rawTrimmed.match(/^\*expected\s*\*:\s*(.*)$/i) ||
|
|
702
771
|
rawTrimmed.match(/^\*expected\*:\s*(.*)$/i);
|
|
703
772
|
if (expectedMatch || expectedStarMatch) {
|
|
704
|
-
foundFirstExpected = true;
|
|
705
773
|
inExpectedResult = true;
|
|
706
774
|
const label = expectedMatch ? expectedMatch[0] : (expectedStarMatch ? expectedStarMatch[0] : '');
|
|
707
775
|
let content = rawTrimmed.slice(label.length).trim();
|
|
@@ -720,7 +788,6 @@ function parseTestStep(lines, index, allowEmpty = false, snippetId) {
|
|
|
720
788
|
}
|
|
721
789
|
// Check for lines that start with * and contain Expected (but don't match the above patterns)
|
|
722
790
|
if (rawTrimmed.match(/^\*[^*]*expected/i)) {
|
|
723
|
-
foundFirstExpected = true;
|
|
724
791
|
inExpectedResult = true;
|
|
725
792
|
// Remove the leading * and trim
|
|
726
793
|
let content = rawTrimmed.slice(1).trim();
|
|
@@ -776,17 +843,6 @@ function parseTestStep(lines, index, allowEmpty = false, snippetId) {
|
|
|
776
843
|
expectedResult = expectedContent;
|
|
777
844
|
}
|
|
778
845
|
}
|
|
779
|
-
else if (foundFirstExpected && rawTrimmed.startsWith("*") && !rawTrimmed.startsWith("* ")) {
|
|
780
|
-
// Non-indented lines starting with single * (not list item) are likely more expected results
|
|
781
|
-
// Remove the leading * and treat the rest as content
|
|
782
|
-
const expectedContent = unescapeMarkdown(rawTrimmed.slice(1).trim());
|
|
783
|
-
if (expectedResult.length > 0) {
|
|
784
|
-
expectedResult += "\n" + expectedContent;
|
|
785
|
-
}
|
|
786
|
-
else {
|
|
787
|
-
expectedResult = expectedContent;
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
846
|
next += 1;
|
|
791
847
|
continue;
|
|
792
848
|
}
|
|
@@ -809,7 +865,8 @@ function parseTestStep(lines, index, allowEmpty = false, snippetId) {
|
|
|
809
865
|
.map((line) => line.trimEnd())
|
|
810
866
|
.join("\n")
|
|
811
867
|
.trim();
|
|
812
|
-
if (!
|
|
868
|
+
if (!isBullet &&
|
|
869
|
+
!isLikelyStep &&
|
|
813
870
|
!expectedResult &&
|
|
814
871
|
stepDataLines.length === 0 &&
|
|
815
872
|
!(allowEmpty && titleWithPlaceholders.length > 0)) {
|
|
@@ -829,6 +886,7 @@ function parseTestStep(lines, index, allowEmpty = false, snippetId) {
|
|
|
829
886
|
stepTitle: titleWithPlaceholders,
|
|
830
887
|
stepData: stepDataWithImages,
|
|
831
888
|
expectedResult,
|
|
889
|
+
listStyle: isNumbered ? "ordered" : "bullet",
|
|
832
890
|
};
|
|
833
891
|
const parsedBlock = {
|
|
834
892
|
type: blockType,
|
|
@@ -1014,6 +1072,60 @@ function parseSnippetWrapper(lines, index) {
|
|
|
1014
1072
|
nextIndex: next,
|
|
1015
1073
|
};
|
|
1016
1074
|
}
|
|
1075
|
+
// Post-process blocks to fix malformed image blocks
|
|
1076
|
+
export function fixMalformedImageBlocks(blocks) {
|
|
1077
|
+
var _a, _b, _c, _d;
|
|
1078
|
+
const result = [];
|
|
1079
|
+
let i = 0;
|
|
1080
|
+
while (i < blocks.length) {
|
|
1081
|
+
const current = blocks[i];
|
|
1082
|
+
const next = blocks[i + 1];
|
|
1083
|
+
// Skip empty paragraphs
|
|
1084
|
+
if (current.type === "paragraph" &&
|
|
1085
|
+
(!current.content || !Array.isArray(current.content) || current.content.length === 0)) {
|
|
1086
|
+
i += 1;
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
// Check if current is a paragraph with just "!" - this is definitely a malformed image
|
|
1090
|
+
if (current.type === "paragraph" &&
|
|
1091
|
+
current.content &&
|
|
1092
|
+
Array.isArray(current.content) &&
|
|
1093
|
+
current.content.length === 1 &&
|
|
1094
|
+
((_a = current.content[0]) === null || _a === void 0 ? void 0 : _a.type) === "text" &&
|
|
1095
|
+
((_b = current.content[0]) === null || _b === void 0 ? void 0 : _b.text) === "!") {
|
|
1096
|
+
// This is a malformed image block, skip it entirely
|
|
1097
|
+
// The full image was likely parsed as  but got corrupted
|
|
1098
|
+
i += 1;
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
1101
|
+
// Check if current is a paragraph with just "!" and next is an empty paragraph
|
|
1102
|
+
if (current.type === "paragraph" &&
|
|
1103
|
+
(next === null || next === void 0 ? void 0 : next.type) === "paragraph" &&
|
|
1104
|
+
current.content &&
|
|
1105
|
+
Array.isArray(current.content) &&
|
|
1106
|
+
current.content.length === 1 &&
|
|
1107
|
+
((_c = current.content[0]) === null || _c === void 0 ? void 0 : _c.type) === "text" &&
|
|
1108
|
+
((_d = current.content[0]) === null || _d === void 0 ? void 0 : _d.text) === "!" &&
|
|
1109
|
+
(!next.content || !Array.isArray(next.content) || next.content.length === 0)) {
|
|
1110
|
+
// This looks like a malformed image, skip both blocks
|
|
1111
|
+
i += 2;
|
|
1112
|
+
continue;
|
|
1113
|
+
}
|
|
1114
|
+
// Check if current has "!" but no link
|
|
1115
|
+
if (current.type === "paragraph" &&
|
|
1116
|
+
current.content &&
|
|
1117
|
+
Array.isArray(current.content) &&
|
|
1118
|
+
current.content.some((item) => item.type === "text" && item.text === "!") &&
|
|
1119
|
+
!current.content.some((item) => item.type === "link")) {
|
|
1120
|
+
// Skip malformed image block
|
|
1121
|
+
i += 1;
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
result.push(current);
|
|
1125
|
+
i += 1;
|
|
1126
|
+
}
|
|
1127
|
+
return result;
|
|
1128
|
+
}
|
|
1017
1129
|
export function markdownToBlocks(markdown) {
|
|
1018
1130
|
var _a, _b;
|
|
1019
1131
|
const normalized = markdown.replace(/\r\n/g, "\n");
|
|
@@ -1051,7 +1163,7 @@ export function markdownToBlocks(markdown) {
|
|
|
1051
1163
|
const headingLevel = (_b = (_a = headingBlock.props) === null || _a === void 0 ? void 0 : _a.level) !== null && _b !== void 0 ? _b : 3;
|
|
1052
1164
|
const headingText = inlineContentToPlainText(headingBlock.content);
|
|
1053
1165
|
const normalizedHeading = headingText.trim().toLowerCase();
|
|
1054
|
-
if (normalizedHeading === "steps") {
|
|
1166
|
+
if (normalizedHeading.replace(/[:\-–—]$/, "") === "steps") {
|
|
1055
1167
|
stepsHeadingLevel = headingLevel;
|
|
1056
1168
|
}
|
|
1057
1169
|
else if (stepsHeadingLevel !== null &&
|
|
@@ -1086,7 +1198,7 @@ export function markdownToBlocks(markdown) {
|
|
|
1086
1198
|
blocks.push(paragraph.block);
|
|
1087
1199
|
index = paragraph.nextIndex;
|
|
1088
1200
|
}
|
|
1089
|
-
return blocks;
|
|
1201
|
+
return fixMalformedImageBlocks(blocks);
|
|
1090
1202
|
}
|
|
1091
1203
|
function splitTableRow(line) {
|
|
1092
1204
|
let value = line.trim();
|
|
@@ -15,6 +15,9 @@ export declare const customSchema: BlockNoteSchema<import("@blocknote/core").Blo
|
|
|
15
15
|
readonly expectedResult: {
|
|
16
16
|
readonly default: "";
|
|
17
17
|
};
|
|
18
|
+
readonly listStyle: {
|
|
19
|
+
readonly default: "bullet";
|
|
20
|
+
};
|
|
18
21
|
};
|
|
19
22
|
};
|
|
20
23
|
implementation: import("@blocknote/core").TiptapBlockImplementation<{
|
|
@@ -30,6 +33,9 @@ export declare const customSchema: BlockNoteSchema<import("@blocknote/core").Blo
|
|
|
30
33
|
readonly expectedResult: {
|
|
31
34
|
readonly default: "";
|
|
32
35
|
};
|
|
36
|
+
readonly listStyle: {
|
|
37
|
+
readonly default: "bullet";
|
|
38
|
+
};
|
|
33
39
|
};
|
|
34
40
|
}, any, import("@blocknote/core").InlineContentSchema, import("@blocknote/core").StyleSchema>;
|
|
35
41
|
};
|