testomatio-editor-blocks 0.4.49 → 0.4.50
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/step.d.ts +1 -1
- package/package/editor/blocks/step.js +8 -18
- package/package/editor/blocks/stepField.d.ts +11 -0
- package/package/editor/blocks/stepField.js +227 -84
- package/package/styles.css +12 -3
- package/package.json +1 -1
- package/src/editor/blocks/step.tsx +26 -42
- package/src/editor/blocks/stepField.tsx +266 -80
- package/src/editor/blocks/stepFieldFormatting.test.ts +44 -0
- package/src/editor/styles.css +12 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns true when a normalised (lowercased, trailing-punctuation-stripped)
|
|
3
3
|
* heading text looks like a "Steps" heading.
|
|
4
|
-
* Accepted forms: steps, step, step(s).
|
|
4
|
+
* Accepted forms: steps, step, step(s), test steps, test step, test step(s).
|
|
5
5
|
*/
|
|
6
6
|
export declare function isStepsHeading(text: string): boolean;
|
|
7
7
|
export declare const isEmptyParagraph: (b: any) => boolean;
|
|
@@ -47,10 +47,10 @@ const writeStepViewMode = (mode) => {
|
|
|
47
47
|
/**
|
|
48
48
|
* Returns true when a normalised (lowercased, trailing-punctuation-stripped)
|
|
49
49
|
* heading text looks like a "Steps" heading.
|
|
50
|
-
* Accepted forms: steps, step, step(s).
|
|
50
|
+
* Accepted forms: steps, step, step(s), test steps, test step, test step(s).
|
|
51
51
|
*/
|
|
52
52
|
export function isStepsHeading(text) {
|
|
53
|
-
return /^step(s|\(s\))?$/.test(text);
|
|
53
|
+
return /^(test\s+)?step(s|\(s\))?$/.test(text);
|
|
54
54
|
}
|
|
55
55
|
export const isEmptyParagraph = (b) => b.type === "paragraph" &&
|
|
56
56
|
(!Array.isArray(b.content) ||
|
|
@@ -307,11 +307,6 @@ export const stepBlock = createReactBlockSpec({
|
|
|
307
307
|
},
|
|
308
308
|
});
|
|
309
309
|
}, [block.id, combinedStepValue, editor]);
|
|
310
|
-
useEffect(() => {
|
|
311
|
-
if (dataHasContent && !isDataVisible) {
|
|
312
|
-
setIsDataVisible(true);
|
|
313
|
-
}
|
|
314
|
-
}, [dataHasContent, isDataVisible]);
|
|
315
310
|
const handleStepTitleChange = useCallback((next) => {
|
|
316
311
|
if (next === stepTitle) {
|
|
317
312
|
return;
|
|
@@ -338,7 +333,8 @@ export const stepBlock = createReactBlockSpec({
|
|
|
338
333
|
}, []);
|
|
339
334
|
const handleHideData = useCallback(() => {
|
|
340
335
|
setIsDataVisible(false);
|
|
341
|
-
|
|
336
|
+
editor.updateBlock(block.id, { props: { stepData: "" } });
|
|
337
|
+
}, [editor, block.id]);
|
|
342
338
|
const handleExpectedChange = useCallback((next) => {
|
|
343
339
|
if (next === expectedResult) {
|
|
344
340
|
return;
|
|
@@ -402,19 +398,13 @@ export const stepBlock = createReactBlockSpec({
|
|
|
402
398
|
const handleHideExpected = useCallback(() => {
|
|
403
399
|
setIsExpectedVisible(false);
|
|
404
400
|
writeExpectedCollapsedPreference(true);
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
if (expectedHasContent && !isExpectedVisible) {
|
|
408
|
-
setIsExpectedVisible(true);
|
|
409
|
-
}
|
|
410
|
-
}, [expectedHasContent, isExpectedVisible]);
|
|
411
|
-
const canToggleData = !dataHasContent;
|
|
412
|
-
const canToggleExpected = !expectedHasContent;
|
|
401
|
+
editor.updateBlock(block.id, { props: { expectedResult: "" } });
|
|
402
|
+
}, [editor, block.id]);
|
|
413
403
|
const viewToggleButton = (_jsx("button", { type: "button", className: `bn-teststep__view-toggle${!effectiveVertical ? " bn-teststep__view-toggle--horizontal" : ""}${forceVertical ? " bn-teststep__view-toggle--disabled" : ""}`, "data-tooltip": forceVertical ? "Not enough space for horizontal view" : "Switch step view", "aria-label": forceVertical ? "Not enough space for horizontal view" : "Switch step view", onClick: forceVertical ? undefined : handleToggleView, "aria-disabled": forceVertical, children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [_jsx("mask", { id: "mask-toggle", style: { maskType: "alpha" }, maskUnits: "userSpaceOnUse", x: "0", y: "0", width: "16", height: "16", children: _jsx("rect", { width: "16", height: "16", fill: "#D9D9D9" }) }), _jsx("g", { mask: "url(#mask-toggle)", children: _jsx("path", { d: "M12.6667 2C13.0333 2 13.3472 2.13056 13.6083 2.39167C13.8694 2.65278 14 2.96667 14 3.33333L14 12.6667C14 13.0333 13.8694 13.3472 13.6083 13.6083C13.3472 13.8694 13.0333 14 12.6667 14L10 14C9.63333 14 9.31944 13.8694 9.05833 13.6083C8.79722 13.3472 8.66667 13.0333 8.66667 12.6667L8.66667 3.33333C8.66667 2.96667 8.79722 2.65278 9.05833 2.39167C9.31945 2.13055 9.63333 2 10 2L12.6667 2ZM6 2C6.36667 2 6.68056 2.13055 6.94167 2.39167C7.20278 2.65278 7.33333 2.96667 7.33333 3.33333L7.33333 12.6667C7.33333 13.0333 7.20278 13.3472 6.94167 13.6083C6.68055 13.8694 6.36667 14 6 14L3.33333 14C2.96667 14 2.65278 13.8694 2.39167 13.6083C2.13056 13.3472 2 13.0333 2 12.6667L2 3.33333C2 2.96667 2.13056 2.65278 2.39167 2.39167C2.65278 2.13055 2.96667 2 3.33333 2L6 2ZM3.33333 12.6667L6 12.6667L6 3.33333L3.33333 3.33333L3.33333 12.6667Z", fill: "currentColor" }) })] }) }));
|
|
414
404
|
if (!effectiveVertical) {
|
|
415
405
|
return (_jsx(StepHorizontalView, { ref: containerRef, blockId: block.id, stepNumber: stepNumber, stepValue: combinedStepValue, expectedResult: expectedResult, onStepChange: handleCombinedStepChange, onExpectedChange: handleExpectedChange, onInsertNextStep: handleInsertNextStep, onFieldFocus: handleFieldFocus, viewToggle: viewToggleButton }));
|
|
416
406
|
}
|
|
417
|
-
return (_jsxs("div", { className: "bn-teststep", "data-block-id": block.id, ref: containerRef, 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__header", children: [_jsx("span", { className: "bn-teststep__title", children: "Step" }), viewToggleButton] }), _jsx(StepField, { label: "Step", showLabel: false, value: stepTitle, placeholder: STEP_TITLE_PLACEHOLDER, onChange: handleStepTitleChange, autoFocus: stepTitle.length === 0, enableAutocomplete: true, fieldName: "title", suggestionFilter: (suggestion) => suggestion.isSnippet !== true, onFieldFocus: handleFieldFocus, enableImageUpload: false, showFormattingButtons: true, onImageFile: async (file) => {
|
|
407
|
+
return (_jsxs("div", { className: "bn-teststep", "data-block-id": block.id, ref: containerRef, 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__header", children: [_jsx("span", { className: "bn-teststep__title", children: "Step" }), viewToggleButton] }), _jsx(StepField, { label: "Step", showLabel: false, value: stepTitle, placeholder: STEP_TITLE_PLACEHOLDER, onChange: handleStepTitleChange, autoFocus: stepTitle.length === 0, multiline: true, enableAutocomplete: true, fieldName: "title", suggestionFilter: (suggestion) => suggestion.isSnippet !== true, onFieldFocus: handleFieldFocus, enableImageUpload: false, showFormattingButtons: true, onImageFile: async (file) => {
|
|
418
408
|
if (!uploadImage) {
|
|
419
409
|
return;
|
|
420
410
|
}
|
|
@@ -434,6 +424,6 @@ export const stepBlock = createReactBlockSpec({
|
|
|
434
424
|
catch (error) {
|
|
435
425
|
console.error("Failed to upload image to Step Data", error);
|
|
436
426
|
}
|
|
437
|
-
} }), isDataVisible ? (_jsx(StepField, { label: "Step data", placeholder: STEP_DATA_PLACEHOLDER, labelAction:
|
|
427
|
+
} }), isDataVisible ? (_jsx(StepField, { label: "Step data", placeholder: STEP_DATA_PLACEHOLDER, labelAction: _jsx("button", { type: "button", className: "bn-step-field__dismiss", "data-tooltip": "Hide step data", onClick: handleHideData, "aria-label": "Hide step data", children: "\u00D7" }), value: stepData, onChange: handleStepDataChange, autoFocus: shouldFocusDataField, focusSignal: dataFocusSignal, multiline: true, enableAutocomplete: true, enableImageUpload: true, showFormattingButtons: true, showImageButton: true, onFieldFocus: handleFieldFocus })) : null, isExpectedVisible ? (_jsx(StepField, { label: "Expected result", placeholder: EXPECTED_RESULT_PLACEHOLDER, labelAction: _jsx("button", { type: "button", className: "bn-step-field__dismiss", "data-tooltip": "Hide expected result", onClick: handleHideExpected, tabIndex: -1, "aria-label": "Hide expected result", children: "\u00D7" }), value: expectedResult, onChange: handleExpectedChange, multiline: true, focusSignal: expectedFocusSignal, enableAutocomplete: true, enableImageUpload: true, showFormattingButtons: true, showImageButton: true, onFieldFocus: handleFieldFocus })) : null, _jsxs("div", { className: "bn-step-actions", children: [_jsxs("button", { type: "button", className: "bn-step-action-btn", onClick: handleInsertNextStep, 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"] }), !isDataVisible && (_jsxs("button", { type: "button", className: "bn-step-action-btn", onClick: handleShowData, children: [_jsx("svg", { className: "bn-step-action-btn__icon", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M8.666 7.333H12.666V8.667H8.666V12.667H7.332V8.667H3.332V7.333H7.332V3.333H8.666V7.333Z", fill: "currentColor" }) }), "Step data"] })), !isExpectedVisible && (_jsxs("button", { type: "button", className: "bn-step-action-btn", onClick: handleShowExpected, children: [_jsx("svg", { className: "bn-step-action-btn__icon", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M8.666 7.333H12.666V8.667H8.666V12.667H7.332V8.667H3.332V7.333H7.332V3.333H8.666V7.333Z", fill: "currentColor" }) }), "Expected result"] }))] })] })] }));
|
|
438
428
|
},
|
|
439
429
|
});
|
|
@@ -30,5 +30,16 @@ type StepFieldProps = {
|
|
|
30
30
|
showImageButton?: boolean;
|
|
31
31
|
onFieldFocus?: () => void;
|
|
32
32
|
};
|
|
33
|
+
export type LinkMeta = {
|
|
34
|
+
start: number;
|
|
35
|
+
end: number;
|
|
36
|
+
url: string;
|
|
37
|
+
};
|
|
38
|
+
export type FormattingMeta = {
|
|
39
|
+
start: number;
|
|
40
|
+
end: number;
|
|
41
|
+
type: "bold" | "italic" | "code";
|
|
42
|
+
};
|
|
43
|
+
export declare function buildFullMarkdown(plainText: string, links: LinkMeta[], formatting: FormattingMeta[]): string;
|
|
33
44
|
export declare function StepField({ label, showLabel, labelToggle, labelAction, placeholder, value, onChange, autoFocus, focusSignal, multiline, enableAutocomplete, fieldName, suggestionFilter, suggestionsOverride, onSuggestionSelect, readOnly, showSuggestionsOnFocus, enableImageUpload, onImageFile, rightAction, showFormattingButtons, showImageButton, onFieldFocus, }: StepFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
34
45
|
export {};
|
|
@@ -18,6 +18,7 @@ const markdownParser = OverType.MarkdownParser;
|
|
|
18
18
|
function ImageUploadIcon() {
|
|
19
19
|
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "M12.667 2C13.0335 2.00008 13.3474 2.13057 13.6084 2.3916C13.8694 2.65264 13.9999 2.96648 14 3.33301V12.667C13.9999 13.0335 13.8694 13.3474 13.6084 13.6084C13.3474 13.8694 13.0335 13.9999 12.667 14H3.33301C2.96648 13.9999 2.65264 13.8694 2.3916 13.6084C2.13057 13.3474 2.00008 13.0335 2 12.667V3.33301C2.00008 2.96648 2.13057 2.65264 2.3916 2.3916C2.65264 2.13057 2.96648 2.00008 3.33301 2H12.667ZM3.33301 12.667H12.667V3.33301H3.33301V12.667ZM12 11.333H4L6 8.66699L7.5 10.667L9.5 8L12 11.333ZM5.66699 4.66699C5.94455 4.66707 6.18066 4.76375 6.375 4.95801C6.56944 5.15245 6.66699 5.38921 6.66699 5.66699C6.66692 5.94463 6.56937 6.18063 6.375 6.375C6.18063 6.56937 5.94463 6.66692 5.66699 6.66699C5.38921 6.66699 5.15245 6.56944 4.95801 6.375C4.76375 6.18066 4.66707 5.94455 4.66699 5.66699C4.66699 5.38921 4.76356 5.15245 4.95801 4.95801C5.15245 4.76356 5.38921 4.66699 5.66699 4.66699Z", fill: "currentColor" }) }));
|
|
20
20
|
}
|
|
21
|
+
const UNDO_STACK_LIMIT = 100;
|
|
21
22
|
function getActiveFormats(formatting, selStart, selEnd) {
|
|
22
23
|
const active = new Set();
|
|
23
24
|
if (selStart === selEnd)
|
|
@@ -170,7 +171,7 @@ function stripInlineMarkdown(markdown) {
|
|
|
170
171
|
}
|
|
171
172
|
return { plainText, links, formatting };
|
|
172
173
|
}
|
|
173
|
-
function buildFullMarkdown(plainText, links, formatting) {
|
|
174
|
+
export function buildFullMarkdown(plainText, links, formatting) {
|
|
174
175
|
if (links.length === 0 && formatting.length === 0)
|
|
175
176
|
return plainText;
|
|
176
177
|
const markers = [];
|
|
@@ -211,17 +212,24 @@ function buildFullMarkdown(plainText, links, formatting) {
|
|
|
211
212
|
function adjustFormattingForEdit(formatting, editPos, delta) {
|
|
212
213
|
return formatting
|
|
213
214
|
.map((fmt) => {
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
215
|
+
if (delta >= 0) {
|
|
216
|
+
if (editPos <= fmt.start) {
|
|
217
|
+
return { ...fmt, start: fmt.start + delta, end: fmt.end + delta };
|
|
218
|
+
}
|
|
219
|
+
if (editPos >= fmt.end) {
|
|
220
|
+
return fmt;
|
|
221
|
+
}
|
|
222
|
+
return { ...fmt, end: fmt.end + delta };
|
|
219
223
|
}
|
|
220
|
-
|
|
224
|
+
const delEnd = editPos + Math.abs(delta);
|
|
225
|
+
const newStart = fmt.start < editPos ? fmt.start : fmt.start >= delEnd ? fmt.start + delta : editPos;
|
|
226
|
+
const newEnd = fmt.end <= editPos ? fmt.end : fmt.end >= delEnd ? fmt.end + delta : editPos;
|
|
227
|
+
return { ...fmt, start: newStart, end: newEnd };
|
|
221
228
|
})
|
|
222
229
|
.filter((fmt) => fmt.end > fmt.start);
|
|
223
230
|
}
|
|
224
231
|
function getCaretRectInPreview(preview, offset, textareaValue) {
|
|
232
|
+
var _a;
|
|
225
233
|
// Convert textarea-space offset to preview-space (strip newlines)
|
|
226
234
|
let nlCount = 0;
|
|
227
235
|
if (textareaValue) {
|
|
@@ -242,7 +250,16 @@ function getCaretRectInPreview(preview, offset, textareaValue) {
|
|
|
242
250
|
const range = document.createRange();
|
|
243
251
|
range.setStart(textNode, localOffset);
|
|
244
252
|
range.collapse(true);
|
|
245
|
-
|
|
253
|
+
let rect = range.getBoundingClientRect();
|
|
254
|
+
// Collapsed ranges at position 0 can return an empty rect in some browsers
|
|
255
|
+
if (rect.height === 0 && rect.top === 0 && rect.left === 0) {
|
|
256
|
+
const span = document.createElement("span");
|
|
257
|
+
span.textContent = "\u200B";
|
|
258
|
+
range.insertNode(span);
|
|
259
|
+
rect = span.getBoundingClientRect();
|
|
260
|
+
(_a = span.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(span);
|
|
261
|
+
preview.normalize();
|
|
262
|
+
}
|
|
246
263
|
const previewRect = preview.getBoundingClientRect();
|
|
247
264
|
return {
|
|
248
265
|
top: rect.top - previewRect.top + preview.scrollTop,
|
|
@@ -403,13 +420,19 @@ function applyFormattingHighlights(preview, formatting, textareaValue) {
|
|
|
403
420
|
function adjustLinksForEdit(links, editPos, delta) {
|
|
404
421
|
return links
|
|
405
422
|
.map((link) => {
|
|
406
|
-
if (
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
423
|
+
if (delta >= 0) {
|
|
424
|
+
if (editPos <= link.start) {
|
|
425
|
+
return { ...link, start: link.start + delta, end: link.end + delta };
|
|
426
|
+
}
|
|
427
|
+
if (editPos >= link.end) {
|
|
428
|
+
return link;
|
|
429
|
+
}
|
|
430
|
+
return { ...link, end: link.end + delta };
|
|
411
431
|
}
|
|
412
|
-
|
|
432
|
+
const delEnd = editPos + Math.abs(delta);
|
|
433
|
+
const newStart = link.start < editPos ? link.start : link.start >= delEnd ? link.start + delta : editPos;
|
|
434
|
+
const newEnd = link.end <= editPos ? link.end : link.end >= delEnd ? link.end + delta : editPos;
|
|
435
|
+
return { ...link, start: newStart, end: newEnd };
|
|
413
436
|
})
|
|
414
437
|
.filter((link) => link.end > link.start);
|
|
415
438
|
}
|
|
@@ -511,8 +534,8 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
511
534
|
const linkSelectionRef = useRef(null);
|
|
512
535
|
const linksRef = useRef([]);
|
|
513
536
|
const formattingRef = useRef([]);
|
|
514
|
-
const
|
|
515
|
-
const
|
|
537
|
+
const undoStackRef = useRef([]);
|
|
538
|
+
const redoStackRef = useRef([]);
|
|
516
539
|
const caretRef = useRef(null);
|
|
517
540
|
const prevTextRef = useRef("");
|
|
518
541
|
const isSyncingRef = useRef(false);
|
|
@@ -524,10 +547,26 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
524
547
|
useEffect(() => {
|
|
525
548
|
onChangeRef.current = onChange;
|
|
526
549
|
}, [onChange]);
|
|
550
|
+
const pushUndoSnapshot = useCallback((text, formatting, links, cursorStart, cursorEnd) => {
|
|
551
|
+
const lastSnapshot = undoStackRef.current[undoStackRef.current.length - 1];
|
|
552
|
+
if (lastSnapshot &&
|
|
553
|
+
lastSnapshot.text === text &&
|
|
554
|
+
JSON.stringify(lastSnapshot.formatting) === JSON.stringify(formatting) &&
|
|
555
|
+
JSON.stringify(lastSnapshot.links) === JSON.stringify(links)) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
undoStackRef.current = [
|
|
559
|
+
...undoStackRef.current.slice(-(UNDO_STACK_LIMIT - 1)),
|
|
560
|
+
{ text, formatting: [...formatting], links: [...links], cursorStart, cursorEnd },
|
|
561
|
+
];
|
|
562
|
+
redoStackRef.current = [];
|
|
563
|
+
}, []);
|
|
527
564
|
const handleEditorChange = useCallback((nextValue) => {
|
|
528
565
|
var _a;
|
|
529
566
|
if (isSyncingRef.current)
|
|
530
567
|
return;
|
|
568
|
+
if (nextValue === prevTextRef.current)
|
|
569
|
+
return;
|
|
531
570
|
const prevText = prevTextRef.current;
|
|
532
571
|
const delta = nextValue.length - prevText.length;
|
|
533
572
|
// Find where the edit happened by comparing old and new text
|
|
@@ -536,10 +575,13 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
536
575
|
while (editPos < minLen && prevText[editPos] === nextValue[editPos]) {
|
|
537
576
|
editPos++;
|
|
538
577
|
}
|
|
578
|
+
// Capture pre-edit state for undo BEFORE mutating
|
|
579
|
+
const prevFormatting = [...formattingRef.current];
|
|
580
|
+
const prevLinks = [...linksRef.current];
|
|
539
581
|
linksRef.current = adjustLinksForEdit(linksRef.current, editPos, delta);
|
|
540
582
|
formattingRef.current = adjustFormattingForEdit(formattingRef.current, editPos, delta);
|
|
541
|
-
|
|
542
|
-
|
|
583
|
+
// Push pre-edit state to undo stack
|
|
584
|
+
pushUndoSnapshot(prevText, prevFormatting, prevLinks, editPos, editPos);
|
|
543
585
|
prevTextRef.current = nextValue;
|
|
544
586
|
const markdown = buildFullMarkdown(nextValue, linksRef.current, formattingRef.current);
|
|
545
587
|
setPlainTextValue((prev) => {
|
|
@@ -547,9 +589,8 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
547
589
|
return prev === normalized ? prev : normalized;
|
|
548
590
|
});
|
|
549
591
|
(_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, markdown);
|
|
550
|
-
}, []);
|
|
592
|
+
}, [pushUndoSnapshot]);
|
|
551
593
|
useEffect(() => {
|
|
552
|
-
var _a;
|
|
553
594
|
const container = editorContainerRef.current;
|
|
554
595
|
if (!container) {
|
|
555
596
|
return;
|
|
@@ -558,6 +599,11 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
558
599
|
linksRef.current = links;
|
|
559
600
|
formattingRef.current = formatting;
|
|
560
601
|
prevTextRef.current = plainText;
|
|
602
|
+
// Push initial state as the baseline undo snapshot
|
|
603
|
+
undoStackRef.current = [
|
|
604
|
+
{ text: plainText, formatting: [...formatting], links: [...links], cursorStart: 0, cursorEnd: 0 },
|
|
605
|
+
];
|
|
606
|
+
redoStackRef.current = [];
|
|
561
607
|
const [instance] = OverType.init(container, {
|
|
562
608
|
value: plainText,
|
|
563
609
|
placeholder: resolvedPlaceholder,
|
|
@@ -575,9 +621,25 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
575
621
|
applyFormattingHighlights(this.preview, formattingRef.current, (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.value);
|
|
576
622
|
applyLinkHighlights(this.preview, linksRef.current);
|
|
577
623
|
};
|
|
578
|
-
//
|
|
579
|
-
|
|
580
|
-
|
|
624
|
+
// Force a full update through the monkey-patched pipeline
|
|
625
|
+
instance.updatePreview();
|
|
626
|
+
// Safety net: re-apply formatting if the preview gets reset externally
|
|
627
|
+
// (e.g. by the original updatePreview being called outside our monkey-patch)
|
|
628
|
+
let isApplyingFormatting = false;
|
|
629
|
+
const formattingObserver = new MutationObserver(() => {
|
|
630
|
+
var _a;
|
|
631
|
+
if (isApplyingFormatting)
|
|
632
|
+
return;
|
|
633
|
+
const hasFormatting = formattingRef.current.length > 0;
|
|
634
|
+
const hasAnyFormattingElement = instance.preview.querySelector("strong.step-preview-bold, em.step-preview-italic, code.step-preview-code") !== null;
|
|
635
|
+
if (hasFormatting && !hasAnyFormattingElement) {
|
|
636
|
+
isApplyingFormatting = true;
|
|
637
|
+
applyFormattingHighlights(instance.preview, formattingRef.current, (_a = instance.textarea) === null || _a === void 0 ? void 0 : _a.value);
|
|
638
|
+
applyLinkHighlights(instance.preview, linksRef.current);
|
|
639
|
+
isApplyingFormatting = false;
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
formattingObserver.observe(instance.preview, { childList: true, subtree: true });
|
|
581
643
|
// Create custom caret element inside the wrapper
|
|
582
644
|
const caretEl = document.createElement("div");
|
|
583
645
|
caretEl.className = "bn-step-custom-caret";
|
|
@@ -586,6 +648,7 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
586
648
|
editorInstanceRef.current = instance;
|
|
587
649
|
setTextareaNode(instance.textarea);
|
|
588
650
|
return () => {
|
|
651
|
+
formattingObserver.disconnect();
|
|
589
652
|
caretRef.current = null;
|
|
590
653
|
instance.destroy();
|
|
591
654
|
editorInstanceRef.current = null;
|
|
@@ -709,6 +772,21 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
709
772
|
delete textareaNode.dataset.stepField;
|
|
710
773
|
}
|
|
711
774
|
}, [fieldName, textareaNode]);
|
|
775
|
+
// Block native undo/redo at the beforeinput level so the browser never
|
|
776
|
+
// applies its own history on the textarea — our custom stack handles it.
|
|
777
|
+
useEffect(() => {
|
|
778
|
+
if (!textareaNode)
|
|
779
|
+
return;
|
|
780
|
+
const blockNativeUndoRedo = (e) => {
|
|
781
|
+
if (e.inputType === "historyUndo" || e.inputType === "historyRedo") {
|
|
782
|
+
e.preventDefault();
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
textareaNode.addEventListener("beforeinput", blockNativeUndoRedo);
|
|
786
|
+
return () => {
|
|
787
|
+
textareaNode.removeEventListener("beforeinput", blockNativeUndoRedo);
|
|
788
|
+
};
|
|
789
|
+
}, [textareaNode]);
|
|
712
790
|
useEffect(() => {
|
|
713
791
|
if (!textareaNode) {
|
|
714
792
|
return;
|
|
@@ -735,6 +813,16 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
735
813
|
const handleBlur = () => {
|
|
736
814
|
setIsFocused(false);
|
|
737
815
|
setShowAllSuggestions(false);
|
|
816
|
+
// Re-apply formatting highlights after blur because OverType may
|
|
817
|
+
// re-render the preview (via debounced selectionchange) and strip them.
|
|
818
|
+
const instance = editorInstanceRef.current;
|
|
819
|
+
if (instance) {
|
|
820
|
+
requestAnimationFrame(() => {
|
|
821
|
+
var _a;
|
|
822
|
+
applyFormattingHighlights(instance.preview, formattingRef.current, (_a = instance.textarea) === null || _a === void 0 ? void 0 : _a.value);
|
|
823
|
+
applyLinkHighlights(instance.preview, linksRef.current);
|
|
824
|
+
});
|
|
825
|
+
}
|
|
738
826
|
};
|
|
739
827
|
textareaNode.addEventListener("focus", handleFocus);
|
|
740
828
|
textareaNode.addEventListener("blur", handleBlur);
|
|
@@ -870,24 +958,22 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
870
958
|
// Check if selection is already formatted
|
|
871
959
|
const existingIdx = formattingRef.current.findIndex((f) => f.type === fmtType && f.start <= start && f.end >= end);
|
|
872
960
|
// Save current state for undo before modifying
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
{ formatting: [...formattingRef.current], links: [...linksRef.current] },
|
|
876
|
-
];
|
|
877
|
-
formattingRedoRef.current = [];
|
|
961
|
+
const currentText = instance.getValue();
|
|
962
|
+
pushUndoSnapshot(currentText, formattingRef.current, linksRef.current, start, end);
|
|
878
963
|
if (existingIdx !== -1) {
|
|
879
964
|
// Remove formatting
|
|
880
965
|
formattingRef.current = formattingRef.current.filter((_, i) => i !== existingIdx);
|
|
881
966
|
}
|
|
882
967
|
else if (start !== end) {
|
|
883
|
-
// Remove overlapping formatting
|
|
884
|
-
|
|
968
|
+
// Remove overlapping formatting:
|
|
969
|
+
// - Code: remove ALL overlapping formatting (code replaces bold/italic)
|
|
970
|
+
// - Bold/Italic: remove only overlapping formatting of the SAME type
|
|
971
|
+
formattingRef.current = formattingRef.current.filter((f) => f.start >= end || f.end <= start || (fmtType !== "code" && f.type !== fmtType));
|
|
885
972
|
// Add formatting for selection
|
|
886
973
|
formattingRef.current = [...formattingRef.current, { start, end, type: fmtType }];
|
|
887
974
|
}
|
|
888
975
|
else {
|
|
889
976
|
// No selection — nothing to format
|
|
890
|
-
formattingUndoRef.current = formattingUndoRef.current.slice(0, -1);
|
|
891
977
|
return;
|
|
892
978
|
}
|
|
893
979
|
const currentValue = instance.getValue();
|
|
@@ -898,7 +984,7 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
898
984
|
// Re-apply highlights
|
|
899
985
|
applyFormattingHighlights(instance.preview, formattingRef.current, textareaNode === null || textareaNode === void 0 ? void 0 : textareaNode.value);
|
|
900
986
|
applyLinkHighlights(instance.preview, linksRef.current);
|
|
901
|
-
}, [textareaNode]);
|
|
987
|
+
}, [textareaNode, pushUndoSnapshot]);
|
|
902
988
|
const updateActiveFormats = useCallback(() => {
|
|
903
989
|
var _a, _b;
|
|
904
990
|
if (!textareaNode)
|
|
@@ -973,6 +1059,8 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
973
1059
|
return;
|
|
974
1060
|
}
|
|
975
1061
|
const currentValue = instance.getValue();
|
|
1062
|
+
// Push undo snapshot before link edit
|
|
1063
|
+
pushUndoSnapshot(currentValue, formattingRef.current, linksRef.current, sel.start, sel.end);
|
|
976
1064
|
const linkText = text || sel.text || url;
|
|
977
1065
|
// Replace selected text with link display text (no markdown syntax in textarea)
|
|
978
1066
|
const before = currentValue.slice(0, sel.start);
|
|
@@ -995,20 +1083,26 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
995
1083
|
linkSelectionRef.current = null;
|
|
996
1084
|
setCursorLink(null);
|
|
997
1085
|
requestAnimationFrame(() => textareaNode === null || textareaNode === void 0 ? void 0 : textareaNode.focus());
|
|
998
|
-
}, [textareaNode]);
|
|
1086
|
+
}, [textareaNode, pushUndoSnapshot]);
|
|
999
1087
|
const handleRemoveLink = useCallback(() => {
|
|
1000
|
-
var _a, _b;
|
|
1088
|
+
var _a, _b, _c, _d;
|
|
1089
|
+
const instance = editorInstanceRef.current;
|
|
1090
|
+
// Push undo snapshot before link removal
|
|
1091
|
+
if (instance) {
|
|
1092
|
+
const currentText = instance.getValue();
|
|
1093
|
+
const cursorPos = (_b = (_a = instance.textarea) === null || _a === void 0 ? void 0 : _a.selectionStart) !== null && _b !== void 0 ? _b : 0;
|
|
1094
|
+
pushUndoSnapshot(currentText, formattingRef.current, linksRef.current, cursorPos, cursorPos);
|
|
1095
|
+
}
|
|
1001
1096
|
linksRef.current = linksRef.current.filter((l) => l !== cursorLink);
|
|
1002
1097
|
setCursorLink(null);
|
|
1003
|
-
const instance = editorInstanceRef.current;
|
|
1004
1098
|
if (instance) {
|
|
1005
1099
|
const markdown = buildFullMarkdown(instance.getValue(), linksRef.current, formattingRef.current);
|
|
1006
|
-
(
|
|
1100
|
+
(_c = onChangeRef.current) === null || _c === void 0 ? void 0 : _c.call(onChangeRef, markdown);
|
|
1007
1101
|
// Re-apply highlights since links changed
|
|
1008
|
-
applyFormattingHighlights(instance.preview, formattingRef.current, (
|
|
1102
|
+
applyFormattingHighlights(instance.preview, formattingRef.current, (_d = instance.textarea) === null || _d === void 0 ? void 0 : _d.value);
|
|
1009
1103
|
applyLinkHighlights(instance.preview, linksRef.current);
|
|
1010
1104
|
}
|
|
1011
|
-
}, [cursorLink]);
|
|
1105
|
+
}, [cursorLink, pushUndoSnapshot]);
|
|
1012
1106
|
const suggestionPool = useMemo(() => {
|
|
1013
1107
|
if (!suggestionFilter) {
|
|
1014
1108
|
return suggestions;
|
|
@@ -1062,6 +1156,11 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
1062
1156
|
}, [value]);
|
|
1063
1157
|
const handleRemoveImage = useCallback((image) => {
|
|
1064
1158
|
var _a;
|
|
1159
|
+
// Push undo snapshot before image removal
|
|
1160
|
+
if (editorInstanceRef.current) {
|
|
1161
|
+
const currentText = editorInstanceRef.current.getValue();
|
|
1162
|
+
pushUndoSnapshot(currentText, formattingRef.current, linksRef.current, image.start, image.end);
|
|
1163
|
+
}
|
|
1065
1164
|
const before = value.slice(0, image.start);
|
|
1066
1165
|
const after = value.slice(image.end);
|
|
1067
1166
|
const nextValue = `${before}${after}`.replace(/\n{3,}/g, "\n\n");
|
|
@@ -1071,7 +1170,7 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
1071
1170
|
(_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, nextValue);
|
|
1072
1171
|
setPlainTextValue(markdownToPlainText(nextValue));
|
|
1073
1172
|
setPreviewImageUrl((prev) => (prev === image.url ? null : prev));
|
|
1074
|
-
}, [value]);
|
|
1173
|
+
}, [value, pushUndoSnapshot]);
|
|
1075
1174
|
const handleImageClick = useCallback((url) => {
|
|
1076
1175
|
setPreviewImageUrl(url);
|
|
1077
1176
|
}, []);
|
|
@@ -1096,14 +1195,18 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
1096
1195
|
return true;
|
|
1097
1196
|
}, [textareaNode]);
|
|
1098
1197
|
const applySuggestion = useCallback((suggestion) => {
|
|
1099
|
-
var _a;
|
|
1198
|
+
var _a, _b;
|
|
1100
1199
|
const escaped = escapeMarkdownText(suggestion.title);
|
|
1101
1200
|
const instance = editorInstanceRef.current;
|
|
1102
1201
|
if (instance) {
|
|
1202
|
+
// Push undo snapshot before applying suggestion
|
|
1203
|
+
const currentText = instance.getValue();
|
|
1204
|
+
const cursorPos = (_a = textareaNode === null || textareaNode === void 0 ? void 0 : textareaNode.selectionStart) !== null && _a !== void 0 ? _a : 0;
|
|
1205
|
+
pushUndoSnapshot(currentText, formattingRef.current, linksRef.current, cursorPos, cursorPos);
|
|
1103
1206
|
instance.setValue(escaped);
|
|
1104
1207
|
}
|
|
1105
1208
|
setPlainTextValue(suggestion.title);
|
|
1106
|
-
(
|
|
1209
|
+
(_b = onChangeRef.current) === null || _b === void 0 ? void 0 : _b.call(onChangeRef, escaped);
|
|
1107
1210
|
onSuggestionSelect === null || onSuggestionSelect === void 0 ? void 0 : onSuggestionSelect(suggestion);
|
|
1108
1211
|
setActiveSuggestionIndex(0);
|
|
1109
1212
|
setShowAllSuggestions(false);
|
|
@@ -1114,11 +1217,11 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
1114
1217
|
textareaNode.selectionEnd = escaped.length;
|
|
1115
1218
|
}
|
|
1116
1219
|
});
|
|
1117
|
-
}, [onSuggestionSelect, textareaNode]);
|
|
1220
|
+
}, [onSuggestionSelect, textareaNode, pushUndoSnapshot]);
|
|
1118
1221
|
const keydownHandlerRef = useRef(null);
|
|
1119
1222
|
useEffect(() => {
|
|
1120
1223
|
keydownHandlerRef.current = (event) => {
|
|
1121
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1224
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
1122
1225
|
if (readOnly) {
|
|
1123
1226
|
const openKeys = enableAutocomplete && (event.metaKey || event.ctrlKey) && AUTOCOMPLETE_TRIGGER_KEYS.has(event.code);
|
|
1124
1227
|
if (!READ_ONLY_ALLOWED_KEYS.has(event.key) && !openKeys) {
|
|
@@ -1147,54 +1250,94 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
1147
1250
|
handleToolbarAction("toggleCode");
|
|
1148
1251
|
return;
|
|
1149
1252
|
}
|
|
1253
|
+
if (event.key === "a" || event.key === "A") {
|
|
1254
|
+
event.stopPropagation();
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1150
1257
|
if (event.key === "z" || event.key === "Z") {
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
];
|
|
1159
|
-
const prev = undoStack[undoStack.length - 1];
|
|
1160
|
-
formattingUndoRef.current = undoStack.slice(0, -1);
|
|
1161
|
-
formattingRef.current = prev.formatting;
|
|
1162
|
-
linksRef.current = prev.links;
|
|
1163
|
-
const instance = editorInstanceRef.current;
|
|
1164
|
-
if (instance) {
|
|
1165
|
-
const markdown = buildFullMarkdown(instance.getValue(), linksRef.current, formattingRef.current);
|
|
1166
|
-
(_b = onChangeRef.current) === null || _b === void 0 ? void 0 : _b.call(onChangeRef, markdown);
|
|
1167
|
-
setPlainTextValue(markdownToPlainText(markdown));
|
|
1168
|
-
applyFormattingHighlights(instance.preview, formattingRef.current, (_c = instance.textarea) === null || _c === void 0 ? void 0 : _c.value);
|
|
1169
|
-
applyLinkHighlights(instance.preview, linksRef.current);
|
|
1170
|
-
}
|
|
1258
|
+
event.preventDefault();
|
|
1259
|
+
event.stopImmediatePropagation();
|
|
1260
|
+
const stack = undoStackRef.current;
|
|
1261
|
+
if (stack.length === 0)
|
|
1262
|
+
return;
|
|
1263
|
+
const instance = editorInstanceRef.current;
|
|
1264
|
+
if (!instance)
|
|
1171
1265
|
return;
|
|
1266
|
+
// Push current state to redo stack
|
|
1267
|
+
const currentText = instance.getValue();
|
|
1268
|
+
redoStackRef.current = [
|
|
1269
|
+
...redoStackRef.current,
|
|
1270
|
+
{
|
|
1271
|
+
text: currentText,
|
|
1272
|
+
formatting: [...formattingRef.current],
|
|
1273
|
+
links: [...linksRef.current],
|
|
1274
|
+
cursorStart: (_b = textareaNode === null || textareaNode === void 0 ? void 0 : textareaNode.selectionStart) !== null && _b !== void 0 ? _b : 0,
|
|
1275
|
+
cursorEnd: (_c = textareaNode === null || textareaNode === void 0 ? void 0 : textareaNode.selectionEnd) !== null && _c !== void 0 ? _c : 0,
|
|
1276
|
+
},
|
|
1277
|
+
];
|
|
1278
|
+
// Pop from undo stack
|
|
1279
|
+
const prev = stack[stack.length - 1];
|
|
1280
|
+
undoStackRef.current = stack.slice(0, -1);
|
|
1281
|
+
// Restore state
|
|
1282
|
+
formattingRef.current = prev.formatting;
|
|
1283
|
+
linksRef.current = prev.links;
|
|
1284
|
+
prevTextRef.current = prev.text;
|
|
1285
|
+
isSyncingRef.current = true;
|
|
1286
|
+
instance.setValue(prev.text);
|
|
1287
|
+
isSyncingRef.current = false;
|
|
1288
|
+
if (textareaNode) {
|
|
1289
|
+
textareaNode.selectionStart = prev.cursorStart;
|
|
1290
|
+
textareaNode.selectionEnd = prev.cursorEnd;
|
|
1172
1291
|
}
|
|
1292
|
+
const markdown = buildFullMarkdown(prev.text, prev.links, prev.formatting);
|
|
1293
|
+
(_d = onChangeRef.current) === null || _d === void 0 ? void 0 : _d.call(onChangeRef, markdown);
|
|
1294
|
+
setPlainTextValue(markdownToPlainText(markdown));
|
|
1295
|
+
applyFormattingHighlights(instance.preview, formattingRef.current, (_e = instance.textarea) === null || _e === void 0 ? void 0 : _e.value);
|
|
1296
|
+
applyLinkHighlights(instance.preview, linksRef.current);
|
|
1297
|
+
return;
|
|
1173
1298
|
}
|
|
1174
1299
|
}
|
|
1175
1300
|
if (modKey && event.shiftKey && (event.key === "z" || event.key === "Z")) {
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
];
|
|
1184
|
-
const next = redoStack[redoStack.length - 1];
|
|
1185
|
-
formattingRedoRef.current = redoStack.slice(0, -1);
|
|
1186
|
-
formattingRef.current = next.formatting;
|
|
1187
|
-
linksRef.current = next.links;
|
|
1188
|
-
const instance = editorInstanceRef.current;
|
|
1189
|
-
if (instance) {
|
|
1190
|
-
const markdown = buildFullMarkdown(instance.getValue(), linksRef.current, formattingRef.current);
|
|
1191
|
-
(_d = onChangeRef.current) === null || _d === void 0 ? void 0 : _d.call(onChangeRef, markdown);
|
|
1192
|
-
setPlainTextValue(markdownToPlainText(markdown));
|
|
1193
|
-
applyFormattingHighlights(instance.preview, formattingRef.current, (_e = instance.textarea) === null || _e === void 0 ? void 0 : _e.value);
|
|
1194
|
-
applyLinkHighlights(instance.preview, linksRef.current);
|
|
1195
|
-
}
|
|
1301
|
+
event.preventDefault();
|
|
1302
|
+
event.stopImmediatePropagation();
|
|
1303
|
+
const stack = redoStackRef.current;
|
|
1304
|
+
if (stack.length === 0)
|
|
1305
|
+
return;
|
|
1306
|
+
const instance = editorInstanceRef.current;
|
|
1307
|
+
if (!instance)
|
|
1196
1308
|
return;
|
|
1309
|
+
// Push current state to undo stack
|
|
1310
|
+
const currentText = instance.getValue();
|
|
1311
|
+
undoStackRef.current = [
|
|
1312
|
+
...undoStackRef.current,
|
|
1313
|
+
{
|
|
1314
|
+
text: currentText,
|
|
1315
|
+
formatting: [...formattingRef.current],
|
|
1316
|
+
links: [...linksRef.current],
|
|
1317
|
+
cursorStart: (_f = textareaNode === null || textareaNode === void 0 ? void 0 : textareaNode.selectionStart) !== null && _f !== void 0 ? _f : 0,
|
|
1318
|
+
cursorEnd: (_g = textareaNode === null || textareaNode === void 0 ? void 0 : textareaNode.selectionEnd) !== null && _g !== void 0 ? _g : 0,
|
|
1319
|
+
},
|
|
1320
|
+
];
|
|
1321
|
+
// Pop from redo stack
|
|
1322
|
+
const next = stack[stack.length - 1];
|
|
1323
|
+
redoStackRef.current = stack.slice(0, -1);
|
|
1324
|
+
// Restore state
|
|
1325
|
+
formattingRef.current = next.formatting;
|
|
1326
|
+
linksRef.current = next.links;
|
|
1327
|
+
prevTextRef.current = next.text;
|
|
1328
|
+
isSyncingRef.current = true;
|
|
1329
|
+
instance.setValue(next.text);
|
|
1330
|
+
isSyncingRef.current = false;
|
|
1331
|
+
if (textareaNode) {
|
|
1332
|
+
textareaNode.selectionStart = next.cursorStart;
|
|
1333
|
+
textareaNode.selectionEnd = next.cursorEnd;
|
|
1197
1334
|
}
|
|
1335
|
+
const markdown = buildFullMarkdown(next.text, next.links, next.formatting);
|
|
1336
|
+
(_h = onChangeRef.current) === null || _h === void 0 ? void 0 : _h.call(onChangeRef, markdown);
|
|
1337
|
+
setPlainTextValue(markdownToPlainText(markdown));
|
|
1338
|
+
applyFormattingHighlights(instance.preview, next.formatting, (_j = instance.textarea) === null || _j === void 0 ? void 0 : _j.value);
|
|
1339
|
+
applyLinkHighlights(instance.preview, next.links);
|
|
1340
|
+
return;
|
|
1198
1341
|
}
|
|
1199
1342
|
if (enableAutocomplete && shouldShowAutocomplete) {
|
|
1200
1343
|
if (event.key === "ArrowDown") {
|
|
@@ -1209,7 +1352,7 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
|
|
|
1209
1352
|
}
|
|
1210
1353
|
if (event.key === "Enter" || event.key === "Tab") {
|
|
1211
1354
|
event.preventDefault();
|
|
1212
|
-
const suggestion = (
|
|
1355
|
+
const suggestion = (_k = filteredSuggestions[activeSuggestionIndex]) !== null && _k !== void 0 ? _k : filteredSuggestions[0];
|
|
1213
1356
|
if (suggestion) {
|
|
1214
1357
|
applySuggestion(suggestion);
|
|
1215
1358
|
}
|