@owomark/react 0.1.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.
- package/README.md +363 -0
- package/dist/index.css +32 -0
- package/dist/index.d.ts +249 -0
- package/dist/index.js +908 -0
- package/package.json +52 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,908 @@
|
|
|
1
|
+
// src/OwoMarkEditor.tsx
|
|
2
|
+
import {
|
|
3
|
+
useRef as useRef3,
|
|
4
|
+
useEffect as useEffect2,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
forwardRef,
|
|
7
|
+
useCallback
|
|
8
|
+
} from "react";
|
|
9
|
+
import { getThemeClassName } from "@owomark/view";
|
|
10
|
+
|
|
11
|
+
// src/editor/EditorBlock.tsx
|
|
12
|
+
import { memo as memo3, useMemo } from "react";
|
|
13
|
+
import { BLOCK_TYPE_TO_CLASS, BQ_STEP, buildBlockquoteBarsBoxShadow } from "@owomark/core";
|
|
14
|
+
|
|
15
|
+
// src/editor/EditorDecorator.tsx
|
|
16
|
+
import { memo as memo2 } from "react";
|
|
17
|
+
import { TOKEN_TO_CLASS } from "@owomark/core";
|
|
18
|
+
|
|
19
|
+
// src/editor/EditorLeaf.tsx
|
|
20
|
+
import { memo } from "react";
|
|
21
|
+
import { jsx } from "react/jsx-runtime";
|
|
22
|
+
var EditorLeaf = memo(function EditorLeaf2({ text }) {
|
|
23
|
+
return /* @__PURE__ */ jsx("span", { "data-owo-leaf": "true", children: text });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// src/editor/EditorDecorator.tsx
|
|
27
|
+
import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
|
|
28
|
+
var EditorDecorator = memo2(function EditorDecorator2({ decorator }) {
|
|
29
|
+
const cls = TOKEN_TO_CLASS[decorator.type] || "";
|
|
30
|
+
const leaves = decorator.leaves.map((leaf, i) => /* @__PURE__ */ jsx2(EditorLeaf, { text: leaf.text }, i));
|
|
31
|
+
if (cls) {
|
|
32
|
+
return /* @__PURE__ */ jsx2("span", { className: cls, "data-owo-token": decorator.type, children: leaves });
|
|
33
|
+
}
|
|
34
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: leaves });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// src/editor/EditorBlock.tsx
|
|
38
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
39
|
+
function buildBlockquoteBarsStyle(depth) {
|
|
40
|
+
const shadow = buildBlockquoteBarsBoxShadow(depth);
|
|
41
|
+
if (!shadow) return void 0;
|
|
42
|
+
return {
|
|
43
|
+
paddingLeft: `${depth * BQ_STEP}px`,
|
|
44
|
+
boxShadow: shadow
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
var EditorBlock = memo3(
|
|
48
|
+
function EditorBlock2({ block, blockIndex }) {
|
|
49
|
+
const className = BLOCK_TYPE_TO_CLASS[block.type] || "owo-block-paragraph";
|
|
50
|
+
const headingLevel = block.type === "heading" ? block.headingLevel : void 0;
|
|
51
|
+
const bqStyle = useMemo(
|
|
52
|
+
() => block.type === "blockquote" ? buildBlockquoteBarsStyle(block.depth) : void 0,
|
|
53
|
+
[block.type, block.depth]
|
|
54
|
+
);
|
|
55
|
+
const hasContent = block.decorators.length > 0 && block.decorators.some((d) => d.leaves.some((l) => l.text.length > 0));
|
|
56
|
+
return /* @__PURE__ */ jsx3(
|
|
57
|
+
"div",
|
|
58
|
+
{
|
|
59
|
+
"data-owo-block": blockIndex,
|
|
60
|
+
"data-block-id": block.id,
|
|
61
|
+
className,
|
|
62
|
+
style: bqStyle,
|
|
63
|
+
...headingLevel ? { "data-owo-heading": headingLevel } : {},
|
|
64
|
+
children: hasContent ? block.decorators.map((dec, i) => /* @__PURE__ */ jsx3(EditorDecorator, { decorator: dec }, i)) : /* @__PURE__ */ jsx3("br", {})
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
},
|
|
68
|
+
(prev, next) => prev.block === next.block && prev.blockIndex === next.blockIndex
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// src/slash/SlashMenu.tsx
|
|
72
|
+
import {
|
|
73
|
+
useRef,
|
|
74
|
+
useEffect,
|
|
75
|
+
useLayoutEffect,
|
|
76
|
+
useState,
|
|
77
|
+
memo as memo4
|
|
78
|
+
} from "react";
|
|
79
|
+
|
|
80
|
+
// src/slash/caret-position.ts
|
|
81
|
+
var MENU_GAP = 4;
|
|
82
|
+
function getCaretRect() {
|
|
83
|
+
const sel = window.getSelection();
|
|
84
|
+
if (!sel || sel.rangeCount === 0) return null;
|
|
85
|
+
const range = sel.getRangeAt(0).cloneRange();
|
|
86
|
+
range.collapse(true);
|
|
87
|
+
const rect = range.getBoundingClientRect();
|
|
88
|
+
if (rect.width === 0 && rect.height === 0 && rect.top === 0) return null;
|
|
89
|
+
return { top: rect.top, left: rect.left, bottom: rect.bottom };
|
|
90
|
+
}
|
|
91
|
+
function computeMenuPosition(caret, menuHeight, menuWidth) {
|
|
92
|
+
const viewportHeight = window.innerHeight;
|
|
93
|
+
const viewportWidth = window.innerWidth;
|
|
94
|
+
let top;
|
|
95
|
+
if (caret.bottom + MENU_GAP + menuHeight <= viewportHeight) {
|
|
96
|
+
top = caret.bottom + MENU_GAP;
|
|
97
|
+
} else {
|
|
98
|
+
top = caret.top - MENU_GAP - menuHeight;
|
|
99
|
+
}
|
|
100
|
+
let left = caret.left;
|
|
101
|
+
if (left + menuWidth > viewportWidth - 8) {
|
|
102
|
+
left = viewportWidth - menuWidth - 8;
|
|
103
|
+
}
|
|
104
|
+
if (left < 8) left = 8;
|
|
105
|
+
return { top: Math.max(0, top), left };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/slash/SlashMenu.tsx
|
|
109
|
+
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
110
|
+
var MENU_MAX_HEIGHT = 320;
|
|
111
|
+
var MENU_WIDTH = 280;
|
|
112
|
+
var SlashMenu = memo4(function SlashMenu2({
|
|
113
|
+
state,
|
|
114
|
+
onSelect,
|
|
115
|
+
onDismiss
|
|
116
|
+
}) {
|
|
117
|
+
const menuRef = useRef(null);
|
|
118
|
+
const [position, setPosition] = useState(null);
|
|
119
|
+
useLayoutEffect(() => {
|
|
120
|
+
if (!state.active && !state.pending) {
|
|
121
|
+
setPosition(null);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
let frameId = 0;
|
|
125
|
+
const updatePosition = () => {
|
|
126
|
+
const caret = getCaretRect();
|
|
127
|
+
if (!caret) return false;
|
|
128
|
+
setPosition(computeMenuPosition(caret, MENU_MAX_HEIGHT, MENU_WIDTH));
|
|
129
|
+
return true;
|
|
130
|
+
};
|
|
131
|
+
if (!updatePosition()) {
|
|
132
|
+
frameId = window.requestAnimationFrame(() => {
|
|
133
|
+
updatePosition();
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return () => {
|
|
137
|
+
if (frameId) {
|
|
138
|
+
window.cancelAnimationFrame(frameId);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}, [state.active, state.pending, state.query]);
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (!state.active && !state.pending || !menuRef.current) return;
|
|
144
|
+
const selected = menuRef.current.querySelector('[data-selected="true"]');
|
|
145
|
+
if (selected) {
|
|
146
|
+
selected.scrollIntoView({ block: "nearest" });
|
|
147
|
+
}
|
|
148
|
+
}, [state.active, state.pending, state.selectedIndex]);
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
if (!state.active) return;
|
|
151
|
+
function handlePointerDown(e) {
|
|
152
|
+
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
153
|
+
onDismiss();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
document.addEventListener("pointerdown", handlePointerDown, true);
|
|
157
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown, true);
|
|
158
|
+
}, [state.active, onDismiss]);
|
|
159
|
+
if (!state.active && !state.pending || !position) return null;
|
|
160
|
+
const groups = groupByCategory(state.filteredCommands);
|
|
161
|
+
return /* @__PURE__ */ jsxs(
|
|
162
|
+
"div",
|
|
163
|
+
{
|
|
164
|
+
ref: menuRef,
|
|
165
|
+
className: "owo-slash-menu",
|
|
166
|
+
role: "listbox",
|
|
167
|
+
style: {
|
|
168
|
+
position: "fixed",
|
|
169
|
+
top: position.top,
|
|
170
|
+
left: position.left,
|
|
171
|
+
width: MENU_WIDTH,
|
|
172
|
+
maxHeight: MENU_MAX_HEIGHT,
|
|
173
|
+
overflow: "auto",
|
|
174
|
+
zIndex: 9999
|
|
175
|
+
},
|
|
176
|
+
children: [
|
|
177
|
+
state.pending && /* @__PURE__ */ jsx4("div", { className: "owo-slash-menu__pending", children: "Executing..." }),
|
|
178
|
+
groups.map(({ category, commands }) => /* @__PURE__ */ jsxs("div", { className: "owo-slash-menu__group", children: [
|
|
179
|
+
category && /* @__PURE__ */ jsx4("div", { className: "owo-slash-menu__category", children: category }),
|
|
180
|
+
commands.map((cmd) => {
|
|
181
|
+
const globalIndex = state.filteredCommands.indexOf(cmd);
|
|
182
|
+
const isSelected = globalIndex === state.selectedIndex;
|
|
183
|
+
return /* @__PURE__ */ jsxs(
|
|
184
|
+
"div",
|
|
185
|
+
{
|
|
186
|
+
className: `owo-slash-menu__item${isSelected ? " owo-slash-menu__item--selected" : ""}`,
|
|
187
|
+
role: "option",
|
|
188
|
+
"aria-selected": isSelected,
|
|
189
|
+
"data-selected": isSelected ? "true" : void 0,
|
|
190
|
+
onPointerDown: (e) => {
|
|
191
|
+
e.preventDefault();
|
|
192
|
+
onSelect(globalIndex);
|
|
193
|
+
},
|
|
194
|
+
children: [
|
|
195
|
+
cmd.icon && /* @__PURE__ */ jsx4("span", { className: "owo-slash-menu__icon", children: cmd.icon }),
|
|
196
|
+
/* @__PURE__ */ jsxs("div", { className: "owo-slash-menu__content", children: [
|
|
197
|
+
/* @__PURE__ */ jsx4("span", { className: "owo-slash-menu__label", children: cmd.label }),
|
|
198
|
+
cmd.shortcut && /* @__PURE__ */ jsx4("span", { className: "owo-slash-menu__shortcut", children: cmd.shortcut }),
|
|
199
|
+
cmd.description && /* @__PURE__ */ jsx4("span", { className: "owo-slash-menu__desc", children: cmd.description })
|
|
200
|
+
] })
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
cmd.id
|
|
204
|
+
);
|
|
205
|
+
})
|
|
206
|
+
] }, category))
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
});
|
|
211
|
+
function groupByCategory(commands) {
|
|
212
|
+
const groups = [];
|
|
213
|
+
const seen = /* @__PURE__ */ new Map();
|
|
214
|
+
for (const cmd of commands) {
|
|
215
|
+
const cat = cmd.category ?? "";
|
|
216
|
+
let group = seen.get(cat);
|
|
217
|
+
if (!group) {
|
|
218
|
+
group = { category: cat, commands: [] };
|
|
219
|
+
seen.set(cat, group);
|
|
220
|
+
groups.push(group);
|
|
221
|
+
}
|
|
222
|
+
group.commands.push(cmd);
|
|
223
|
+
}
|
|
224
|
+
return groups;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/useOwoMarkCore.ts
|
|
228
|
+
import {
|
|
229
|
+
useRef as useRef2,
|
|
230
|
+
useState as useState2,
|
|
231
|
+
useLayoutEffect as useLayoutEffect2
|
|
232
|
+
} from "react";
|
|
233
|
+
import {
|
|
234
|
+
createOwoMarkCore,
|
|
235
|
+
readSelection,
|
|
236
|
+
restoreSelection,
|
|
237
|
+
invalidateBlockCache,
|
|
238
|
+
virtualToLinear
|
|
239
|
+
} from "@owomark/core";
|
|
240
|
+
function useOwoMarkCore(options) {
|
|
241
|
+
const { initialMarkdown, readOnly = false } = options;
|
|
242
|
+
const containerRef = useRef2(null);
|
|
243
|
+
const coreRef = useRef2(null);
|
|
244
|
+
const composingRef = useRef2(false);
|
|
245
|
+
const pendingSelectionRef = useRef2(null);
|
|
246
|
+
const onCompositionStateChangeRef = useRef2(options.onCompositionStateChange);
|
|
247
|
+
onCompositionStateChangeRef.current = options.onCompositionStateChange;
|
|
248
|
+
if (!coreRef.current) {
|
|
249
|
+
coreRef.current = createOwoMarkCore({ initialMarkdown });
|
|
250
|
+
}
|
|
251
|
+
const core = coreRef.current;
|
|
252
|
+
const [doc, setDoc] = useState2(() => core.getDocument());
|
|
253
|
+
const [slashState, setSlashState] = useState2(() => core.getSlashState());
|
|
254
|
+
useLayoutEffect2(() => {
|
|
255
|
+
const el = containerRef.current;
|
|
256
|
+
if (!el) return;
|
|
257
|
+
const root = el;
|
|
258
|
+
root.contentEditable = readOnly ? "false" : "true";
|
|
259
|
+
root.setAttribute("role", "textbox");
|
|
260
|
+
root.setAttribute("aria-multiline", "true");
|
|
261
|
+
root.setAttribute("aria-label", "Markdown editor");
|
|
262
|
+
root.classList.add("owo-editor-root");
|
|
263
|
+
root.style.whiteSpace = "pre-wrap";
|
|
264
|
+
root.style.wordBreak = "break-word";
|
|
265
|
+
root.style.outline = "none";
|
|
266
|
+
const unsubDoc = core.onDocumentChange((newDoc, newSelection) => {
|
|
267
|
+
if (composingRef.current) return;
|
|
268
|
+
setDoc(newDoc);
|
|
269
|
+
pendingSelectionRef.current = newSelection;
|
|
270
|
+
});
|
|
271
|
+
const unsubComp = core.onCompositionStateChange((active) => {
|
|
272
|
+
composingRef.current = active;
|
|
273
|
+
onCompositionStateChangeRef.current?.(active);
|
|
274
|
+
});
|
|
275
|
+
const unsubSlash = core.onSlashStateChange((s) => setSlashState(s));
|
|
276
|
+
function syncEmptyAttr() {
|
|
277
|
+
const d = core.getDocument();
|
|
278
|
+
const empty = d.blocks.length === 0 || d.blocks.length === 1 && d.blocks[0].raw === "";
|
|
279
|
+
if (empty) root.setAttribute("data-owo-empty", "true");
|
|
280
|
+
else root.removeAttribute("data-owo-empty");
|
|
281
|
+
}
|
|
282
|
+
syncEmptyAttr();
|
|
283
|
+
let compositionJustEnded = false;
|
|
284
|
+
let pendingNativeSync = false;
|
|
285
|
+
function syncFromDOM() {
|
|
286
|
+
const parts = [];
|
|
287
|
+
for (const child of root.childNodes) {
|
|
288
|
+
if (child.nodeType === Node.ELEMENT_NODE) {
|
|
289
|
+
const el2 = child;
|
|
290
|
+
if (el2.hasAttribute("data-owo-block")) {
|
|
291
|
+
if (parts.length > 0) parts.push("\n");
|
|
292
|
+
parts.push(el2.textContent ?? "");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const textContent = parts.join("");
|
|
297
|
+
if (textContent !== core.getMarkdown()) {
|
|
298
|
+
const sel = readSelection(root);
|
|
299
|
+
core.syncText(textContent, sel);
|
|
300
|
+
syncEmptyAttr();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function onBeforeInput(e) {
|
|
304
|
+
if (core.getComposition().active) return;
|
|
305
|
+
const sel = readSelection(root);
|
|
306
|
+
const result = core.applyBeforeInput(
|
|
307
|
+
{ inputType: e.inputType, data: e.data },
|
|
308
|
+
sel
|
|
309
|
+
);
|
|
310
|
+
if (result.action === "handled") {
|
|
311
|
+
e.preventDefault();
|
|
312
|
+
syncEmptyAttr();
|
|
313
|
+
} else if (result.action === "allow-native") {
|
|
314
|
+
pendingNativeSync = true;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function onInput() {
|
|
318
|
+
if (core.getComposition().active) return;
|
|
319
|
+
if (compositionJustEnded) {
|
|
320
|
+
compositionJustEnded = false;
|
|
321
|
+
syncFromDOM();
|
|
322
|
+
} else if (pendingNativeSync) {
|
|
323
|
+
pendingNativeSync = false;
|
|
324
|
+
syncFromDOM();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function onKeyDown(e) {
|
|
328
|
+
if (core.getComposition().active) return;
|
|
329
|
+
const sel = readSelection(root);
|
|
330
|
+
const result = core.applyKeyDown(
|
|
331
|
+
{
|
|
332
|
+
key: e.key,
|
|
333
|
+
ctrlKey: e.ctrlKey,
|
|
334
|
+
metaKey: e.metaKey,
|
|
335
|
+
altKey: e.altKey,
|
|
336
|
+
shiftKey: e.shiftKey
|
|
337
|
+
},
|
|
338
|
+
sel
|
|
339
|
+
);
|
|
340
|
+
if (result.action === "handled") {
|
|
341
|
+
e.preventDefault();
|
|
342
|
+
syncEmptyAttr();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function onPaste(e) {
|
|
346
|
+
e.preventDefault();
|
|
347
|
+
const raw = e.clipboardData?.getData("text/plain") ?? "";
|
|
348
|
+
const sel = readSelection(root);
|
|
349
|
+
core.applyPaste({ text: raw }, sel);
|
|
350
|
+
syncEmptyAttr();
|
|
351
|
+
}
|
|
352
|
+
function onCompositionStart() {
|
|
353
|
+
const sel = readSelection(root);
|
|
354
|
+
core.handleCompositionStart(sel);
|
|
355
|
+
}
|
|
356
|
+
function onCompositionUpdate() {
|
|
357
|
+
core.handleCompositionUpdate();
|
|
358
|
+
}
|
|
359
|
+
function onCompositionEnd() {
|
|
360
|
+
const sel = readSelection(root);
|
|
361
|
+
core.handleCompositionEnd(sel);
|
|
362
|
+
compositionJustEnded = true;
|
|
363
|
+
requestAnimationFrame(() => {
|
|
364
|
+
if (compositionJustEnded) {
|
|
365
|
+
compositionJustEnded = false;
|
|
366
|
+
syncFromDOM();
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
function onSelectionChange() {
|
|
371
|
+
const sel = readSelection(root);
|
|
372
|
+
if (sel) core.updateSelection(sel);
|
|
373
|
+
}
|
|
374
|
+
root.addEventListener("beforeinput", onBeforeInput);
|
|
375
|
+
root.addEventListener("input", onInput);
|
|
376
|
+
root.addEventListener("compositionstart", onCompositionStart);
|
|
377
|
+
root.addEventListener("compositionupdate", onCompositionUpdate);
|
|
378
|
+
root.addEventListener("compositionend", onCompositionEnd);
|
|
379
|
+
root.addEventListener("keydown", onKeyDown);
|
|
380
|
+
root.addEventListener("paste", onPaste);
|
|
381
|
+
const onDocSelectionChange = () => onSelectionChange();
|
|
382
|
+
root.ownerDocument.addEventListener("selectionchange", onDocSelectionChange);
|
|
383
|
+
return () => {
|
|
384
|
+
unsubDoc();
|
|
385
|
+
unsubComp();
|
|
386
|
+
unsubSlash();
|
|
387
|
+
root.removeEventListener("beforeinput", onBeforeInput);
|
|
388
|
+
root.removeEventListener("input", onInput);
|
|
389
|
+
root.removeEventListener("compositionstart", onCompositionStart);
|
|
390
|
+
root.removeEventListener("compositionupdate", onCompositionUpdate);
|
|
391
|
+
root.removeEventListener("compositionend", onCompositionEnd);
|
|
392
|
+
root.removeEventListener("keydown", onKeyDown);
|
|
393
|
+
root.removeEventListener("paste", onPaste);
|
|
394
|
+
root.ownerDocument.removeEventListener("selectionchange", onDocSelectionChange);
|
|
395
|
+
core.destroy();
|
|
396
|
+
};
|
|
397
|
+
}, []);
|
|
398
|
+
useLayoutEffect2(() => {
|
|
399
|
+
const el = containerRef.current;
|
|
400
|
+
if (!el || !pendingSelectionRef.current) return;
|
|
401
|
+
const linearSel = virtualToLinear(doc, pendingSelectionRef.current);
|
|
402
|
+
invalidateBlockCache(el);
|
|
403
|
+
restoreSelection(el, linearSel);
|
|
404
|
+
pendingSelectionRef.current = null;
|
|
405
|
+
}, [doc]);
|
|
406
|
+
return { core, doc, slashState, containerRef };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// src/config.ts
|
|
410
|
+
var DEFAULT_EDITOR_CONFIG = {
|
|
411
|
+
indentMode: "auto",
|
|
412
|
+
enableSideAnnotation: true,
|
|
413
|
+
enableMath: true,
|
|
414
|
+
enableMarkdownSandbox: true,
|
|
415
|
+
markdownSandbox: {
|
|
416
|
+
outerFenceTicks: 4
|
|
417
|
+
},
|
|
418
|
+
theme: "light"
|
|
419
|
+
};
|
|
420
|
+
function resolveEditorConfig(config) {
|
|
421
|
+
return {
|
|
422
|
+
...DEFAULT_EDITOR_CONFIG,
|
|
423
|
+
...config,
|
|
424
|
+
markdownSandbox: {
|
|
425
|
+
...DEFAULT_EDITOR_CONFIG.markdownSandbox,
|
|
426
|
+
...config?.markdownSandbox
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function deriveProcessorOptions(config) {
|
|
431
|
+
return {
|
|
432
|
+
enableSideAnnotation: config.enableSideAnnotation,
|
|
433
|
+
enableMath: config.enableMath,
|
|
434
|
+
enableMarkdownSandbox: config.enableMarkdownSandbox,
|
|
435
|
+
markdownSandbox: config.markdownSandbox
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// src/OwoMarkEditor.tsx
|
|
440
|
+
import "@owomark/view/style.css";
|
|
441
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
442
|
+
var OwoMarkEditor = forwardRef(
|
|
443
|
+
function OwoMarkEditor2(props, ref) {
|
|
444
|
+
const {
|
|
445
|
+
value,
|
|
446
|
+
defaultValue,
|
|
447
|
+
onChange,
|
|
448
|
+
onSelectionChange,
|
|
449
|
+
onCompositionStateChange,
|
|
450
|
+
onScroll,
|
|
451
|
+
readOnly = false,
|
|
452
|
+
placeholder,
|
|
453
|
+
className,
|
|
454
|
+
theme: themeProp = "light",
|
|
455
|
+
themeClassName,
|
|
456
|
+
commandsRef,
|
|
457
|
+
coreRef: externalCoreRef,
|
|
458
|
+
controller,
|
|
459
|
+
indentMode: indentModeProp = "auto",
|
|
460
|
+
ariaLabel,
|
|
461
|
+
config: configProp
|
|
462
|
+
} = props;
|
|
463
|
+
const resolved = resolveEditorConfig(configProp);
|
|
464
|
+
const indentMode = configProp ? resolved.indentMode : indentModeProp;
|
|
465
|
+
const theme = configProp ? resolved.theme : themeProp;
|
|
466
|
+
const enableSideAnnotation = resolved.enableSideAnnotation;
|
|
467
|
+
const enableMath = resolved.enableMath;
|
|
468
|
+
const isControlled = value !== void 0;
|
|
469
|
+
const lastExternalValue = useRef3(value ?? defaultValue ?? "");
|
|
470
|
+
const suppressOnChange = useRef3(false);
|
|
471
|
+
const { core, doc, slashState, containerRef } = useOwoMarkCore({
|
|
472
|
+
initialMarkdown: lastExternalValue.current,
|
|
473
|
+
readOnly,
|
|
474
|
+
onCompositionStateChange
|
|
475
|
+
});
|
|
476
|
+
const handleSlashSelect = useCallback((index) => {
|
|
477
|
+
core.executeSlashCommand(index);
|
|
478
|
+
}, [core]);
|
|
479
|
+
const handleSlashDismiss = useCallback(() => {
|
|
480
|
+
core.dismissSlashMenu();
|
|
481
|
+
}, [core]);
|
|
482
|
+
useEffect2(() => {
|
|
483
|
+
if (typeof externalCoreRef === "function") externalCoreRef(core);
|
|
484
|
+
else if (externalCoreRef && typeof externalCoreRef === "object") {
|
|
485
|
+
externalCoreRef.current = core;
|
|
486
|
+
}
|
|
487
|
+
return () => {
|
|
488
|
+
if (typeof externalCoreRef === "function") externalCoreRef(null);
|
|
489
|
+
else if (externalCoreRef && typeof externalCoreRef === "object") {
|
|
490
|
+
externalCoreRef.current = null;
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
}, [core, externalCoreRef]);
|
|
494
|
+
useEffect2(() => {
|
|
495
|
+
const unsub = core.onChange((markdown) => {
|
|
496
|
+
if (suppressOnChange.current) return;
|
|
497
|
+
lastExternalValue.current = markdown;
|
|
498
|
+
onChange?.(markdown);
|
|
499
|
+
});
|
|
500
|
+
return unsub;
|
|
501
|
+
}, [core, onChange]);
|
|
502
|
+
useEffect2(() => {
|
|
503
|
+
if (!onSelectionChange) return;
|
|
504
|
+
return core.onSelectionChange(onSelectionChange);
|
|
505
|
+
}, [core, onSelectionChange]);
|
|
506
|
+
useEffect2(() => {
|
|
507
|
+
if (!controller) return;
|
|
508
|
+
return controller.connectEditor(core);
|
|
509
|
+
}, [core, controller]);
|
|
510
|
+
useEffect2(() => {
|
|
511
|
+
if (!isControlled) return;
|
|
512
|
+
if (value === lastExternalValue.current) return;
|
|
513
|
+
suppressOnChange.current = true;
|
|
514
|
+
core.setMarkdown(value);
|
|
515
|
+
lastExternalValue.current = value;
|
|
516
|
+
suppressOnChange.current = false;
|
|
517
|
+
}, [core, value, isControlled]);
|
|
518
|
+
useEffect2(() => {
|
|
519
|
+
core.setIndentMode(indentMode);
|
|
520
|
+
}, [core, indentMode]);
|
|
521
|
+
useEffect2(() => {
|
|
522
|
+
core.setEnableSideAnnotation(enableSideAnnotation);
|
|
523
|
+
}, [core, enableSideAnnotation]);
|
|
524
|
+
useEffect2(() => {
|
|
525
|
+
core.setEnableMath(enableMath);
|
|
526
|
+
}, [core, enableMath]);
|
|
527
|
+
useEffect2(() => {
|
|
528
|
+
if (containerRef.current && ariaLabel) {
|
|
529
|
+
containerRef.current.setAttribute("aria-label", ariaLabel);
|
|
530
|
+
}
|
|
531
|
+
}, [ariaLabel, containerRef]);
|
|
532
|
+
useEffect2(() => {
|
|
533
|
+
if (containerRef.current) {
|
|
534
|
+
containerRef.current.contentEditable = readOnly ? "false" : "true";
|
|
535
|
+
if (readOnly) {
|
|
536
|
+
containerRef.current.setAttribute("aria-readonly", "true");
|
|
537
|
+
} else {
|
|
538
|
+
containerRef.current.removeAttribute("aria-readonly");
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}, [readOnly, containerRef]);
|
|
542
|
+
useImperativeHandle(commandsRef, () => ({
|
|
543
|
+
focus: () => containerRef.current?.focus(),
|
|
544
|
+
undo: () => core.dispatch("undo"),
|
|
545
|
+
redo: () => core.dispatch("redo"),
|
|
546
|
+
toggleBold: () => core.dispatch("toggleBold"),
|
|
547
|
+
toggleItalic: () => core.dispatch("toggleItalic"),
|
|
548
|
+
insertLink: (url) => core.dispatch("insertLink", url),
|
|
549
|
+
insertCodeFence: (language) => core.dispatch("insertCodeFence", language),
|
|
550
|
+
insertMath: () => core.dispatch("insertMath"),
|
|
551
|
+
insertSideAnnotation: (type) => core.dispatch("insertSideAnnotation", type),
|
|
552
|
+
getMarkdown: () => core.getMarkdown(),
|
|
553
|
+
setMarkdown: (markdown) => core.setMarkdown(markdown),
|
|
554
|
+
replaceMarkdown: (markdown, selection) => core.replaceMarkdown(markdown, selection)
|
|
555
|
+
}), [core, containerRef]);
|
|
556
|
+
const themeClass = getThemeClassName(theme);
|
|
557
|
+
const combinedClassName = [
|
|
558
|
+
themeClass,
|
|
559
|
+
themeClassName,
|
|
560
|
+
className
|
|
561
|
+
].filter(Boolean).join(" ");
|
|
562
|
+
const setRefs = useCallback((el) => {
|
|
563
|
+
containerRef.current = el;
|
|
564
|
+
if (typeof ref === "function") ref(el);
|
|
565
|
+
else if (ref) ref.current = el;
|
|
566
|
+
}, [ref, containerRef]);
|
|
567
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
568
|
+
/* @__PURE__ */ jsx5(
|
|
569
|
+
"div",
|
|
570
|
+
{
|
|
571
|
+
ref: setRefs,
|
|
572
|
+
className: combinedClassName,
|
|
573
|
+
"data-placeholder": placeholder,
|
|
574
|
+
onScroll,
|
|
575
|
+
suppressContentEditableWarning: true,
|
|
576
|
+
children: doc.blocks.map((block, i) => /* @__PURE__ */ jsx5(EditorBlock, { block, blockIndex: i }, block.id))
|
|
577
|
+
}
|
|
578
|
+
),
|
|
579
|
+
/* @__PURE__ */ jsx5(
|
|
580
|
+
SlashMenu,
|
|
581
|
+
{
|
|
582
|
+
state: slashState,
|
|
583
|
+
onSelect: handleSlashSelect,
|
|
584
|
+
onDismiss: handleSlashDismiss
|
|
585
|
+
}
|
|
586
|
+
)
|
|
587
|
+
] });
|
|
588
|
+
}
|
|
589
|
+
);
|
|
590
|
+
|
|
591
|
+
// src/OwoMarkPreview.tsx
|
|
592
|
+
import { useRef as useRef4, useEffect as useEffect3 } from "react";
|
|
593
|
+
import {
|
|
594
|
+
createOwoMarkPreviewEngine
|
|
595
|
+
} from "@owomark/view";
|
|
596
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
597
|
+
function isController(store) {
|
|
598
|
+
return typeof store.updateVisibleBlockIds === "function";
|
|
599
|
+
}
|
|
600
|
+
var OwoMarkPreview = function OwoMarkPreview2(props) {
|
|
601
|
+
const {
|
|
602
|
+
state,
|
|
603
|
+
className,
|
|
604
|
+
strategy,
|
|
605
|
+
themeKey,
|
|
606
|
+
registry,
|
|
607
|
+
viewportFirst,
|
|
608
|
+
ariaLabel,
|
|
609
|
+
renderBlock,
|
|
610
|
+
onContentUpdate
|
|
611
|
+
} = props;
|
|
612
|
+
const onContentUpdateRef = useRef4(onContentUpdate);
|
|
613
|
+
onContentUpdateRef.current = onContentUpdate;
|
|
614
|
+
const renderBlockRef = useRef4(renderBlock);
|
|
615
|
+
renderBlockRef.current = renderBlock;
|
|
616
|
+
const hasRenderBlock = !!renderBlock;
|
|
617
|
+
const containerRef = useRef4(null);
|
|
618
|
+
const engineRef = useRef4(null);
|
|
619
|
+
useEffect3(() => {
|
|
620
|
+
if (!containerRef.current) return;
|
|
621
|
+
const engineOptions = {
|
|
622
|
+
strategy,
|
|
623
|
+
themeKey,
|
|
624
|
+
registry,
|
|
625
|
+
viewportFirst,
|
|
626
|
+
renderBlock: hasRenderBlock ? (block, ctx) => renderBlockRef.current(block, ctx) : void 0,
|
|
627
|
+
onContentUpdate: () => onContentUpdateRef.current?.()
|
|
628
|
+
};
|
|
629
|
+
const engine = createOwoMarkPreviewEngine(engineOptions);
|
|
630
|
+
engine.mount(containerRef.current);
|
|
631
|
+
engineRef.current = engine;
|
|
632
|
+
const currentState = state.getState();
|
|
633
|
+
void engine.update(currentState);
|
|
634
|
+
return () => {
|
|
635
|
+
engine.destroy();
|
|
636
|
+
engineRef.current = null;
|
|
637
|
+
};
|
|
638
|
+
}, [strategy, themeKey, registry, viewportFirst, hasRenderBlock]);
|
|
639
|
+
useEffect3(() => {
|
|
640
|
+
const unsub = state.subscribe((newState) => {
|
|
641
|
+
const engine = engineRef.current;
|
|
642
|
+
if (engine) {
|
|
643
|
+
void engine.update(newState);
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
return unsub;
|
|
647
|
+
}, [state]);
|
|
648
|
+
useEffect3(() => {
|
|
649
|
+
const container = containerRef.current;
|
|
650
|
+
if (!viewportFirst || !container || !isController(state)) return;
|
|
651
|
+
const controller = state;
|
|
652
|
+
const observer = new IntersectionObserver(
|
|
653
|
+
(entries) => {
|
|
654
|
+
const visible = /* @__PURE__ */ new Set();
|
|
655
|
+
for (const id of controller.getState().visibleBlockIds) {
|
|
656
|
+
visible.add(id);
|
|
657
|
+
}
|
|
658
|
+
for (const entry of entries) {
|
|
659
|
+
const blockId = entry.target.getAttribute("data-block-id");
|
|
660
|
+
if (!blockId) continue;
|
|
661
|
+
if (entry.isIntersecting) {
|
|
662
|
+
visible.add(blockId);
|
|
663
|
+
} else {
|
|
664
|
+
visible.delete(blockId);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
controller.updateVisibleBlockIds([...visible]);
|
|
668
|
+
},
|
|
669
|
+
{ root: container, rootMargin: "200px 0px" }
|
|
670
|
+
);
|
|
671
|
+
const wrappers = container.querySelectorAll("[data-block-id]");
|
|
672
|
+
for (const wrapper of wrappers) {
|
|
673
|
+
observer.observe(wrapper);
|
|
674
|
+
}
|
|
675
|
+
const mutation = new MutationObserver((mutations) => {
|
|
676
|
+
for (const m of mutations) {
|
|
677
|
+
for (const node of m.addedNodes) {
|
|
678
|
+
if (node instanceof HTMLElement && node.hasAttribute("data-block-id")) {
|
|
679
|
+
observer.observe(node);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
mutation.observe(container, { childList: true, subtree: true });
|
|
685
|
+
return () => {
|
|
686
|
+
observer.disconnect();
|
|
687
|
+
mutation.disconnect();
|
|
688
|
+
};
|
|
689
|
+
}, [viewportFirst, state]);
|
|
690
|
+
return /* @__PURE__ */ jsx6(
|
|
691
|
+
"div",
|
|
692
|
+
{
|
|
693
|
+
ref: containerRef,
|
|
694
|
+
className,
|
|
695
|
+
role: "document",
|
|
696
|
+
"aria-label": ariaLabel ?? "Preview",
|
|
697
|
+
"aria-live": "polite"
|
|
698
|
+
}
|
|
699
|
+
);
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
// src/useOwoMarkSharedState.ts
|
|
703
|
+
import { useRef as useRef5, useSyncExternalStore, useCallback as useCallback2 } from "react";
|
|
704
|
+
import {
|
|
705
|
+
createSharedStateStore
|
|
706
|
+
} from "@owomark/core";
|
|
707
|
+
function useOwoMarkSharedState(options) {
|
|
708
|
+
const controllerRef = useRef5(null);
|
|
709
|
+
if (!controllerRef.current) {
|
|
710
|
+
controllerRef.current = createSharedStateStore(options);
|
|
711
|
+
}
|
|
712
|
+
return controllerRef.current;
|
|
713
|
+
}
|
|
714
|
+
function useSharedStateSnapshot(controller) {
|
|
715
|
+
const subscribe = useCallback2(
|
|
716
|
+
(onStoreChange) => controller.subscribe(onStoreChange),
|
|
717
|
+
[controller]
|
|
718
|
+
);
|
|
719
|
+
const getSnapshot = useCallback2(() => controller.getState(), [controller]);
|
|
720
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// src/useBlockContext.ts
|
|
724
|
+
import { useState as useState3, useEffect as useEffect5 } from "react";
|
|
725
|
+
function useBlockContext(core) {
|
|
726
|
+
const [blockContext, setBlockContext] = useState3(
|
|
727
|
+
() => core.getBlockContext()
|
|
728
|
+
);
|
|
729
|
+
useEffect5(() => {
|
|
730
|
+
setBlockContext(core.getBlockContext());
|
|
731
|
+
return core.onBlockContextChange((ctx) => {
|
|
732
|
+
setBlockContext(ctx);
|
|
733
|
+
});
|
|
734
|
+
}, [core]);
|
|
735
|
+
return blockContext;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// src/index.ts
|
|
739
|
+
import { getThemeClassName as getThemeClassName2, THEME_LIGHT_CLASS, THEME_DARK_CLASS } from "@owomark/view";
|
|
740
|
+
|
|
741
|
+
// src/useVirtualList.ts
|
|
742
|
+
import { useState as useState4, useCallback as useCallback3, useRef as useRef6, useEffect as useEffect6, useMemo as useMemo2 } from "react";
|
|
743
|
+
import {
|
|
744
|
+
buildVirtualRows,
|
|
745
|
+
computeVisibleRange
|
|
746
|
+
} from "@owomark/core";
|
|
747
|
+
function useVirtualList(options) {
|
|
748
|
+
const { blocks, containerRef, overscan = 5, charsPerLine = 80 } = options;
|
|
749
|
+
const heightCacheRef = useRef6(/* @__PURE__ */ new Map());
|
|
750
|
+
const [scrollTop, setScrollTop] = useState4(0);
|
|
751
|
+
const [viewportHeight, setViewportHeight] = useState4(0);
|
|
752
|
+
const [heightCacheVersion, setHeightCacheVersion] = useState4(0);
|
|
753
|
+
const rafRef = useRef6(0);
|
|
754
|
+
const [container, setContainer] = useState4(null);
|
|
755
|
+
useEffect6(() => {
|
|
756
|
+
setContainer(containerRef.current);
|
|
757
|
+
});
|
|
758
|
+
useEffect6(() => {
|
|
759
|
+
if (!container) return;
|
|
760
|
+
setViewportHeight(container.clientHeight);
|
|
761
|
+
const ro = new ResizeObserver((entries) => {
|
|
762
|
+
for (const entry of entries) {
|
|
763
|
+
setViewportHeight(entry.contentRect.height);
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
ro.observe(container);
|
|
767
|
+
return () => ro.disconnect();
|
|
768
|
+
}, [container]);
|
|
769
|
+
useEffect6(() => {
|
|
770
|
+
if (!container) return;
|
|
771
|
+
const onScroll = () => {
|
|
772
|
+
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
773
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
774
|
+
setScrollTop(container.scrollTop);
|
|
775
|
+
});
|
|
776
|
+
};
|
|
777
|
+
container.addEventListener("scroll", onScroll, { passive: true });
|
|
778
|
+
return () => {
|
|
779
|
+
container.removeEventListener("scroll", onScroll);
|
|
780
|
+
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
781
|
+
};
|
|
782
|
+
}, [container]);
|
|
783
|
+
const rows = useMemo2(
|
|
784
|
+
() => buildVirtualRows(blocks, heightCacheRef.current, charsPerLine),
|
|
785
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
786
|
+
[blocks, charsPerLine, heightCacheVersion]
|
|
787
|
+
);
|
|
788
|
+
const visibleRange = useMemo2(
|
|
789
|
+
() => computeVisibleRange(rows, scrollTop, viewportHeight, overscan),
|
|
790
|
+
[rows, scrollTop, viewportHeight, overscan]
|
|
791
|
+
);
|
|
792
|
+
const updateBlockHeight = useCallback3((blockId, height) => {
|
|
793
|
+
const cache = heightCacheRef.current;
|
|
794
|
+
if (cache.get(blockId) !== height) {
|
|
795
|
+
cache.set(blockId, height);
|
|
796
|
+
setHeightCacheVersion((v) => v + 1);
|
|
797
|
+
}
|
|
798
|
+
}, []);
|
|
799
|
+
const isBlockVisible = useCallback3(
|
|
800
|
+
(blockIndex) => blockIndex >= visibleRange.startIndex && blockIndex <= visibleRange.endIndex,
|
|
801
|
+
[visibleRange]
|
|
802
|
+
);
|
|
803
|
+
const invalidateAllHeights = useCallback3(() => {
|
|
804
|
+
heightCacheRef.current.clear();
|
|
805
|
+
setHeightCacheVersion((v) => v + 1);
|
|
806
|
+
}, []);
|
|
807
|
+
return {
|
|
808
|
+
visibleRange,
|
|
809
|
+
rows,
|
|
810
|
+
totalHeight: visibleRange.totalHeight,
|
|
811
|
+
updateBlockHeight,
|
|
812
|
+
isBlockVisible,
|
|
813
|
+
invalidateAllHeights
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// src/MdxSkeleton.tsx
|
|
818
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
819
|
+
function MdxSkeleton({ height, lines = 3, className, style }) {
|
|
820
|
+
const combinedStyle = {
|
|
821
|
+
...style,
|
|
822
|
+
...height != null ? { height, minHeight: height } : {}
|
|
823
|
+
};
|
|
824
|
+
return /* @__PURE__ */ jsx7(
|
|
825
|
+
"div",
|
|
826
|
+
{
|
|
827
|
+
className: `owo-mdx-skeleton-block ${className ?? ""}`,
|
|
828
|
+
style: combinedStyle,
|
|
829
|
+
"aria-hidden": "true",
|
|
830
|
+
children: Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx7("div", { className: "owo-mdx-skeleton owo-mdx-skeleton-line" }, i))
|
|
831
|
+
}
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// src/MdxComponentShell.tsx
|
|
836
|
+
import {
|
|
837
|
+
useRef as useRef7,
|
|
838
|
+
useState as useState5,
|
|
839
|
+
useEffect as useEffect7,
|
|
840
|
+
useCallback as useCallback4
|
|
841
|
+
} from "react";
|
|
842
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
843
|
+
var componentHeightCache = /* @__PURE__ */ new Map();
|
|
844
|
+
var DEFAULT_ESTIMATED_HEIGHT = 80;
|
|
845
|
+
function MdxComponentShell({ name, children }) {
|
|
846
|
+
const containerRef = useRef7(null);
|
|
847
|
+
const [ready, setReady] = useState5(false);
|
|
848
|
+
const estimatedHeight = componentHeightCache.get(name) ?? DEFAULT_ESTIMATED_HEIGHT;
|
|
849
|
+
useEffect7(() => {
|
|
850
|
+
const id = requestAnimationFrame(() => setReady(true));
|
|
851
|
+
return () => cancelAnimationFrame(id);
|
|
852
|
+
}, []);
|
|
853
|
+
const measureHeight = useCallback4(() => {
|
|
854
|
+
const el = containerRef.current;
|
|
855
|
+
if (!el) return;
|
|
856
|
+
const h = el.getBoundingClientRect().height;
|
|
857
|
+
if (h > 0) {
|
|
858
|
+
componentHeightCache.set(name, h);
|
|
859
|
+
}
|
|
860
|
+
}, [name]);
|
|
861
|
+
useEffect7(() => {
|
|
862
|
+
const el = containerRef.current;
|
|
863
|
+
if (!el || !ready) return;
|
|
864
|
+
measureHeight();
|
|
865
|
+
const ro = new ResizeObserver(() => measureHeight());
|
|
866
|
+
ro.observe(el);
|
|
867
|
+
return () => ro.disconnect();
|
|
868
|
+
}, [ready, measureHeight]);
|
|
869
|
+
if (!ready) {
|
|
870
|
+
return /* @__PURE__ */ jsx8(MdxSkeleton, { height: estimatedHeight });
|
|
871
|
+
}
|
|
872
|
+
return /* @__PURE__ */ jsx8("div", { ref: containerRef, children });
|
|
873
|
+
}
|
|
874
|
+
function wrapWithShell(name, Component) {
|
|
875
|
+
function ShellWrapped(props) {
|
|
876
|
+
return /* @__PURE__ */ jsx8(MdxComponentShell, { name, children: /* @__PURE__ */ jsx8(Component, { ...props }) });
|
|
877
|
+
}
|
|
878
|
+
ShellWrapped.displayName = `MdxShell(${name})`;
|
|
879
|
+
return ShellWrapped;
|
|
880
|
+
}
|
|
881
|
+
function clearComponentHeightCache() {
|
|
882
|
+
componentHeightCache.clear();
|
|
883
|
+
}
|
|
884
|
+
export {
|
|
885
|
+
DEFAULT_EDITOR_CONFIG,
|
|
886
|
+
EditorBlock,
|
|
887
|
+
EditorDecorator,
|
|
888
|
+
EditorLeaf,
|
|
889
|
+
MdxComponentShell,
|
|
890
|
+
MdxSkeleton,
|
|
891
|
+
OwoMarkEditor,
|
|
892
|
+
OwoMarkPreview,
|
|
893
|
+
SlashMenu,
|
|
894
|
+
THEME_DARK_CLASS,
|
|
895
|
+
THEME_LIGHT_CLASS,
|
|
896
|
+
clearComponentHeightCache,
|
|
897
|
+
computeMenuPosition,
|
|
898
|
+
deriveProcessorOptions,
|
|
899
|
+
getCaretRect,
|
|
900
|
+
getThemeClassName2 as getThemeClassName,
|
|
901
|
+
resolveEditorConfig,
|
|
902
|
+
useBlockContext,
|
|
903
|
+
useOwoMarkCore,
|
|
904
|
+
useOwoMarkSharedState,
|
|
905
|
+
useSharedStateSnapshot,
|
|
906
|
+
useVirtualList,
|
|
907
|
+
wrapWithShell
|
|
908
|
+
};
|