testomatio-editor-blocks 0.4.25 → 0.4.27
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 +6 -0
- package/package/editor/blocks/step.js +10 -2
- package/package/editor/createMarkdownPasteHandler.d.ts +12 -0
- package/package/editor/createMarkdownPasteHandler.js +36 -0
- package/package/editor/customMarkdownConverter.js +3 -3
- package/package/index.d.ts +2 -1
- package/package/index.js +2 -1
- package/package/styles.css +2 -2
- package/package.json +1 -1
- package/src/App.tsx +2 -40
- package/src/editor/blocks/step.tsx +11 -2
- package/src/editor/createMarkdownPasteHandler.ts +49 -0
- package/src/editor/customMarkdownConverter.test.ts +34 -0
- package/src/editor/customMarkdownConverter.ts +3 -3
- package/src/editor/styles.css +2 -2
- package/src/index.ts +3 -1
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns true when a normalised (lowercased, trailing-punctuation-stripped)
|
|
3
|
+
* heading text looks like a "Steps" heading.
|
|
4
|
+
* Accepted forms: steps, step, step(s).
|
|
5
|
+
*/
|
|
6
|
+
export declare function isStepsHeading(text: string): boolean;
|
|
1
7
|
export declare const isEmptyParagraph: (b: any) => boolean;
|
|
2
8
|
/**
|
|
3
9
|
* Check whether a step or snippet can be inserted at / after the given block.
|
|
@@ -43,6 +43,14 @@ const writeStepViewMode = (mode) => {
|
|
|
43
43
|
//
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* Returns true when a normalised (lowercased, trailing-punctuation-stripped)
|
|
48
|
+
* heading text looks like a "Steps" heading.
|
|
49
|
+
* Accepted forms: steps, step, step(s).
|
|
50
|
+
*/
|
|
51
|
+
export function isStepsHeading(text) {
|
|
52
|
+
return /^step(s|\(s\))?$/.test(text);
|
|
53
|
+
}
|
|
46
54
|
export const isEmptyParagraph = (b) => b.type === "paragraph" &&
|
|
47
55
|
(!Array.isArray(b.content) ||
|
|
48
56
|
b.content.length === 0 ||
|
|
@@ -71,7 +79,7 @@ export function canInsertStepOrSnippet(editor, referenceBlockId) {
|
|
|
71
79
|
.trim()
|
|
72
80
|
.toLowerCase()
|
|
73
81
|
.replace(/[:\-–—]$/, "");
|
|
74
|
-
return text
|
|
82
|
+
return isStepsHeading(text);
|
|
75
83
|
}
|
|
76
84
|
return false;
|
|
77
85
|
}
|
|
@@ -148,7 +156,7 @@ export const stepBlock = createReactBlockSpec({
|
|
|
148
156
|
.trim()
|
|
149
157
|
.toLowerCase()
|
|
150
158
|
.replace(/[:\-–—]$/, "");
|
|
151
|
-
return text
|
|
159
|
+
return isStepsHeading(text);
|
|
152
160
|
}
|
|
153
161
|
return false;
|
|
154
162
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { BlockNoteEditor } from "@blocknote/core";
|
|
2
|
+
import type { CustomPartialBlock } from "./customMarkdownConverter";
|
|
3
|
+
type PasteHandlerContext = {
|
|
4
|
+
event: ClipboardEvent;
|
|
5
|
+
editor: BlockNoteEditor<any, any, any>;
|
|
6
|
+
defaultPasteHandler: (context?: {
|
|
7
|
+
prioritizeMarkdownOverHTML?: boolean;
|
|
8
|
+
plainTextAsMarkdown?: boolean;
|
|
9
|
+
}) => boolean | undefined;
|
|
10
|
+
};
|
|
11
|
+
export declare function createMarkdownPasteHandler(converter: (markdown: string) => CustomPartialBlock[]): ({ event, editor, defaultPasteHandler }: PasteHandlerContext) => boolean | undefined;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function createMarkdownPasteHandler(converter) {
|
|
2
|
+
return ({ event, editor, defaultPasteHandler }) => {
|
|
3
|
+
var _a, _b, _c, _d;
|
|
4
|
+
const plainText = (_b = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData("text/plain")) !== null && _b !== void 0 ? _b : "";
|
|
5
|
+
if (!plainText.trim())
|
|
6
|
+
return defaultPasteHandler();
|
|
7
|
+
try {
|
|
8
|
+
const parsedBlocks = converter(plainText);
|
|
9
|
+
if (parsedBlocks.length === 0)
|
|
10
|
+
return defaultPasteHandler();
|
|
11
|
+
const selection = editor.getSelection();
|
|
12
|
+
const selectedIds = (_d = (_c = selection === null || selection === void 0 ? void 0 : selection.blocks) === null || _c === void 0 ? void 0 : _c.map((block) => block.id).filter((id) => Boolean(id))) !== null && _d !== void 0 ? _d : [];
|
|
13
|
+
if (selectedIds.length > 0) {
|
|
14
|
+
editor.replaceBlocks(selectedIds, parsedBlocks);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
const cursorBlock = editor.getTextCursorPosition().block;
|
|
18
|
+
if (cursorBlock) {
|
|
19
|
+
editor.replaceBlocks([cursorBlock.id], parsedBlocks);
|
|
20
|
+
}
|
|
21
|
+
else if (editor.document.length > 0) {
|
|
22
|
+
const reference = editor.document[editor.document.length - 1];
|
|
23
|
+
editor.insertBlocks(parsedBlocks, reference.id, "after");
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
return defaultPasteHandler();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
editor.focus();
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return defaultPasteHandler();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isLinkInlineContent, isStyledTextInlineContent, } from "@blocknote/core";
|
|
2
|
+
import { isStepsHeading } from "./blocks/step";
|
|
2
3
|
const BASE_BLOCK_PROPS = {
|
|
3
4
|
textAlignment: "left",
|
|
4
5
|
textColor: "default",
|
|
@@ -463,9 +464,8 @@ function serializeBlocks(blocks, ctx) {
|
|
|
463
464
|
export function blocksToMarkdown(blocks) {
|
|
464
465
|
const lines = serializeBlocks(blocks, { listDepth: 0, insideQuote: false });
|
|
465
466
|
const cleaned = lines
|
|
466
|
-
// Collapse excessive blank lines but preserve one extra for empty paragraphs.
|
|
467
467
|
.join("\n")
|
|
468
|
-
.replace(/\n{
|
|
468
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
469
469
|
.trimEnd();
|
|
470
470
|
return cleaned;
|
|
471
471
|
}
|
|
@@ -1183,7 +1183,7 @@ export function markdownToBlocks(markdown) {
|
|
|
1183
1183
|
const headingLevel = (_b = (_a = headingBlock.props) === null || _a === void 0 ? void 0 : _a.level) !== null && _b !== void 0 ? _b : 3;
|
|
1184
1184
|
const headingText = inlineContentToPlainText(headingBlock.content);
|
|
1185
1185
|
const normalizedHeading = headingText.trim().toLowerCase();
|
|
1186
|
-
if (normalizedHeading.replace(/[:\-–—]$/, "")
|
|
1186
|
+
if (isStepsHeading(normalizedHeading.replace(/[:\-–—]$/, ""))) {
|
|
1187
1187
|
stepsHeadingLevel = headingLevel;
|
|
1188
1188
|
}
|
|
1189
1189
|
else if (stepsHeadingLevel !== null &&
|
package/package/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { customSchema, type CustomSchema, type CustomBlock, type CustomEditor, } from "./editor/customSchema";
|
|
2
|
-
export { stepBlock, canInsertStepOrSnippet } from "./editor/blocks/step";
|
|
2
|
+
export { stepBlock, canInsertStepOrSnippet, isStepsHeading } from "./editor/blocks/step";
|
|
3
3
|
export { snippetBlock } from "./editor/blocks/snippet";
|
|
4
4
|
export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";
|
|
5
5
|
export { blocksToMarkdown, markdownToBlocks, type CustomEditorBlock, type CustomPartialBlock, } from "./editor/customMarkdownConverter";
|
|
6
6
|
export { useStepAutocomplete, parseStepsFromJsonApi, setStepsFetcher, type StepSuggestion, type StepJsonApiDocument, type StepJsonApiResource, } from "./editor/stepAutocomplete";
|
|
7
7
|
export { useStepImageUpload, setImageUploadHandler, type StepImageUploadHandler, } from "./editor/stepImageUpload";
|
|
8
|
+
export { createMarkdownPasteHandler } from "./editor/createMarkdownPasteHandler";
|
|
8
9
|
export declare const testomatioEditorClassName = "markdown testomatio-editor";
|
package/package/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { customSchema, } from "./editor/customSchema";
|
|
2
|
-
export { stepBlock, canInsertStepOrSnippet } from "./editor/blocks/step";
|
|
2
|
+
export { stepBlock, canInsertStepOrSnippet, isStepsHeading } from "./editor/blocks/step";
|
|
3
3
|
export { snippetBlock } from "./editor/blocks/snippet";
|
|
4
4
|
export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";
|
|
5
5
|
export { blocksToMarkdown, markdownToBlocks, } from "./editor/customMarkdownConverter";
|
|
6
6
|
export { useStepAutocomplete, parseStepsFromJsonApi, setStepsFetcher, } from "./editor/stepAutocomplete";
|
|
7
7
|
export { useStepImageUpload, setImageUploadHandler, } from "./editor/stepImageUpload";
|
|
8
|
+
export { createMarkdownPasteHandler } from "./editor/createMarkdownPasteHandler";
|
|
8
9
|
export const testomatioEditorClassName = "markdown testomatio-editor";
|
package/package/styles.css
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
--color-accent-500: #10b981;
|
|
26
26
|
|
|
27
27
|
/* Selection */
|
|
28
|
-
--color-selection:
|
|
28
|
+
--color-selection: rgba(0, 120, 215, 0.3);
|
|
29
29
|
|
|
30
30
|
/* Semantic tokens - these reference the palette above */
|
|
31
31
|
--text-primary: #262626;
|
|
@@ -1162,7 +1162,7 @@ html.dark .bn-step-image-preview__content {
|
|
|
1162
1162
|
}
|
|
1163
1163
|
|
|
1164
1164
|
.bn-step-field:focus-within .overtype-wrapper .overtype-input::selection {
|
|
1165
|
-
background-color:
|
|
1165
|
+
background-color: var(--color-selection) !important;
|
|
1166
1166
|
}
|
|
1167
1167
|
|
|
1168
1168
|
/* Hide OverType's built-in link tooltip — we use our own */
|
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
type CustomEditorBlock,
|
|
18
18
|
type CustomPartialBlock,
|
|
19
19
|
} from "./editor/customMarkdownConverter";
|
|
20
|
+
import { createMarkdownPasteHandler } from "./editor/createMarkdownPasteHandler";
|
|
20
21
|
import { customSchema, type CustomEditor } from "./editor/customSchema";
|
|
21
22
|
import { setStepsFetcher, type StepJsonApiDocument } from "./editor/stepAutocomplete";
|
|
22
23
|
import { setSnippetFetcher, type SnippetJsonApiDocument } from "./editor/snippetAutocomplete";
|
|
@@ -347,46 +348,7 @@ function CustomSlashMenu() {
|
|
|
347
348
|
function App() {
|
|
348
349
|
const editor = useCreateBlockNote({
|
|
349
350
|
schema: customSchema,
|
|
350
|
-
pasteHandler: (
|
|
351
|
-
const plainText = event.clipboardData?.getData("text/plain") ?? "";
|
|
352
|
-
|
|
353
|
-
if (!plainText.trim()) {
|
|
354
|
-
return defaultPasteHandler();
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
try {
|
|
358
|
-
const parsedBlocks = markdownToBlocks(plainText);
|
|
359
|
-
|
|
360
|
-
if (parsedBlocks.length === 0) {
|
|
361
|
-
return defaultPasteHandler();
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const selection = editor.getSelection();
|
|
365
|
-
const selectedIds = selection?.blocks
|
|
366
|
-
?.map((block) => block.id)
|
|
367
|
-
.filter((id): id is string => Boolean(id)) ?? [];
|
|
368
|
-
|
|
369
|
-
if (selectedIds.length > 0) {
|
|
370
|
-
editor.replaceBlocks(selectedIds, parsedBlocks);
|
|
371
|
-
} else {
|
|
372
|
-
const cursorBlock = editor.getTextCursorPosition().block;
|
|
373
|
-
if (cursorBlock) {
|
|
374
|
-
editor.replaceBlocks([cursorBlock.id], parsedBlocks);
|
|
375
|
-
} else if (editor.document.length > 0) {
|
|
376
|
-
const reference = editor.document[editor.document.length - 1];
|
|
377
|
-
editor.insertBlocks(parsedBlocks, reference.id, "after");
|
|
378
|
-
} else {
|
|
379
|
-
return defaultPasteHandler();
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
editor.focus();
|
|
384
|
-
return true;
|
|
385
|
-
} catch (error) {
|
|
386
|
-
console.error("Failed to paste custom markdown", error);
|
|
387
|
-
return defaultPasteHandler();
|
|
388
|
-
}
|
|
389
|
-
},
|
|
351
|
+
pasteHandler: createMarkdownPasteHandler(markdownToBlocks),
|
|
390
352
|
});
|
|
391
353
|
const [markdown, setMarkdown] = useState("");
|
|
392
354
|
const [conversionError, setConversionError] = useState<string | null>(null);
|
|
@@ -47,6 +47,15 @@ const writeStepViewMode = (mode: StepViewMode) => {
|
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Returns true when a normalised (lowercased, trailing-punctuation-stripped)
|
|
52
|
+
* heading text looks like a "Steps" heading.
|
|
53
|
+
* Accepted forms: steps, step, step(s).
|
|
54
|
+
*/
|
|
55
|
+
export function isStepsHeading(text: string): boolean {
|
|
56
|
+
return /^step(s|\(s\))?$/.test(text);
|
|
57
|
+
}
|
|
58
|
+
|
|
50
59
|
export const isEmptyParagraph = (b: any): boolean =>
|
|
51
60
|
b.type === "paragraph" &&
|
|
52
61
|
(!Array.isArray(b.content) ||
|
|
@@ -80,7 +89,7 @@ export function canInsertStepOrSnippet(
|
|
|
80
89
|
.trim()
|
|
81
90
|
.toLowerCase()
|
|
82
91
|
.replace(/[:\-–—]$/, "");
|
|
83
|
-
return text
|
|
92
|
+
return isStepsHeading(text);
|
|
84
93
|
}
|
|
85
94
|
return false;
|
|
86
95
|
}
|
|
@@ -162,7 +171,7 @@ export const stepBlock = createReactBlockSpec(
|
|
|
162
171
|
.trim()
|
|
163
172
|
.toLowerCase()
|
|
164
173
|
.replace(/[:\-–—]$/, "");
|
|
165
|
-
return text
|
|
174
|
+
return isStepsHeading(text);
|
|
166
175
|
}
|
|
167
176
|
return false;
|
|
168
177
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { BlockNoteEditor } from "@blocknote/core";
|
|
2
|
+
import type { CustomPartialBlock } from "./customMarkdownConverter";
|
|
3
|
+
|
|
4
|
+
type PasteHandlerContext = {
|
|
5
|
+
event: ClipboardEvent;
|
|
6
|
+
editor: BlockNoteEditor<any, any, any>;
|
|
7
|
+
defaultPasteHandler: (context?: {
|
|
8
|
+
prioritizeMarkdownOverHTML?: boolean;
|
|
9
|
+
plainTextAsMarkdown?: boolean;
|
|
10
|
+
}) => boolean | undefined;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function createMarkdownPasteHandler(
|
|
14
|
+
converter: (markdown: string) => CustomPartialBlock[],
|
|
15
|
+
) {
|
|
16
|
+
return ({ event, editor, defaultPasteHandler }: PasteHandlerContext): boolean | undefined => {
|
|
17
|
+
const plainText = event.clipboardData?.getData("text/plain") ?? "";
|
|
18
|
+
if (!plainText.trim()) return defaultPasteHandler();
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const parsedBlocks = converter(plainText);
|
|
22
|
+
if (parsedBlocks.length === 0) return defaultPasteHandler();
|
|
23
|
+
|
|
24
|
+
const selection = editor.getSelection();
|
|
25
|
+
const selectedIds = selection?.blocks
|
|
26
|
+
?.map((block: any) => block.id)
|
|
27
|
+
.filter((id: unknown): id is string => Boolean(id)) ?? [];
|
|
28
|
+
|
|
29
|
+
if (selectedIds.length > 0) {
|
|
30
|
+
editor.replaceBlocks(selectedIds, parsedBlocks);
|
|
31
|
+
} else {
|
|
32
|
+
const cursorBlock = editor.getTextCursorPosition().block;
|
|
33
|
+
if (cursorBlock) {
|
|
34
|
+
editor.replaceBlocks([cursorBlock.id], parsedBlocks);
|
|
35
|
+
} else if (editor.document.length > 0) {
|
|
36
|
+
const reference = editor.document[editor.document.length - 1];
|
|
37
|
+
editor.insertBlocks(parsedBlocks, reference.id, "after");
|
|
38
|
+
} else {
|
|
39
|
+
return defaultPasteHandler();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
editor.focus();
|
|
44
|
+
return true;
|
|
45
|
+
} catch {
|
|
46
|
+
return defaultPasteHandler();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -863,6 +863,40 @@ describe("markdownToBlocks", () => {
|
|
|
863
863
|
]);
|
|
864
864
|
});
|
|
865
865
|
|
|
866
|
+
it("parses steps under a 'Step' heading (singular)", () => {
|
|
867
|
+
const markdown = ["## Step", "", "* Open the app", "* Click login"].join("\n");
|
|
868
|
+
const blocks = markdownToBlocks(markdown);
|
|
869
|
+
const stepBlocks = blocks.filter((block) => block.type === "testStep");
|
|
870
|
+
expect(stepBlocks).toHaveLength(2);
|
|
871
|
+
expect(stepBlocks[0].props).toMatchObject({ stepTitle: "Open the app" });
|
|
872
|
+
expect(stepBlocks[1].props).toMatchObject({ stepTitle: "Click login" });
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
it("parses steps under a 'Step(s)' heading", () => {
|
|
876
|
+
const markdown = ["# Step(s)", "", "1. First step", "2. Second step"].join("\n");
|
|
877
|
+
const blocks = markdownToBlocks(markdown);
|
|
878
|
+
const stepBlocks = blocks.filter((block) => block.type === "testStep");
|
|
879
|
+
expect(stepBlocks).toHaveLength(2);
|
|
880
|
+
expect(stepBlocks[0].props).toMatchObject({ stepTitle: "First step", listStyle: "ordered" });
|
|
881
|
+
expect(stepBlocks[1].props).toMatchObject({ stepTitle: "Second step", listStyle: "ordered" });
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
it("parses steps under an h4 'step' heading (lowercase)", () => {
|
|
885
|
+
const markdown = ["#### step", "", "* Do something"].join("\n");
|
|
886
|
+
const blocks = markdownToBlocks(markdown);
|
|
887
|
+
const stepBlocks = blocks.filter((block) => block.type === "testStep");
|
|
888
|
+
expect(stepBlocks).toHaveLength(1);
|
|
889
|
+
expect(stepBlocks[0].props).toMatchObject({ stepTitle: "Do something" });
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
it("parses steps under a 'Step:' heading with trailing colon", () => {
|
|
893
|
+
const markdown = ["### Step:", "", "* Verify output"].join("\n");
|
|
894
|
+
const blocks = markdownToBlocks(markdown);
|
|
895
|
+
const stepBlocks = blocks.filter((block) => block.type === "testStep");
|
|
896
|
+
expect(stepBlocks).toHaveLength(1);
|
|
897
|
+
expect(stepBlocks[0].props).toMatchObject({ stepTitle: "Verify output" });
|
|
898
|
+
});
|
|
899
|
+
|
|
866
900
|
it("handles multiple steps with expected results without extra asterisks", () => {
|
|
867
901
|
const markdown = [
|
|
868
902
|
"### Preconditions",
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
Styles,
|
|
10
10
|
} from "@blocknote/core";
|
|
11
11
|
import type { customSchema } from "./customSchema";
|
|
12
|
+
import { isStepsHeading } from "./blocks/step";
|
|
12
13
|
|
|
13
14
|
// Types derived from the custom schema so the converter stays type-safe when the schema evolves.
|
|
14
15
|
type Schema = typeof customSchema;
|
|
@@ -577,9 +578,8 @@ function serializeBlocks(blocks: CustomEditorBlock[], ctx: MarkdownContext): str
|
|
|
577
578
|
export function blocksToMarkdown(blocks: CustomEditorBlock[]): string {
|
|
578
579
|
const lines = serializeBlocks(blocks, { listDepth: 0, insideQuote: false });
|
|
579
580
|
const cleaned = lines
|
|
580
|
-
// Collapse excessive blank lines but preserve one extra for empty paragraphs.
|
|
581
581
|
.join("\n")
|
|
582
|
-
.replace(/\n{
|
|
582
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
583
583
|
.trimEnd();
|
|
584
584
|
|
|
585
585
|
return cleaned;
|
|
@@ -1413,7 +1413,7 @@ export function markdownToBlocks(markdown: string): CustomPartialBlock[] {
|
|
|
1413
1413
|
const headingText = inlineContentToPlainText(headingBlock.content as any);
|
|
1414
1414
|
const normalizedHeading = headingText.trim().toLowerCase();
|
|
1415
1415
|
|
|
1416
|
-
if (normalizedHeading.replace(/[:\-–—]$/, "")
|
|
1416
|
+
if (isStepsHeading(normalizedHeading.replace(/[:\-–—]$/, ""))) {
|
|
1417
1417
|
stepsHeadingLevel = headingLevel;
|
|
1418
1418
|
} else if (
|
|
1419
1419
|
stepsHeadingLevel !== null &&
|
package/src/editor/styles.css
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
--color-accent-500: #10b981;
|
|
26
26
|
|
|
27
27
|
/* Selection */
|
|
28
|
-
--color-selection:
|
|
28
|
+
--color-selection: rgba(0, 120, 215, 0.3);
|
|
29
29
|
|
|
30
30
|
/* Semantic tokens - these reference the palette above */
|
|
31
31
|
--text-primary: #262626;
|
|
@@ -1162,7 +1162,7 @@ html.dark .bn-step-image-preview__content {
|
|
|
1162
1162
|
}
|
|
1163
1163
|
|
|
1164
1164
|
.bn-step-field:focus-within .overtype-wrapper .overtype-input::selection {
|
|
1165
|
-
background-color:
|
|
1165
|
+
background-color: var(--color-selection) !important;
|
|
1166
1166
|
}
|
|
1167
1167
|
|
|
1168
1168
|
/* Hide OverType's built-in link tooltip — we use our own */
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ export {
|
|
|
4
4
|
type CustomBlock,
|
|
5
5
|
type CustomEditor,
|
|
6
6
|
} from "./editor/customSchema";
|
|
7
|
-
export { stepBlock, canInsertStepOrSnippet } from "./editor/blocks/step";
|
|
7
|
+
export { stepBlock, canInsertStepOrSnippet, isStepsHeading } from "./editor/blocks/step";
|
|
8
8
|
export { snippetBlock } from "./editor/blocks/snippet";
|
|
9
9
|
export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";
|
|
10
10
|
|
|
@@ -30,4 +30,6 @@ export {
|
|
|
30
30
|
type StepImageUploadHandler,
|
|
31
31
|
} from "./editor/stepImageUpload";
|
|
32
32
|
|
|
33
|
+
export { createMarkdownPasteHandler } from "./editor/createMarkdownPasteHandler";
|
|
34
|
+
|
|
33
35
|
export const testomatioEditorClassName = "markdown testomatio-editor";
|