testomatio-editor-blocks 0.4.27 → 0.4.28

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.
@@ -14,6 +14,16 @@ export declare const isEmptyParagraph: (b: any) => boolean;
14
14
  export declare function canInsertStepOrSnippet(editor: {
15
15
  document: any[];
16
16
  }, referenceBlockId: string): boolean;
17
+ /**
18
+ * Programmatically add an empty step block to the editor.
19
+ * - If a "Steps" heading exists, inserts after the last step/snippet under it.
20
+ * - Otherwise, appends a "Steps" heading + empty step at the end.
21
+ * Returns the inserted step's block ID (for focusing), or null.
22
+ */
23
+ export declare function addStepsBlock(editor: {
24
+ document: any[];
25
+ insertBlocks: (blocks: any[], referenceId: string, placement: string) => any[];
26
+ }): string | null;
17
27
  export declare const stepBlock: {
18
28
  config: {
19
29
  readonly type: "testStep";
@@ -85,6 +85,60 @@ export function canInsertStepOrSnippet(editor, referenceBlockId) {
85
85
  }
86
86
  return false;
87
87
  }
88
+ /**
89
+ * Programmatically add an empty step block to the editor.
90
+ * - If a "Steps" heading exists, inserts after the last step/snippet under it.
91
+ * - Otherwise, appends a "Steps" heading + empty step at the end.
92
+ * Returns the inserted step's block ID (for focusing), or null.
93
+ */
94
+ export function addStepsBlock(editor) {
95
+ var _a, _b, _c, _d;
96
+ const allBlocks = editor.document;
97
+ const emptyStep = {
98
+ type: "testStep",
99
+ props: { stepTitle: "", stepData: "", expectedResult: "" },
100
+ children: [],
101
+ };
102
+ let stepsHeadingIndex = -1;
103
+ for (let i = 0; i < allBlocks.length; i++) {
104
+ const b = allBlocks[i];
105
+ if (b.type !== "heading")
106
+ continue;
107
+ const text = (Array.isArray(b.content) ? b.content : [])
108
+ .filter((n) => n.type === "text")
109
+ .map((n) => { var _a; return (_a = n.text) !== null && _a !== void 0 ? _a : ""; })
110
+ .join("")
111
+ .trim()
112
+ .toLowerCase()
113
+ .replace(/[:\-–—]$/, "");
114
+ if (isStepsHeading(text)) {
115
+ stepsHeadingIndex = i;
116
+ break;
117
+ }
118
+ }
119
+ if (stepsHeadingIndex >= 0) {
120
+ let lastIndex = stepsHeadingIndex;
121
+ for (let i = stepsHeadingIndex + 1; i < allBlocks.length; i++) {
122
+ const b = allBlocks[i];
123
+ if (b.type === "testStep" || b.type === "snippet" || isEmptyParagraph(b)) {
124
+ lastIndex = i;
125
+ continue;
126
+ }
127
+ break;
128
+ }
129
+ const inserted = editor.insertBlocks([emptyStep], allBlocks[lastIndex].id, "after");
130
+ return (_b = (_a = inserted === null || inserted === void 0 ? void 0 : inserted[0]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null;
131
+ }
132
+ const lastBlock = allBlocks[allBlocks.length - 1];
133
+ const stepsHeading = {
134
+ type: "heading",
135
+ props: { level: 3 },
136
+ content: [{ type: "text", text: "Steps" }],
137
+ children: [],
138
+ };
139
+ const inserted = editor.insertBlocks([stepsHeading, emptyStep], lastBlock.id, "after");
140
+ return (_d = (_c = inserted === null || inserted === void 0 ? void 0 : inserted[1]) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : null;
141
+ }
88
142
  export const stepBlock = createReactBlockSpec({
89
143
  type: "testStep",
90
144
  content: "none",
@@ -1,7 +1,20 @@
1
1
  export function createMarkdownPasteHandler(converter) {
2
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 : "";
3
+ var _a, _b, _c, _d, _e, _f, _g, _h;
4
+ const types = (_b = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.types) !== null && _b !== void 0 ? _b : [];
5
+ if (types.includes("blocknote/html"))
6
+ return defaultPasteHandler();
7
+ if (types.includes("vscode-editor-data"))
8
+ return defaultPasteHandler();
9
+ if (types.includes("text/html")) {
10
+ const html = (_d = (_c = event.clipboardData) === null || _c === void 0 ? void 0 : _c.getData("text/html")) !== null && _d !== void 0 ? _d : "";
11
+ if (/<(pre|code)[\s>]/i.test(html))
12
+ return defaultPasteHandler();
13
+ }
14
+ const cursorBlock = editor.getTextCursorPosition().block;
15
+ if ((cursorBlock === null || cursorBlock === void 0 ? void 0 : cursorBlock.type) === "codeBlock" || (cursorBlock === null || cursorBlock === void 0 ? void 0 : cursorBlock.type) === "quote" || (cursorBlock === null || cursorBlock === void 0 ? void 0 : cursorBlock.type) === "table")
16
+ return defaultPasteHandler();
17
+ const plainText = (_f = (_e = event.clipboardData) === null || _e === void 0 ? void 0 : _e.getData("text/plain")) !== null && _f !== void 0 ? _f : "";
5
18
  if (!plainText.trim())
6
19
  return defaultPasteHandler();
7
20
  try {
@@ -9,7 +22,7 @@ export function createMarkdownPasteHandler(converter) {
9
22
  if (parsedBlocks.length === 0)
10
23
  return defaultPasteHandler();
11
24
  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 : [];
25
+ const selectedIds = (_h = (_g = selection === null || selection === void 0 ? void 0 : selection.blocks) === null || _g === void 0 ? void 0 : _g.map((block) => block.id).filter((id) => Boolean(id))) !== null && _h !== void 0 ? _h : [];
13
26
  if (selectedIds.length > 0) {
14
27
  editor.replaceBlocks(selectedIds, parsedBlocks);
15
28
  }
@@ -1,5 +1,5 @@
1
1
  export { customSchema, type CustomSchema, type CustomBlock, type CustomEditor, } from "./editor/customSchema";
2
- export { stepBlock, canInsertStepOrSnippet, isStepsHeading } from "./editor/blocks/step";
2
+ export { stepBlock, canInsertStepOrSnippet, isStepsHeading, addStepsBlock } 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";
package/package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { customSchema, } from "./editor/customSchema";
2
- export { stepBlock, canInsertStepOrSnippet, isStepsHeading } from "./editor/blocks/step";
2
+ export { stepBlock, canInsertStepOrSnippet, isStepsHeading, addStepsBlock } 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";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testomatio-editor-blocks",
3
- "version": "0.4.27",
3
+ "version": "0.4.28",
4
4
  "description": "Custom BlockNote schema, markdown conversion helpers, and UI for Testomatio-style test cases and steps.",
5
5
  "type": "module",
6
6
  "main": "./package/index.js",
@@ -96,6 +96,65 @@ export function canInsertStepOrSnippet(
96
96
  return false;
97
97
  }
98
98
 
99
+ /**
100
+ * Programmatically add an empty step block to the editor.
101
+ * - If a "Steps" heading exists, inserts after the last step/snippet under it.
102
+ * - Otherwise, appends a "Steps" heading + empty step at the end.
103
+ * Returns the inserted step's block ID (for focusing), or null.
104
+ */
105
+ export function addStepsBlock(editor: {
106
+ document: any[];
107
+ insertBlocks: (blocks: any[], referenceId: string, placement: string) => any[];
108
+ }): string | null {
109
+ const allBlocks = editor.document;
110
+ const emptyStep = {
111
+ type: "testStep" as const,
112
+ props: { stepTitle: "", stepData: "", expectedResult: "" },
113
+ children: [],
114
+ };
115
+
116
+ let stepsHeadingIndex = -1;
117
+ for (let i = 0; i < allBlocks.length; i++) {
118
+ const b = allBlocks[i];
119
+ if (b.type !== "heading") continue;
120
+ const text = (Array.isArray(b.content) ? b.content : [])
121
+ .filter((n: any) => n.type === "text")
122
+ .map((n: any) => n.text ?? "")
123
+ .join("")
124
+ .trim()
125
+ .toLowerCase()
126
+ .replace(/[:\-–—]$/, "");
127
+ if (isStepsHeading(text)) {
128
+ stepsHeadingIndex = i;
129
+ break;
130
+ }
131
+ }
132
+
133
+ if (stepsHeadingIndex >= 0) {
134
+ let lastIndex = stepsHeadingIndex;
135
+ for (let i = stepsHeadingIndex + 1; i < allBlocks.length; i++) {
136
+ const b = allBlocks[i];
137
+ if (b.type === "testStep" || b.type === "snippet" || isEmptyParagraph(b)) {
138
+ lastIndex = i;
139
+ continue;
140
+ }
141
+ break;
142
+ }
143
+ const inserted = editor.insertBlocks([emptyStep], allBlocks[lastIndex].id, "after");
144
+ return inserted?.[0]?.id ?? null;
145
+ }
146
+
147
+ const lastBlock = allBlocks[allBlocks.length - 1];
148
+ const stepsHeading = {
149
+ type: "heading" as const,
150
+ props: { level: 3 },
151
+ content: [{ type: "text" as const, text: "Steps" }],
152
+ children: [],
153
+ };
154
+ const inserted = editor.insertBlocks([stepsHeading, emptyStep], lastBlock.id, "after");
155
+ return inserted?.[1]?.id ?? null;
156
+ }
157
+
99
158
  export const stepBlock = createReactBlockSpec(
100
159
  {
101
160
  type: "testStep",
@@ -14,6 +14,19 @@ export function createMarkdownPasteHandler(
14
14
  converter: (markdown: string) => CustomPartialBlock[],
15
15
  ) {
16
16
  return ({ event, editor, defaultPasteHandler }: PasteHandlerContext): boolean | undefined => {
17
+ const types = event.clipboardData?.types ?? [];
18
+
19
+ if (types.includes("blocknote/html")) return defaultPasteHandler();
20
+ if (types.includes("vscode-editor-data")) return defaultPasteHandler();
21
+
22
+ if (types.includes("text/html")) {
23
+ const html = event.clipboardData?.getData("text/html") ?? "";
24
+ if (/<(pre|code)[\s>]/i.test(html)) return defaultPasteHandler();
25
+ }
26
+
27
+ const cursorBlock = editor.getTextCursorPosition().block;
28
+ if (cursorBlock?.type === "codeBlock" || cursorBlock?.type === "quote" || cursorBlock?.type === "table") return defaultPasteHandler();
29
+
17
30
  const plainText = event.clipboardData?.getData("text/plain") ?? "";
18
31
  if (!plainText.trim()) return defaultPasteHandler();
19
32
 
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, isStepsHeading } from "./editor/blocks/step";
7
+ export { stepBlock, canInsertStepOrSnippet, isStepsHeading, addStepsBlock } from "./editor/blocks/step";
8
8
  export { snippetBlock } from "./editor/blocks/snippet";
9
9
  export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";
10
10