testomatio-editor-blocks 0.4.64 → 0.4.66

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.
@@ -0,0 +1,37 @@
1
+ export type MetaField = {
2
+ key: string;
3
+ value: string;
4
+ };
5
+ export declare function serializeMetaFields(fields: MetaField[]): string;
6
+ export declare const testMetaBlock: {
7
+ config: {
8
+ readonly type: "testMeta";
9
+ readonly content: "none";
10
+ readonly propSchema: {
11
+ readonly metaKind: {
12
+ readonly default: "test";
13
+ };
14
+ readonly metaFields: {
15
+ readonly default: "[]";
16
+ };
17
+ readonly metaInline: {
18
+ readonly default: false;
19
+ };
20
+ };
21
+ };
22
+ implementation: import("@blocknote/core").TiptapBlockImplementation<{
23
+ readonly type: "testMeta";
24
+ readonly content: "none";
25
+ readonly propSchema: {
26
+ readonly metaKind: {
27
+ readonly default: "test";
28
+ };
29
+ readonly metaFields: {
30
+ readonly default: "[]";
31
+ };
32
+ readonly metaInline: {
33
+ readonly default: false;
34
+ };
35
+ };
36
+ }, any, import("@blocknote/core").InlineContentSchema, import("@blocknote/core").StyleSchema>;
37
+ };
@@ -0,0 +1,111 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createReactBlockSpec } from "@blocknote/react";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { getMetaFieldSuggestions } from "../testMetaFields";
5
+ const ID_KEYS = new Set(["id"]);
6
+ function parseMetaFields(raw) {
7
+ if (typeof raw !== "string" || raw.trim().length === 0) {
8
+ return [];
9
+ }
10
+ try {
11
+ const parsed = JSON.parse(raw);
12
+ if (!Array.isArray(parsed)) {
13
+ return [];
14
+ }
15
+ return parsed
16
+ .filter((item) => item && typeof item === "object")
17
+ .map((item) => ({
18
+ key: typeof item.key === "string" ? item.key : "",
19
+ value: typeof item.value === "string" ? item.value : "",
20
+ }));
21
+ }
22
+ catch {
23
+ return [];
24
+ }
25
+ }
26
+ export function serializeMetaFields(fields) {
27
+ return JSON.stringify(fields);
28
+ }
29
+ function AddFieldMenu({ kind, usedKeys, onPick }) {
30
+ const [isOpen, setIsOpen] = useState(false);
31
+ const containerRef = useRef(null);
32
+ useEffect(() => {
33
+ if (!isOpen)
34
+ return;
35
+ const handleMouseDown = (event) => {
36
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
37
+ setIsOpen(false);
38
+ }
39
+ };
40
+ document.addEventListener("mousedown", handleMouseDown);
41
+ return () => document.removeEventListener("mousedown", handleMouseDown);
42
+ }, [isOpen]);
43
+ const available = useMemo(() => {
44
+ const used = new Set(usedKeys.map((k) => k.trim().toLowerCase()));
45
+ return getMetaFieldSuggestions(kind).filter((s) => !used.has(s.key.trim().toLowerCase()));
46
+ }, [kind, usedKeys]);
47
+ const pick = useCallback((key) => {
48
+ onPick(key);
49
+ setIsOpen(false);
50
+ }, [onPick]);
51
+ return (_jsxs("div", { className: "bn-testmeta__add-wrap", ref: containerRef, children: [_jsx("button", { type: "button", className: "bn-testmeta__add", "aria-label": "Add field", title: "Add field", onClick: () => setIsOpen((prev) => !prev), children: "+" }), isOpen && (_jsxs("div", { className: "bn-testmeta__menu", role: "listbox", children: [available.map((suggestion) => {
52
+ var _a;
53
+ return (_jsx("button", { type: "button", role: "option", className: "bn-testmeta__menu-item", onMouseDown: (event) => {
54
+ event.preventDefault();
55
+ pick(suggestion.key);
56
+ }, children: (_a = suggestion.label) !== null && _a !== void 0 ? _a : suggestion.key }, suggestion.key));
57
+ }), _jsx("button", { type: "button", className: "bn-testmeta__menu-item bn-testmeta__menu-item--custom", onMouseDown: (event) => {
58
+ event.preventDefault();
59
+ pick("");
60
+ }, children: "Custom field\u2026" })] }))] }));
61
+ }
62
+ export const testMetaBlock = createReactBlockSpec({
63
+ type: "testMeta",
64
+ content: "none",
65
+ propSchema: {
66
+ // "test" | "suite" — which keyword the comment opened with.
67
+ metaKind: {
68
+ default: "test",
69
+ },
70
+ // JSON-encoded MetaField[] so insertion order is preserved.
71
+ metaFields: {
72
+ default: "[]",
73
+ },
74
+ // true when the source comment was a one-liner (`<!-- test id: @T.. -->`).
75
+ metaInline: {
76
+ default: false,
77
+ },
78
+ },
79
+ }, {
80
+ render: ({ block, editor }) => {
81
+ const kind = block.props.metaKind === "suite" ? "suite" : "test";
82
+ const fields = useMemo(() => parseMetaFields(block.props.metaFields), [block.props.metaFields]);
83
+ const commitFields = useCallback((next) => {
84
+ editor.updateBlock(block.id, {
85
+ props: { metaFields: serializeMetaFields(next) },
86
+ });
87
+ }, [block.id, editor]);
88
+ const handleValueChange = useCallback((index, value) => {
89
+ const next = fields.map((field, i) => i === index ? { ...field, value } : field);
90
+ commitFields(next);
91
+ }, [fields, commitFields]);
92
+ const handleKeyChange = useCallback((index, key) => {
93
+ const next = fields.map((field, i) => i === index ? { ...field, key } : field);
94
+ commitFields(next);
95
+ }, [fields, commitFields]);
96
+ const handleRemove = useCallback((index) => {
97
+ commitFields(fields.filter((_, i) => i !== index));
98
+ }, [fields, commitFields]);
99
+ const handleAddField = useCallback((key) => {
100
+ commitFields([...fields, { key, value: "" }]);
101
+ }, [fields, commitFields]);
102
+ const usedKeys = useMemo(() => fields.map((f) => f.key), [fields]);
103
+ // The read-only `id` is shown inline on the header line next to the kind
104
+ // label; every other field renders as an editable row below.
105
+ const idField = fields.find((f) => ID_KEYS.has(f.key.trim().toLowerCase()));
106
+ const editableFields = fields
107
+ .map((field, index) => ({ field, index }))
108
+ .filter(({ field }) => !ID_KEYS.has(field.key.trim().toLowerCase()));
109
+ return (_jsxs("div", { className: "bn-testmeta", "data-block-id": block.id, "data-kind": kind, contentEditable: false, suppressContentEditableWarning: true, draggable: false, children: [_jsxs("div", { className: "bn-testmeta__header", children: [_jsx("span", { className: "bn-testmeta__label", children: kind.toUpperCase() }), (idField === null || idField === void 0 ? void 0 : idField.value) && _jsx("span", { className: "bn-testmeta__id", children: idField.value }), _jsx(AddFieldMenu, { kind: kind, usedKeys: usedKeys, onPick: handleAddField })] }), editableFields.length > 0 && (_jsx("div", { className: "bn-testmeta__rows", children: editableFields.map(({ field, index }) => (_jsxs("div", { className: "bn-testmeta__row", children: [_jsx("input", { className: "bn-testmeta__key bn-testmeta__key--input", type: "text", value: field.key, placeholder: "key", spellCheck: false, onChange: (e) => handleKeyChange(index, e.target.value) }), _jsx("input", { className: "bn-testmeta__value", type: "text", value: field.value, placeholder: "value", spellCheck: false, onChange: (e) => handleValueChange(index, e.target.value) }), _jsx("button", { type: "button", className: "bn-testmeta__remove", "aria-label": "Remove field", title: "Remove field", onClick: () => handleRemove(index), children: "\u00D7" })] }, index))) }))] }));
110
+ },
111
+ });
@@ -224,7 +224,7 @@ function serializeChildren(block, ctx) {
224
224
  return serializeBlocks(block.children, childCtx);
225
225
  }
