@optilogic/editor 1.0.0-beta.0

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,95 @@
1
+ import * as React from 'react';
2
+ import { BaseEditor, Descendant } from 'slate';
3
+ import { ReactEditor } from 'slate-react';
4
+ import { HistoryEditor } from 'slate-history';
5
+
6
+ /**
7
+ * SlateEditor Component
8
+ *
9
+ * A reusable rich text editor primitive based on Slate.js
10
+ * Provides consistent theming and can be used as an alternative to textarea
11
+ */
12
+
13
+ type TagElement = {
14
+ type: "tag";
15
+ tag: string;
16
+ children: [{
17
+ text: "";
18
+ }];
19
+ };
20
+ type ParagraphElement = {
21
+ type: "paragraph";
22
+ children: Descendant[];
23
+ };
24
+ type CustomElement = TagElement | ParagraphElement;
25
+ type CustomText = {
26
+ text: string;
27
+ };
28
+ declare module "slate" {
29
+ interface CustomTypes {
30
+ Editor: BaseEditor & ReactEditor & HistoryEditor;
31
+ Element: CustomElement;
32
+ Text: CustomText;
33
+ }
34
+ }
35
+ interface SlateEditorRef {
36
+ /** Focus the editor */
37
+ focus: () => void;
38
+ /** Clear the editor content */
39
+ clear: () => void;
40
+ /** Get plain text content */
41
+ getText: () => string;
42
+ /** Insert text at cursor */
43
+ insertText: (text: string) => void;
44
+ /** Insert a tag element */
45
+ insertTag: (tag: string) => void;
46
+ }
47
+ interface SlateEditorProps {
48
+ /** Initial plain text value */
49
+ value?: string;
50
+ /** Callback when content changes (plain text) */
51
+ onChange?: (value: string) => void;
52
+ /** Callback when a tag is created */
53
+ onTagCreate?: (tag: string) => void;
54
+ /** Callback when a tag is deleted */
55
+ onTagDelete?: (tag: string) => void;
56
+ /** Callback when Enter is pressed (without Shift) */
57
+ onSubmit?: (text: string) => void;
58
+ /** Placeholder text */
59
+ placeholder?: string;
60
+ /** Whether the editor is disabled */
61
+ disabled?: boolean;
62
+ /** Whether to enable tag detection (# character) */
63
+ enableTags?: boolean;
64
+ /** Minimum number of rows */
65
+ minRows?: number;
66
+ /** Maximum number of rows before scrolling */
67
+ maxRows?: number;
68
+ /** Additional class names */
69
+ className?: string;
70
+ }
71
+ /**
72
+ * Convert Slate content to plain text
73
+ */
74
+ declare function serializeToText(nodes: Descendant[]): string;
75
+ /**
76
+ * Convert plain text to Slate nodes (preserving existing tags)
77
+ */
78
+ declare function deserializeFromText(text: string): Descendant[];
79
+ /**
80
+ * SlateEditor component
81
+ *
82
+ * A rich text editor based on Slate.js with support for inline tags.
83
+ * Can be used as a drop-in replacement for textarea with enhanced features.
84
+ *
85
+ * @example
86
+ * <SlateEditor
87
+ * value={content}
88
+ * onChange={setContent}
89
+ * placeholder="Type your message..."
90
+ * onSubmit={(text) => console.log('Submitted:', text)}
91
+ * />
92
+ */
93
+ declare const SlateEditor: React.ForwardRefExoticComponent<SlateEditorProps & React.RefAttributes<SlateEditorRef>>;
94
+
95
+ export { SlateEditor, type SlateEditorProps, type SlateEditorRef, deserializeFromText, serializeToText };
@@ -0,0 +1,95 @@
1
+ import * as React from 'react';
2
+ import { BaseEditor, Descendant } from 'slate';
3
+ import { ReactEditor } from 'slate-react';
4
+ import { HistoryEditor } from 'slate-history';
5
+
6
+ /**
7
+ * SlateEditor Component
8
+ *
9
+ * A reusable rich text editor primitive based on Slate.js
10
+ * Provides consistent theming and can be used as an alternative to textarea
11
+ */
12
+
13
+ type TagElement = {
14
+ type: "tag";
15
+ tag: string;
16
+ children: [{
17
+ text: "";
18
+ }];
19
+ };
20
+ type ParagraphElement = {
21
+ type: "paragraph";
22
+ children: Descendant[];
23
+ };
24
+ type CustomElement = TagElement | ParagraphElement;
25
+ type CustomText = {
26
+ text: string;
27
+ };
28
+ declare module "slate" {
29
+ interface CustomTypes {
30
+ Editor: BaseEditor & ReactEditor & HistoryEditor;
31
+ Element: CustomElement;
32
+ Text: CustomText;
33
+ }
34
+ }
35
+ interface SlateEditorRef {
36
+ /** Focus the editor */
37
+ focus: () => void;
38
+ /** Clear the editor content */
39
+ clear: () => void;
40
+ /** Get plain text content */
41
+ getText: () => string;
42
+ /** Insert text at cursor */
43
+ insertText: (text: string) => void;
44
+ /** Insert a tag element */
45
+ insertTag: (tag: string) => void;
46
+ }
47
+ interface SlateEditorProps {
48
+ /** Initial plain text value */
49
+ value?: string;
50
+ /** Callback when content changes (plain text) */
51
+ onChange?: (value: string) => void;
52
+ /** Callback when a tag is created */
53
+ onTagCreate?: (tag: string) => void;
54
+ /** Callback when a tag is deleted */
55
+ onTagDelete?: (tag: string) => void;
56
+ /** Callback when Enter is pressed (without Shift) */
57
+ onSubmit?: (text: string) => void;
58
+ /** Placeholder text */
59
+ placeholder?: string;
60
+ /** Whether the editor is disabled */
61
+ disabled?: boolean;
62
+ /** Whether to enable tag detection (# character) */
63
+ enableTags?: boolean;
64
+ /** Minimum number of rows */
65
+ minRows?: number;
66
+ /** Maximum number of rows before scrolling */
67
+ maxRows?: number;
68
+ /** Additional class names */
69
+ className?: string;
70
+ }
71
+ /**
72
+ * Convert Slate content to plain text
73
+ */
74
+ declare function serializeToText(nodes: Descendant[]): string;
75
+ /**
76
+ * Convert plain text to Slate nodes (preserving existing tags)
77
+ */
78
+ declare function deserializeFromText(text: string): Descendant[];
79
+ /**
80
+ * SlateEditor component
81
+ *
82
+ * A rich text editor based on Slate.js with support for inline tags.
83
+ * Can be used as a drop-in replacement for textarea with enhanced features.
84
+ *
85
+ * @example
86
+ * <SlateEditor
87
+ * value={content}
88
+ * onChange={setContent}
89
+ * placeholder="Type your message..."
90
+ * onSubmit={(text) => console.log('Submitted:', text)}
91
+ * />
92
+ */
93
+ declare const SlateEditor: React.ForwardRefExoticComponent<SlateEditorProps & React.RefAttributes<SlateEditorRef>>;
94
+
95
+ export { SlateEditor, type SlateEditorProps, type SlateEditorRef, deserializeFromText, serializeToText };
package/dist/index.js ADDED
@@ -0,0 +1,463 @@
1
+ import * as React from 'react';
2
+ import { createEditor, Transforms, Editor, Range, Path, Text, Node, Element } from 'slate';
3
+ import { withReact, ReactEditor, Slate, Editable } from 'slate-react';
4
+ import { withHistory } from 'slate-history';
5
+ import { cn } from '@optilogic/core';
6
+ import { jsx, jsxs } from 'react/jsx-runtime';
7
+
8
+ // src/slate-editor.tsx
9
+ var withTags = (editor) => {
10
+ const { isInline, isVoid, deleteBackward, deleteForward } = editor;
11
+ editor.isInline = (element) => {
12
+ return element.type === "tag" ? true : isInline(element);
13
+ };
14
+ editor.isVoid = (element) => {
15
+ return element.type === "tag" ? true : isVoid(element);
16
+ };
17
+ editor.deleteBackward = (unit) => {
18
+ const { selection } = editor;
19
+ if (selection && Range.isCollapsed(selection)) {
20
+ const [tagEntry] = Editor.nodes(editor, {
21
+ match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.type === "tag"
22
+ });
23
+ if (tagEntry) {
24
+ const [, path] = tagEntry;
25
+ const after = Editor.after(editor, path);
26
+ if (after && Path.equals(selection.anchor.path, after.path)) {
27
+ Transforms.removeNodes(editor, { at: path });
28
+ return;
29
+ }
30
+ }
31
+ const before = Editor.before(editor, selection);
32
+ if (before) {
33
+ const [beforeTagEntry] = Editor.nodes(editor, {
34
+ at: before,
35
+ match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.type === "tag"
36
+ });
37
+ if (beforeTagEntry) {
38
+ const [, path] = beforeTagEntry;
39
+ Transforms.removeNodes(editor, { at: path });
40
+ return;
41
+ }
42
+ }
43
+ }
44
+ deleteBackward(unit);
45
+ };
46
+ editor.deleteForward = (unit) => {
47
+ const { selection } = editor;
48
+ if (selection && Range.isCollapsed(selection)) {
49
+ const after = Editor.after(editor, selection);
50
+ if (after) {
51
+ const [afterTagEntry] = Editor.nodes(editor, {
52
+ at: after,
53
+ match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.type === "tag"
54
+ });
55
+ if (afterTagEntry) {
56
+ const [, path] = afterTagEntry;
57
+ Transforms.removeNodes(editor, { at: path });
58
+ return;
59
+ }
60
+ }
61
+ }
62
+ deleteForward(unit);
63
+ };
64
+ return editor;
65
+ };
66
+ function serializeToText(nodes) {
67
+ return nodes.map((n) => {
68
+ if (Text.isText(n)) {
69
+ return n.text;
70
+ }
71
+ if (Element.isElement(n)) {
72
+ if (n.type === "tag") {
73
+ return `#${n.tag}`;
74
+ }
75
+ return serializeToText(n.children);
76
+ }
77
+ return "";
78
+ }).join("");
79
+ }
80
+ function deserializeFromText(text) {
81
+ if (!text) {
82
+ return [{ type: "paragraph", children: [{ text: "" }] }];
83
+ }
84
+ const children = [];
85
+ const tagRegex = /#([a-zA-Z0-9@#$_-]+(?:--[a-zA-Z0-9_-]+)?)/g;
86
+ let lastIndex = 0;
87
+ let match;
88
+ while ((match = tagRegex.exec(text)) !== null) {
89
+ if (match.index > lastIndex) {
90
+ children.push({ text: text.substring(lastIndex, match.index) });
91
+ }
92
+ children.push({
93
+ type: "tag",
94
+ tag: match[1],
95
+ children: [{ text: "" }]
96
+ });
97
+ lastIndex = match.index + match[0].length;
98
+ }
99
+ if (lastIndex < text.length) {
100
+ children.push({ text: text.substring(lastIndex) });
101
+ }
102
+ if (children.length === 0) {
103
+ children.push({ text: "" });
104
+ }
105
+ return [{ type: "paragraph", children }];
106
+ }
107
+ var TagElementRenderer = ({
108
+ attributes,
109
+ children,
110
+ element
111
+ }) => {
112
+ return /* @__PURE__ */ jsxs(
113
+ "span",
114
+ {
115
+ ...attributes,
116
+ contentEditable: false,
117
+ className: cn(
118
+ "inline-flex items-center",
119
+ "px-1.5 py-0.5 mx-0.5",
120
+ "rounded-md border",
121
+ "bg-accent/20 text-accent border-accent/40",
122
+ "text-sm font-medium",
123
+ "select-none cursor-default"
124
+ ),
125
+ children: [
126
+ /* @__PURE__ */ jsx("span", { className: "font-bold", children: "#" }),
127
+ element.tag,
128
+ children
129
+ ]
130
+ }
131
+ );
132
+ };
133
+ var DefaultElement = ({ attributes, children }) => {
134
+ return /* @__PURE__ */ jsx("p", { ...attributes, children });
135
+ };
136
+ var Leaf = ({ attributes, children }) => {
137
+ return /* @__PURE__ */ jsx("span", { ...attributes, children });
138
+ };
139
+ var SlateEditor = React.forwardRef(
140
+ ({
141
+ value = "",
142
+ onChange,
143
+ onTagCreate,
144
+ onTagDelete,
145
+ onSubmit,
146
+ placeholder = "Type something...",
147
+ disabled = false,
148
+ enableTags = true,
149
+ minRows = 3,
150
+ maxRows = 8,
151
+ className
152
+ }, ref) => {
153
+ const editor = React.useMemo(
154
+ () => withTags(withHistory(withReact(createEditor()))),
155
+ []
156
+ );
157
+ const [isCreatingTag, setIsCreatingTag] = React.useState(false);
158
+ const [tagStartPoint, setTagStartPoint] = React.useState(null);
159
+ const [currentTagText, setCurrentTagText] = React.useState("");
160
+ const [editorValue, setEditorValue] = React.useState(
161
+ () => deserializeFromText(value)
162
+ );
163
+ React.useEffect(() => {
164
+ const newValue = deserializeFromText(value);
165
+ const currentText = serializeToText(editorValue);
166
+ if (currentText !== value) {
167
+ setEditorValue(newValue);
168
+ Transforms.removeNodes(editor, { at: [] });
169
+ Transforms.insertNodes(editor, newValue, { at: [0] });
170
+ Transforms.select(editor, Editor.start(editor, []));
171
+ }
172
+ }, [value, editor]);
173
+ React.useImperativeHandle(
174
+ ref,
175
+ () => ({
176
+ focus: () => {
177
+ ReactEditor.focus(editor);
178
+ },
179
+ clear: () => {
180
+ const newValue = [
181
+ { type: "paragraph", children: [{ text: "" }] }
182
+ ];
183
+ setEditorValue(newValue);
184
+ Transforms.select(editor, Editor.start(editor, []));
185
+ },
186
+ getText: () => serializeToText(editorValue),
187
+ insertText: (text) => {
188
+ Transforms.insertText(editor, text);
189
+ },
190
+ insertTag: (tag) => {
191
+ insertTag(editor, tag);
192
+ onTagCreate?.(tag);
193
+ }
194
+ }),
195
+ [editor, editorValue, onTagCreate]
196
+ );
197
+ const handleChange = React.useCallback(
198
+ (newValue) => {
199
+ setEditorValue(newValue);
200
+ if (isCreatingTag && tagStartPoint) {
201
+ const { selection } = editor;
202
+ if (selection && Range.isCollapsed(selection)) {
203
+ const currentPath = selection.anchor.path;
204
+ const currentOffset = selection.anchor.offset;
205
+ if (Path.equals(currentPath, tagStartPoint.path)) {
206
+ try {
207
+ const [textNode] = Editor.node(editor, currentPath);
208
+ if (Text.isText(textNode)) {
209
+ const tagText = textNode.text.substring(
210
+ tagStartPoint.offset + 1,
211
+ currentOffset
212
+ );
213
+ setCurrentTagText(tagText);
214
+ }
215
+ } catch {
216
+ }
217
+ }
218
+ }
219
+ }
220
+ const oldTags = /* @__PURE__ */ new Set();
221
+ const newTags = /* @__PURE__ */ new Set();
222
+ for (const node of Node.descendants(editor, {
223
+ from: [],
224
+ pass: ([n]) => Element.isElement(n) && n.type === "paragraph"
225
+ })) {
226
+ const [n] = node;
227
+ if (Element.isElement(n) && n.type === "tag") {
228
+ oldTags.add(n.tag);
229
+ }
230
+ }
231
+ const extractTags = (nodes) => {
232
+ for (const node of nodes) {
233
+ if (Element.isElement(node)) {
234
+ if (node.type === "tag") {
235
+ newTags.add(node.tag);
236
+ }
237
+ if ("children" in node) {
238
+ extractTags(node.children);
239
+ }
240
+ }
241
+ }
242
+ };
243
+ extractTags(newValue);
244
+ for (const tag of oldTags) {
245
+ if (!newTags.has(tag)) {
246
+ onTagDelete?.(tag);
247
+ }
248
+ }
249
+ const text = serializeToText(newValue);
250
+ onChange?.(text);
251
+ },
252
+ [editor, onChange, onTagDelete, isCreatingTag, tagStartPoint]
253
+ );
254
+ const insertTag = (editor2, tag) => {
255
+ const tagNode = {
256
+ type: "tag",
257
+ tag,
258
+ children: [{ text: "" }]
259
+ };
260
+ Transforms.insertNodes(editor2, tagNode);
261
+ Transforms.move(editor2);
262
+ Transforms.insertText(editor2, " ");
263
+ };
264
+ const completeTag = React.useCallback(() => {
265
+ if (!isCreatingTag || !tagStartPoint) return;
266
+ const { selection } = editor;
267
+ if (!selection || !Range.isCollapsed(selection)) return;
268
+ const currentPath = selection.anchor.path;
269
+ const currentOffset = selection.anchor.offset;
270
+ if (!Path.equals(currentPath, tagStartPoint.path)) {
271
+ setIsCreatingTag(false);
272
+ setTagStartPoint(null);
273
+ setCurrentTagText("");
274
+ return;
275
+ }
276
+ const [textNode] = Editor.node(editor, currentPath);
277
+ if (!Text.isText(textNode)) {
278
+ setIsCreatingTag(false);
279
+ setTagStartPoint(null);
280
+ setCurrentTagText("");
281
+ return;
282
+ }
283
+ const tagText = textNode.text.substring(
284
+ tagStartPoint.offset + 1,
285
+ currentOffset
286
+ );
287
+ if (!tagText.trim()) {
288
+ setIsCreatingTag(false);
289
+ setTagStartPoint(null);
290
+ setCurrentTagText("");
291
+ return;
292
+ }
293
+ const start = { path: currentPath, offset: tagStartPoint.offset };
294
+ const end = { path: currentPath, offset: currentOffset };
295
+ Transforms.delete(editor, { at: { anchor: start, focus: end } });
296
+ insertTag(editor, tagText.trim());
297
+ onTagCreate?.(tagText.trim());
298
+ setIsCreatingTag(false);
299
+ setTagStartPoint(null);
300
+ setCurrentTagText("");
301
+ }, [editor, isCreatingTag, tagStartPoint, onTagCreate]);
302
+ const cancelTag = React.useCallback(() => {
303
+ setIsCreatingTag(false);
304
+ setTagStartPoint(null);
305
+ setCurrentTagText("");
306
+ }, []);
307
+ const handleKeyDown = React.useCallback(
308
+ (event) => {
309
+ if (isCreatingTag) {
310
+ if (event.key === "Enter" || event.key === " ") {
311
+ event.preventDefault();
312
+ completeTag();
313
+ return;
314
+ }
315
+ if (event.key === "Escape") {
316
+ event.preventDefault();
317
+ cancelTag();
318
+ return;
319
+ }
320
+ }
321
+ if (enableTags && event.key === "#" && !isCreatingTag) {
322
+ const { selection } = editor;
323
+ if (selection && Range.isCollapsed(selection)) {
324
+ setIsCreatingTag(true);
325
+ setCurrentTagText("");
326
+ setTagStartPoint({
327
+ path: selection.anchor.path,
328
+ offset: selection.anchor.offset
329
+ });
330
+ }
331
+ }
332
+ if (event.key === "Enter" && !event.shiftKey && !isCreatingTag) {
333
+ event.preventDefault();
334
+ const text = serializeToText(editorValue);
335
+ if (text.trim() && onSubmit) {
336
+ onSubmit(text.trim());
337
+ }
338
+ return;
339
+ }
340
+ if ((event.key === "Backspace" || event.key === "Delete") && event.ctrlKey) {
341
+ const { selection } = editor;
342
+ if (selection && Range.isCollapsed(selection)) {
343
+ const direction = event.key === "Backspace" ? "backward" : "forward";
344
+ const pointFn = direction === "backward" ? Editor.before : Editor.after;
345
+ const point = pointFn(editor, selection, { unit: "word" });
346
+ if (point) {
347
+ const [tagEntry] = Editor.nodes(editor, {
348
+ at: direction === "backward" ? { anchor: point, focus: selection.anchor } : { anchor: selection.anchor, focus: point },
349
+ match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.type === "tag"
350
+ });
351
+ if (tagEntry) {
352
+ event.preventDefault();
353
+ const [tagNode, tagPath] = tagEntry;
354
+ if (Element.isElement(tagNode) && tagNode.type === "tag") {
355
+ onTagDelete?.(tagNode.tag);
356
+ }
357
+ Transforms.removeNodes(editor, { at: tagPath });
358
+ return;
359
+ }
360
+ }
361
+ }
362
+ }
363
+ },
364
+ [
365
+ editor,
366
+ editorValue,
367
+ isCreatingTag,
368
+ enableTags,
369
+ completeTag,
370
+ cancelTag,
371
+ onSubmit,
372
+ onTagDelete
373
+ ]
374
+ );
375
+ const renderElement = React.useCallback((props) => {
376
+ switch (props.element.type) {
377
+ case "tag":
378
+ return /* @__PURE__ */ jsx(
379
+ TagElementRenderer,
380
+ {
381
+ ...props,
382
+ element: props.element
383
+ }
384
+ );
385
+ default:
386
+ return /* @__PURE__ */ jsx(DefaultElement, { ...props });
387
+ }
388
+ }, []);
389
+ const renderLeaf = React.useCallback((props) => {
390
+ return /* @__PURE__ */ jsx(Leaf, { ...props });
391
+ }, []);
392
+ const lineHeight = 24;
393
+ const minHeight = lineHeight * minRows;
394
+ const maxHeight = lineHeight * maxRows;
395
+ return /* @__PURE__ */ jsxs(
396
+ "div",
397
+ {
398
+ className: cn(
399
+ "relative w-full rounded-md",
400
+ "bg-transparent",
401
+ disabled && "opacity-50 cursor-not-allowed",
402
+ className
403
+ ),
404
+ children: [
405
+ /* @__PURE__ */ jsx(
406
+ Slate,
407
+ {
408
+ editor,
409
+ initialValue: editorValue,
410
+ onChange: handleChange,
411
+ children: /* @__PURE__ */ jsx(
412
+ Editable,
413
+ {
414
+ readOnly: disabled,
415
+ placeholder,
416
+ renderElement,
417
+ renderLeaf,
418
+ onKeyDown: handleKeyDown,
419
+ className: cn(
420
+ "w-full outline-none",
421
+ "text-foreground placeholder:text-muted-foreground",
422
+ "leading-6",
423
+ "overflow-y-auto overflow-x-hidden",
424
+ "whitespace-pre-wrap break-words"
425
+ ),
426
+ style: {
427
+ minHeight: `${minHeight}px`,
428
+ maxHeight: `${maxHeight}px`,
429
+ wordBreak: "break-word",
430
+ overflowWrap: "break-word"
431
+ },
432
+ spellCheck: true,
433
+ autoCorrect: "off",
434
+ autoCapitalize: "off"
435
+ }
436
+ )
437
+ }
438
+ ),
439
+ isCreatingTag && /* @__PURE__ */ jsxs("div", { className: "absolute bottom-full left-0 mb-2 flex items-center gap-2.5 animate-in fade-in-0 slide-in-from-bottom-1 duration-150", children: [
440
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center px-2 py-1 rounded-md border shadow-sm bg-accent/20 border-accent/40", children: [
441
+ /* @__PURE__ */ jsx("span", { className: "font-bold text-sm text-accent", children: "#" }),
442
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium min-w-[2ch] text-accent", children: currentTagText || /* @__PURE__ */ jsx("span", { className: "opacity-50 italic", children: "tag" }) })
443
+ ] }),
444
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground/80 text-[11px]", children: [
445
+ /* @__PURE__ */ jsx("kbd", { className: "px-1.5 py-0.5 bg-muted rounded text-[10px] font-mono mr-1", children: "Enter" }),
446
+ "or",
447
+ /* @__PURE__ */ jsx("kbd", { className: "px-1.5 py-0.5 bg-muted rounded text-[10px] font-mono mx-1", children: "Space" }),
448
+ "to add",
449
+ /* @__PURE__ */ jsx("span", { className: "mx-1.5 text-muted-foreground/50", children: "\xB7" }),
450
+ /* @__PURE__ */ jsx("kbd", { className: "px-1.5 py-0.5 bg-muted rounded text-[10px] font-mono mr-1", children: "Esc" }),
451
+ "to cancel"
452
+ ] })
453
+ ] })
454
+ ]
455
+ }
456
+ );
457
+ }
458
+ );
459
+ SlateEditor.displayName = "SlateEditor";
460
+
461
+ export { SlateEditor, deserializeFromText, serializeToText };
462
+ //# sourceMappingURL=index.js.map
463
+ //# sourceMappingURL=index.js.map