226
226
  function serializeBlock(block, ctx, orderedIndex, stepIndex) {
227
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
227
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
228
228
  const lines = [];
229
229
  const indent = ctx.listDepth > 0 ? " ".repeat(ctx.listDepth) : "";
230
230
  switch (block.type) {
@@ -262,8 +262,7 @@ function serializeBlock(block, ctx, orderedIndex, stepIndex) {
262
262
  return lines;
263
263
  }
264
264
  case "codeBlock": {
265
- const rawLanguage = block.props.language || "";
266
- const language = /[\s`]/.test(rawLanguage) ? "" : rawLanguage;
265
+ const language = block.props.language || "";
267
266
  const fence = "```" + language;
268
267
  const body = inlineContentToPlainText(block.content);
269
268
  lines.push(fence);
@@ -317,19 +316,48 @@ function serializeBlock(block, ctx, orderedIndex, stepIndex) {
317
316
  }
318
317
  return lines;
319
318
  }
319
+ case "testMeta": {
320
+ const kind = block.props.metaKind === "suite" ? "suite" : "test";
321
+ const inline = Boolean(block.props.metaInline);
322
+ let fields = [];
323
+ try {
324
+ const parsed = JSON.parse(((_e = block.props.metaFields) !== null && _e !== void 0 ? _e : "[]"));
325
+ if (Array.isArray(parsed)) {
326
+ fields = parsed
327
+ .filter((f) => f && typeof f === "object" && typeof f.key === "string")
328
+ .map((f) => ({ key: f.key.trim(), value: typeof f.value === "string" ? f.value.trim() : "" }))
329
+ // Skip incomplete fields: both a key and a value are required.
330
+ .filter((f) => f.key.length > 0 && f.value.length > 0);
331
+ }
332
+ }
333
+ catch {
334
+ fields = [];
335
+ }
336
+ // Preserve the one-liner form only when it still fits on a single line
337
+ // (a one-liner holds at most one `key: value` pair).
338
+ if (inline && fields.length <= 1) {
339
+ const field = fields[0];
340
+ lines.push(field ? `<!-- ${kind} ${field.key}: ${field.value} -->` : `<!-- ${kind} -->`);
341
+ return lines;
342
+ }
343
+ lines.push(`<!-- ${kind}`);
344
+ fields.forEach((field) => lines.push(`${field.key}: ${field.value}`));
345
+ lines.push("-->");
346
+ return lines;
347
+ }
320
348
  case "testStep":
321
349
  case "snippet": {
322
350
  const isSnippet = block.type === "snippet";
323
- const snippetId = isSnippet ? ((_e = block.props.snippetId) !== null && _e !== void 0 ? _e : "").trim() : "";
351
+ const snippetId = isSnippet ? ((_f = block.props.snippetId) !== null && _f !== void 0 ? _f : "").trim() : "";
324
352
  const stepTitle = isSnippet
325
- ? ((_f = block.props.snippetTitle) !== null && _f !== void 0 ? _f : "").trim()
326
- : ((_g = block.props.stepTitle) !== null && _g !== void 0 ? _g : "").trim();
353
+ ? ((_g = block.props.snippetTitle) !== null && _g !== void 0 ? _g : "").trim()
354
+ : ((_h = block.props.stepTitle) !== null && _h !== void 0 ? _h : "").trim();
327
355
  const stepData = isSnippet
328
- ? ((_h = block.props.snippetData) !== null && _h !== void 0 ? _h : "").trim()
329
- : ((_j = block.props.stepData) !== null && _j !== void 0 ? _j : "").trim();
356
+ ? ((_j = block.props.snippetData) !== null && _j !== void 0 ? _j : "").trim()
357
+ : ((_k = block.props.stepData) !== null && _k !== void 0 ? _k : "").trim();
330
358
  const expectedResult = isSnippet
331
- ? ((_k = block.props.snippetExpectedResult) !== null && _k !== void 0 ? _k : "").trim()
332
- : ((_l = block.props.expectedResult) !== null && _l !== void 0 ? _l : "").trim();
359
+ ? ((_l = block.props.snippetExpectedResult) !== null && _l !== void 0 ? _l : "").trim()
360
+ : ((_m = block.props.expectedResult) !== null && _m !== void 0 ? _m : "").trim();
333
361
  if (isSnippet) {
334
362
  if (snippetId) {
335
363
  lines.push(`<!-- begin snippet #${snippetId} -->`);
@@ -353,7 +381,7 @@ function serializeBlock(block, ctx, orderedIndex, stepIndex) {
353
381
  const normalizedExpectedForCheck = stripExpectedPrefix(expectedResult).trim();
354
382
  const hasContent = stepData.length > 0 || normalizedExpectedForCheck.length > 0;
355
383
  if (normalizedTitle.length > 0 || hasContent) {
356
- const listStyle = (_m = block.props.listStyle) !== null && _m !== void 0 ? _m : "bullet";
384
+ const listStyle = (_o = block.props.listStyle) !== null && _o !== void 0 ? _o : "bullet";
357
385
  const prefix = listStyle === "ordered" ? `${(stepIndex !== null && stepIndex !== void 0 ? stepIndex : 0) + 1}.` : "*";
358
386
  lines.push(normalizedTitle.length > 0 ? `${prefix} ${escapeStepContent(normalizedTitle)}` : `${prefix} `);
359
387
  }
@@ -407,7 +435,7 @@ function serializeBlock(block, ctx, orderedIndex, stepIndex) {
407
435
  return lines;
408
436
  }
409
437
  const headerRowCount = rows.length
410
- ? Math.min(rows.length, Math.max((_o = tableContent.headerRows) !== null && _o !== void 0 ? _o : 1, 1))
438
+ ? Math.min(rows.length, Math.max((_p = tableContent.headerRows) !== null && _p !== void 0 ? _p : 1, 1))
411
439
  : 0;
412
440
  const columnAlignments = new Array(columnCount).fill("left");
413
441
  const getCellAlignment = (cell) => {
@@ -1088,17 +1116,8 @@ function parseCodeBlock(lines, index) {
1088
1116
  nextIndex: index + 1,
1089
1117
  };
1090
1118
  }
1091
- const info = afterOpening.trim();
1092
- let language = "";
1119
+ const language = afterOpening.trim();
1093
1120
  const body = [];
1094
- if (info.length > 0) {
1095
- if (/[\s`]/.test(info)) {
1096
- body.push(afterOpening);
1097
- }
1098
- else {
1099
- language = info;
1100
- }
1101
- }
1102
1121
  let next = index + 1;
1103
1122
  while (next < lines.length && !lines[next].startsWith("```")) {
1104
1123
  body.push(lines[next]);
@@ -1155,6 +1174,86 @@ function parseParagraph(lines, index) {
1155
1174
  nextIndex: index + 1,
1156
1175
  };
1157
1176
  }
1177
+ const META_COMMENT_OPEN_REGEX = /^<!--\s*(test|suite)(?=\s|-->|$)/i;
1178
+ function metaFieldsFromBody(bodyLines) {
1179
+ const fields = [];
1180
+ for (const raw of bodyLines) {
1181
+ const line = raw.trim();
1182
+ if (!line)
1183
+ continue;
1184
+ const colon = line.indexOf(":");
1185
+ // "Each line is `key: value`; lines without `:` are ignored."
1186
+ if (colon === -1)
1187
+ continue;
1188
+ const key = line.slice(0, colon).trim();
1189
+ const value = line.slice(colon + 1).trim();
1190
+ if (!key)
1191
+ continue;
1192
+ fields.push({ key, value });
1193
+ }
1194
+ return fields;
1195
+ }
1196
+ function parseMetaComment(lines, index) {
1197
+ const first = lines[index];
1198
+ const openMatch = first.match(META_COMMENT_OPEN_REGEX);
1199
+ if (!openMatch) {
1200
+ return null;
1201
+ }
1202
+ const kind = openMatch[1].toLowerCase();
1203
+ let bodyLines = [];
1204
+ let inline = false;
1205
+ let nextIndex;
1206
+ // One-liner: opening and closing markers on the same line.
1207
+ const oneLine = first.match(/^<!--\s*(?:test|suite)\b\s*([\s\S]*?)\s*-->\s*$/i);
1208
+ if (oneLine) {
1209
+ inline = true;
1210
+ if (oneLine[1].trim()) {
1211
+ bodyLines = [oneLine[1].trim()];
1212
+ }
1213
+ nextIndex = index + 1;
1214
+ }
1215
+ else {
1216
+ // Block form: keyword line, fields on their own lines, closing `-->`.
1217
+ const afterKeyword = first.replace(/^<!--\s*(?:test|suite)\b/i, "").trim();
1218
+ if (afterKeyword) {
1219
+ bodyLines.push(afterKeyword);
1220
+ }
1221
+ let next = index + 1;
1222
+ let closed = false;
1223
+ while (next < lines.length) {
1224
+ const current = lines[next];
1225
+ if (/-->\s*$/.test(current)) {
1226
+ const beforeClose = current.replace(/-->\s*$/, "").trim();
1227
+ if (beforeClose) {
1228
+ bodyLines.push(beforeClose);
1229
+ }
1230
+ next += 1;
1231
+ closed = true;
1232
+ break;
1233
+ }
1234
+ bodyLines.push(current);
1235
+ next += 1;
1236
+ }
1237
+ if (!closed) {
1238
+ // Unterminated comment — let normal parsing handle these lines.
1239
+ return null;
1240
+ }
1241
+ nextIndex = next;
1242
+ }
1243
+ const fields = metaFieldsFromBody(bodyLines);
1244
+ return {
1245
+ block: {
1246
+ type: "testMeta",
1247
+ props: {
1248
+ metaKind: kind,
1249
+ metaFields: JSON.stringify(fields),
1250
+ metaInline: inline,
1251
+ },
1252
+ children: [],
1253
+ },
1254
+ nextIndex,
1255
+ };
1256
+ }
1158
1257
  function parseSnippetWrapper(lines, index) {
1159
1258
  const trimmed = lines[index].trim();
1160
1259
  const startMatch = trimmed.match(/^<!--\s*begin snippet\s*#?\s*([^\s>]+)\s*-->/i);
@@ -1267,6 +1366,14 @@ export function markdownToBlocks(markdown, _options) {
1267
1366
  index += 1;
1268
1367
  continue;
1269
1368
  }
1369
+ // Test/suite metadata comments can appear anywhere (typically at the top of
1370
+ // a document or right after a heading), so this runs ungated.
1371
+ const metaComment = parseMetaComment(lines, index);
1372
+ if (metaComment) {
1373
+ blocks.push(metaComment.block);
1374
+ index = metaComment.nextIndex;
1375
+ continue;
1376
+ }
1270
1377
  const snippetWrapper = stepsHeadingLevel !== null
1271
1378
  ? parseSnippetWrapper(lines, index)
1272
1379
  : null;
@@ -117,6 +117,38 @@ export declare const customSchema: BlockNoteSchema<import("@blocknote/core").Blo
117
117
  };
118
118
  }, any, import("@blocknote/core").InlineContentSchema, import("@blocknote/core").StyleSchema>;
119
119
  };
120
+ testMeta: {
121
+ config: {
122
+ readonly type: "testMeta";
123
+ readonly content: "none";
124
+ readonly propSchema: {
125
+ readonly metaKind: {
126
+ readonly default: "test";
127
+ };
128
+ readonly metaFields: {
129
+ readonly default: "[]";
130
+ };
131
+ readonly metaInline: {
132
+ readonly default: false;
133
+ };
134
+ };
135
+ };
136
+ implementation: import("@blocknote/core").TiptapBlockImplementation<{
137
+ readonly type: "testMeta";
138
+ readonly content: "none";
139
+ readonly propSchema: {
140
+ readonly metaKind: {
141
+ readonly default: "test";
142
+ };
143
+ readonly metaFields: {
144
+ readonly default: "[]";
145
+ };
146
+ readonly metaInline: {
147
+ readonly default: false;
148
+ };
149
+ };
150
+ }, any, import("@blocknote/core").InlineContentSchema, import("@blocknote/core").StyleSchema>;
151
+ };
120
152
  paragraph: {
121
153
  config: {
122
154
  type: "paragraph";
@@ -2,6 +2,7 @@ import { defaultBlockSpecs } from "@blocknote/core";
2
2
  import { BlockNoteSchema } from "@blocknote/core";
3
3
  import { stepBlock } from "./blocks/step";
4
4
  import { snippetBlock } from "./blocks/snippet";
5
+ import { testMetaBlock } from "./blocks/testMeta";
5
6
  import { fileBlock } from "./blocks/fileBlock";
6
7
  import { htmlToMarkdown, markdownToHtml } from "./blocks/markdown";
7
8
  export const customSchema = BlockNoteSchema.create({
@@ -10,6 +11,7 @@ export const customSchema = BlockNoteSchema.create({
10
11
  file: fileBlock,
11
12
  testStep: stepBlock,
12
13
  snippet: snippetBlock,
14
+ testMeta: testMetaBlock,
13
15
  },
14
16
  });
15
17
  export const __markdownTestUtils = {
@@ -0,0 +1,17 @@
1
+ export type MetaFieldSuggestion = {
2
+ /** The field key that gets inserted, e.g. "priority". */
3
+ key: string;
4
+ /** Optional display label; defaults to `key`. */
5
+ label?: string;
6
+ };
7
+ /**
8
+ * Either a flat list (applied to both test and suite blocks) or per-kind lists.
9
+ * Configure from the host app via `setMetaFieldSuggestions` so embedders can
10
+ * plug in their own set of suggested metadata fields.
11
+ */
12
+ export type MetaFieldSuggestionsConfig = MetaFieldSuggestion[] | {
13
+ test?: MetaFieldSuggestion[];
14
+ suite?: MetaFieldSuggestion[];
15
+ };
16
+ export declare function setMetaFieldSuggestions(config: MetaFieldSuggestionsConfig | null): void;
17
+ export declare function getMetaFieldSuggestions(kind: "test" | "suite"): MetaFieldSuggestion[];
@@ -0,0 +1,33 @@
1
+ // Defaults follow the classical Testomatio markdown format. `id` is intentionally
2
+ // omitted: it is a read-only, system-assigned field, not something users add.
3
+ const DEFAULT_TEST_FIELDS = [
4
+ { key: "priority" },
5
+ { key: "type" },
6
+ { key: "tags" },
7
+ { key: "labels" },
8
+ { key: "assignee" },
9
+ { key: "creator" },
10
+ { key: "shared" },
11
+ ];
12
+ const DEFAULT_SUITE_FIELDS = [
13
+ { key: "emoji" },
14
+ { key: "tags" },
15
+ { key: "labels" },
16
+ { key: "assignee" },
17
+ ];
18
+ let configured = null;
19
+ export function setMetaFieldSuggestions(config) {
20
+ configured = config;
21
+ }
22
+ export function getMetaFieldSuggestions(kind) {
23
+ if (configured) {
24
+ if (Array.isArray(configured)) {
25
+ return configured;
26
+ }
27
+ const list = kind === "suite" ? configured.suite : configured.test;
28
+ if (list) {
29
+ return list;
30
+ }
31
+ }
32
+ return kind === "suite" ? DEFAULT_SUITE_FIELDS : DEFAULT_TEST_FIELDS;
33
+ }
@@ -1,6 +1,8 @@
1
1
  export { customSchema, type CustomSchema, type CustomBlock, type CustomEditor, } from "./editor/customSchema";
2
2
  export { stepBlock, canInsertStepOrSnippet, isStepsHeading, addStepsBlock, addSnippetBlock } from "./editor/blocks/step";
3
3
  export { snippetBlock } from "./editor/blocks/snippet";
4
+ export { testMetaBlock } from "./editor/blocks/testMeta";
5
+ export { setMetaFieldSuggestions, getMetaFieldSuggestions, type MetaFieldSuggestion, type MetaFieldSuggestionsConfig, } from "./editor/testMetaFields";
4
6
  export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";
5
7
  export { blocksToMarkdown, markdownToBlocks, type CustomEditorBlock, type CustomPartialBlock, type MarkdownToBlocksOptions, } from "./editor/customMarkdownConverter";
6
8
  export { useStepAutocomplete, parseStepsFromJsonApi, setStepsFetcher, type StepSuggestion, type StepJsonApiDocument, type StepJsonApiResource, } from "./editor/stepAutocomplete";
package/package/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  export { customSchema, } from "./editor/customSchema";
2
2
  export { stepBlock, canInsertStepOrSnippet, isStepsHeading, addStepsBlock, addSnippetBlock } from "./editor/blocks/step";
3
3
  export { snippetBlock } from "./editor/blocks/snippet";
4
+ export { testMetaBlock } from "./editor/blocks/testMeta";
5
+ export { setMetaFieldSuggestions, getMetaFieldSuggestions, } from "./editor/testMetaFields";
4
6
  export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";
5
7
  export { blocksToMarkdown, markdownToBlocks, } from "./editor/customMarkdownConverter";
6
8
  export { useStepAutocomplete, parseStepsFromJsonApi, setStepsFetcher, } from "./editor/stepAutocomplete";