@owomark/react 0.1.6 → 0.1.7
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 +9 -2
- package/dist/.build-manifest.json +58 -0
- package/dist/highlight.worker.js +113 -0
- package/dist/index.d.ts +109 -16
- package/dist/index.js +1076 -268
- package/dist/mdx.worker.js +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
// src/OwoMarkEditor.tsx
|
|
2
2
|
import {
|
|
3
|
-
useRef as
|
|
4
|
-
useEffect as
|
|
3
|
+
useRef as useRef4,
|
|
4
|
+
useEffect as useEffect3,
|
|
5
5
|
useImperativeHandle,
|
|
6
6
|
forwardRef,
|
|
7
|
-
useCallback
|
|
7
|
+
useCallback as useCallback2
|
|
8
8
|
} from "react";
|
|
9
9
|
import { getThemeClassName } from "@owomark/view";
|
|
10
10
|
|
|
11
11
|
// src/editor/EditorBlock.tsx
|
|
12
12
|
import { memo as memo3, useMemo } from "react";
|
|
13
|
-
import { BLOCK_TYPE_TO_CLASS, BQ_STEP, buildBlockquoteBarsBoxShadow } from "@owomark/core";
|
|
13
|
+
import { BLOCK_TYPE_TO_CLASS, BQ_STEP, buildBlockquoteBarsBoxShadow } from "@owomark/core/browser";
|
|
14
14
|
|
|
15
15
|
// src/editor/EditorDecorator.tsx
|
|
16
16
|
import { memo as memo2 } from "react";
|
|
17
|
-
import { TOKEN_TO_CLASS } from "@owomark/core";
|
|
17
|
+
import { TOKEN_TO_CLASS } from "@owomark/core/browser";
|
|
18
18
|
|
|
19
19
|
// src/editor/EditorLeaf.tsx
|
|
20
20
|
import { memo } from "react";
|
|
@@ -58,6 +58,9 @@ var EditorBlock = memo3(
|
|
|
58
58
|
{
|
|
59
59
|
"data-owo-block": blockIndex,
|
|
60
60
|
"data-block-id": block.id,
|
|
61
|
+
"data-source-key": block.sourceKey,
|
|
62
|
+
"data-source-line-start": block.startLine,
|
|
63
|
+
"data-source-line-end": block.endLine,
|
|
61
64
|
className,
|
|
62
65
|
style: bqStyle,
|
|
63
66
|
...headingLevel ? { "data-owo-heading": headingLevel } : {},
|
|
@@ -224,33 +227,135 @@ function groupByCategory(commands) {
|
|
|
224
227
|
return groups;
|
|
225
228
|
}
|
|
226
229
|
|
|
227
|
-
// src/
|
|
230
|
+
// src/toolbar/Toolbar.tsx
|
|
228
231
|
import {
|
|
229
|
-
|
|
232
|
+
useMemo as useMemo2,
|
|
230
233
|
useState as useState2,
|
|
234
|
+
useRef as useRef2,
|
|
235
|
+
useEffect as useEffect2,
|
|
236
|
+
useCallback,
|
|
237
|
+
memo as memo5
|
|
238
|
+
} from "react";
|
|
239
|
+
import {
|
|
240
|
+
editorEntryRegistry
|
|
241
|
+
} from "@owomark/core";
|
|
242
|
+
import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
243
|
+
var EMPTY_IDS = [];
|
|
244
|
+
var COLLAPSE_BREAKPOINT = 420;
|
|
245
|
+
var Toolbar = memo5(function Toolbar2({ core, toolbarConfig }) {
|
|
246
|
+
const commandIds = toolbarConfig.componentCommands ?? EMPTY_IDS;
|
|
247
|
+
const cacheKey = commandIds.join(",");
|
|
248
|
+
const commands = useMemo2(() => {
|
|
249
|
+
const registry = core.getCommandRegistry();
|
|
250
|
+
const result = [];
|
|
251
|
+
for (const id of commandIds) {
|
|
252
|
+
const descriptor = editorEntryRegistry.list().find((entry) => entry.commandId === id);
|
|
253
|
+
if (!descriptor || descriptor.entryKind !== "component") {
|
|
254
|
+
if (process.env.NODE_ENV !== "production") {
|
|
255
|
+
console.warn(`[OwoMark Toolbar] Unknown component command id: "${id}", skipping.`);
|
|
256
|
+
}
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const cmd = registry.get(id);
|
|
260
|
+
if (cmd && cmd.toolbarEligible) {
|
|
261
|
+
result.push(cmd);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return result;
|
|
265
|
+
}, [core, cacheKey]);
|
|
266
|
+
const containerRef = useRef2(null);
|
|
267
|
+
const [collapsed, setCollapsed] = useState2(false);
|
|
268
|
+
const [popoverOpen, setPopoverOpen] = useState2(false);
|
|
269
|
+
const popoverRef = useRef2(null);
|
|
270
|
+
useEffect2(() => {
|
|
271
|
+
const el = containerRef.current;
|
|
272
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
273
|
+
const ro = new ResizeObserver((entries) => {
|
|
274
|
+
for (const entry of entries) {
|
|
275
|
+
setCollapsed(entry.contentRect.width < COLLAPSE_BREAKPOINT);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
ro.observe(el);
|
|
279
|
+
return () => ro.disconnect();
|
|
280
|
+
}, []);
|
|
281
|
+
useEffect2(() => {
|
|
282
|
+
if (!popoverOpen) return;
|
|
283
|
+
function handlePointerDown(e) {
|
|
284
|
+
if (popoverRef.current && !popoverRef.current.contains(e.target)) {
|
|
285
|
+
setPopoverOpen(false);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
document.addEventListener("pointerdown", handlePointerDown, true);
|
|
289
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown, true);
|
|
290
|
+
}, [popoverOpen]);
|
|
291
|
+
const handleCommandClick = useCallback((e, cmdId) => {
|
|
292
|
+
e.preventDefault();
|
|
293
|
+
core.executeCommandById(cmdId);
|
|
294
|
+
setPopoverOpen(false);
|
|
295
|
+
}, [core]);
|
|
296
|
+
if (commands.length === 0) return null;
|
|
297
|
+
const buttons = commands.map((cmd) => /* @__PURE__ */ jsxs2(
|
|
298
|
+
"button",
|
|
299
|
+
{
|
|
300
|
+
type: "button",
|
|
301
|
+
className: "owo-toolbar__component-button",
|
|
302
|
+
onPointerDown: (e) => handleCommandClick(e, cmd.id),
|
|
303
|
+
children: [
|
|
304
|
+
cmd.icon && /* @__PURE__ */ jsx5("span", { className: "owo-toolbar__component-icon", children: cmd.icon }),
|
|
305
|
+
/* @__PURE__ */ jsx5("span", { className: "owo-toolbar__component-label", children: cmd.label })
|
|
306
|
+
]
|
|
307
|
+
},
|
|
308
|
+
cmd.id
|
|
309
|
+
));
|
|
310
|
+
return /* @__PURE__ */ jsx5("div", { ref: containerRef, className: "owo-toolbar__components-group", role: "toolbar", "aria-label": "Components", children: collapsed ? /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
|
|
311
|
+
/* @__PURE__ */ jsxs2(
|
|
312
|
+
"button",
|
|
313
|
+
{
|
|
314
|
+
type: "button",
|
|
315
|
+
className: "owo-toolbar__collapse-trigger",
|
|
316
|
+
onPointerDown: (e) => {
|
|
317
|
+
e.preventDefault();
|
|
318
|
+
setPopoverOpen((v) => !v);
|
|
319
|
+
},
|
|
320
|
+
children: [
|
|
321
|
+
/* @__PURE__ */ jsx5("span", { className: "owo-toolbar__component-label", children: "Components" }),
|
|
322
|
+
/* @__PURE__ */ jsx5("span", { "aria-hidden": "true", children: popoverOpen ? "\u25B2" : "\u25BC" })
|
|
323
|
+
]
|
|
324
|
+
}
|
|
325
|
+
),
|
|
326
|
+
popoverOpen && /* @__PURE__ */ jsx5("div", { ref: popoverRef, className: "owo-toolbar__popover", children: buttons })
|
|
327
|
+
] }) : buttons });
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// src/useOwoMarkCore.ts
|
|
331
|
+
import {
|
|
332
|
+
useRef as useRef3,
|
|
333
|
+
useState as useState3,
|
|
231
334
|
useLayoutEffect as useLayoutEffect2
|
|
232
335
|
} from "react";
|
|
233
336
|
import {
|
|
234
337
|
createOwoMarkCore,
|
|
235
|
-
readSelection,
|
|
236
|
-
restoreSelection,
|
|
237
|
-
invalidateBlockCache,
|
|
238
338
|
virtualToLinear
|
|
239
339
|
} from "@owomark/core";
|
|
340
|
+
import {
|
|
341
|
+
readSelection,
|
|
342
|
+
restoreSelection,
|
|
343
|
+
invalidateBlockCache
|
|
344
|
+
} from "@owomark/core/browser";
|
|
240
345
|
function useOwoMarkCore(options) {
|
|
241
346
|
const { initialMarkdown, readOnly = false } = options;
|
|
242
|
-
const containerRef =
|
|
243
|
-
const coreRef =
|
|
244
|
-
const composingRef =
|
|
245
|
-
const pendingSelectionRef =
|
|
246
|
-
const onCompositionStateChangeRef =
|
|
347
|
+
const containerRef = useRef3(null);
|
|
348
|
+
const coreRef = useRef3(null);
|
|
349
|
+
const composingRef = useRef3(false);
|
|
350
|
+
const pendingSelectionRef = useRef3(null);
|
|
351
|
+
const onCompositionStateChangeRef = useRef3(options.onCompositionStateChange);
|
|
247
352
|
onCompositionStateChangeRef.current = options.onCompositionStateChange;
|
|
248
353
|
if (!coreRef.current) {
|
|
249
354
|
coreRef.current = createOwoMarkCore({ initialMarkdown });
|
|
250
355
|
}
|
|
251
356
|
const core = coreRef.current;
|
|
252
|
-
const [doc, setDoc] =
|
|
253
|
-
const [slashState, setSlashState] =
|
|
357
|
+
const [doc, setDoc] = useState3(() => core.getDocument());
|
|
358
|
+
const [slashState, setSlashState] = useState3(() => core.getSlashState());
|
|
254
359
|
useLayoutEffect2(() => {
|
|
255
360
|
const el = containerRef.current;
|
|
256
361
|
if (!el) return;
|
|
@@ -407,13 +512,25 @@ function useOwoMarkCore(options) {
|
|
|
407
512
|
}
|
|
408
513
|
|
|
409
514
|
// src/config.ts
|
|
515
|
+
var DEFAULT_TOOLBAR_COMPONENT_COMMANDS = [
|
|
516
|
+
"component-note",
|
|
517
|
+
"component-callout",
|
|
518
|
+
"component-code-demo",
|
|
519
|
+
"component-details",
|
|
520
|
+
"component-steps",
|
|
521
|
+
"component-tabs"
|
|
522
|
+
];
|
|
410
523
|
var DEFAULT_EDITOR_CONFIG = {
|
|
411
524
|
indentMode: "auto",
|
|
412
525
|
enableSideAnnotation: true,
|
|
413
526
|
enableMath: true,
|
|
527
|
+
enableComponents: true,
|
|
414
528
|
enableMarkdownSandbox: true,
|
|
415
529
|
markdownSandbox: {
|
|
416
530
|
outerFenceTicks: 4
|
|
531
|
+
},
|
|
532
|
+
toolbarConfig: {
|
|
533
|
+
componentCommands: DEFAULT_TOOLBAR_COMPONENT_COMMANDS
|
|
417
534
|
}
|
|
418
535
|
};
|
|
419
536
|
function resolveEditorConfig(config) {
|
|
@@ -423,6 +540,10 @@ function resolveEditorConfig(config) {
|
|
|
423
540
|
markdownSandbox: {
|
|
424
541
|
...DEFAULT_EDITOR_CONFIG.markdownSandbox,
|
|
425
542
|
...config?.markdownSandbox
|
|
543
|
+
},
|
|
544
|
+
toolbarConfig: {
|
|
545
|
+
...DEFAULT_EDITOR_CONFIG.toolbarConfig,
|
|
546
|
+
...config?.toolbarConfig
|
|
426
547
|
}
|
|
427
548
|
};
|
|
428
549
|
}
|
|
@@ -430,6 +551,7 @@ function deriveProcessorOptions(config) {
|
|
|
430
551
|
return {
|
|
431
552
|
enableSideAnnotation: config.enableSideAnnotation,
|
|
432
553
|
enableMath: config.enableMath,
|
|
554
|
+
enableComponents: config.enableComponents,
|
|
433
555
|
enableMarkdownSandbox: config.enableMarkdownSandbox,
|
|
434
556
|
markdownSandbox: config.markdownSandbox
|
|
435
557
|
};
|
|
@@ -437,7 +559,7 @@ function deriveProcessorOptions(config) {
|
|
|
437
559
|
|
|
438
560
|
// src/OwoMarkEditor.tsx
|
|
439
561
|
import "@owomark/view/style.css";
|
|
440
|
-
import { Fragment as Fragment2, jsx as
|
|
562
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
441
563
|
var OwoMarkEditor = forwardRef(
|
|
442
564
|
function OwoMarkEditor2(props, ref) {
|
|
443
565
|
const {
|
|
@@ -455,6 +577,7 @@ var OwoMarkEditor = forwardRef(
|
|
|
455
577
|
commandsRef,
|
|
456
578
|
coreRef: externalCoreRef,
|
|
457
579
|
controller,
|
|
580
|
+
scrollController,
|
|
458
581
|
indentMode: indentModeProp = "auto",
|
|
459
582
|
ariaLabel,
|
|
460
583
|
config: configProp
|
|
@@ -464,21 +587,24 @@ var OwoMarkEditor = forwardRef(
|
|
|
464
587
|
const theme = themeProp;
|
|
465
588
|
const enableSideAnnotation = resolved.enableSideAnnotation;
|
|
466
589
|
const enableMath = resolved.enableMath;
|
|
590
|
+
const enableComponents = resolved.enableComponents;
|
|
591
|
+
const toolbarConfig = resolved.toolbarConfig;
|
|
467
592
|
const isControlled = value !== void 0;
|
|
468
|
-
const lastExternalValue =
|
|
469
|
-
const suppressOnChange =
|
|
593
|
+
const lastExternalValue = useRef4(value ?? defaultValue ?? "");
|
|
594
|
+
const suppressOnChange = useRef4(false);
|
|
595
|
+
const boundSurfaceRef = useRef4(null);
|
|
470
596
|
const { core, doc, slashState, containerRef } = useOwoMarkCore({
|
|
471
597
|
initialMarkdown: lastExternalValue.current,
|
|
472
598
|
readOnly,
|
|
473
599
|
onCompositionStateChange
|
|
474
600
|
});
|
|
475
|
-
const handleSlashSelect =
|
|
601
|
+
const handleSlashSelect = useCallback2((index) => {
|
|
476
602
|
core.executeSlashCommand(index);
|
|
477
603
|
}, [core]);
|
|
478
|
-
const handleSlashDismiss =
|
|
604
|
+
const handleSlashDismiss = useCallback2(() => {
|
|
479
605
|
core.dismissSlashMenu();
|
|
480
606
|
}, [core]);
|
|
481
|
-
|
|
607
|
+
useEffect3(() => {
|
|
482
608
|
if (typeof externalCoreRef === "function") externalCoreRef(core);
|
|
483
609
|
else if (externalCoreRef && typeof externalCoreRef === "object") {
|
|
484
610
|
externalCoreRef.current = core;
|
|
@@ -490,7 +616,7 @@ var OwoMarkEditor = forwardRef(
|
|
|
490
616
|
}
|
|
491
617
|
};
|
|
492
618
|
}, [core, externalCoreRef]);
|
|
493
|
-
|
|
619
|
+
useEffect3(() => {
|
|
494
620
|
const unsub = core.onChange((markdown) => {
|
|
495
621
|
if (suppressOnChange.current) return;
|
|
496
622
|
lastExternalValue.current = markdown;
|
|
@@ -498,15 +624,45 @@ var OwoMarkEditor = forwardRef(
|
|
|
498
624
|
});
|
|
499
625
|
return unsub;
|
|
500
626
|
}, [core, onChange]);
|
|
501
|
-
|
|
627
|
+
useEffect3(() => {
|
|
502
628
|
if (!onSelectionChange) return;
|
|
503
629
|
return core.onSelectionChange(onSelectionChange);
|
|
504
630
|
}, [core, onSelectionChange]);
|
|
505
|
-
|
|
631
|
+
useEffect3(() => {
|
|
506
632
|
if (!controller) return;
|
|
507
633
|
return controller.connectEditor(core);
|
|
508
634
|
}, [core, controller]);
|
|
509
|
-
|
|
635
|
+
useEffect3(() => {
|
|
636
|
+
const root = containerRef.current;
|
|
637
|
+
if (!root || !scrollController) return;
|
|
638
|
+
const measure = () => {
|
|
639
|
+
scrollController.measureSurface("editor");
|
|
640
|
+
};
|
|
641
|
+
const resizeObserver = new ResizeObserver(measure);
|
|
642
|
+
resizeObserver.observe(root);
|
|
643
|
+
const mutationObserver = new MutationObserver(measure);
|
|
644
|
+
mutationObserver.observe(root, {
|
|
645
|
+
childList: true,
|
|
646
|
+
subtree: true,
|
|
647
|
+
characterData: true
|
|
648
|
+
});
|
|
649
|
+
const frame = requestAnimationFrame(measure);
|
|
650
|
+
let disposed = false;
|
|
651
|
+
if (typeof document !== "undefined" && document.fonts) {
|
|
652
|
+
document.fonts.ready.then(() => {
|
|
653
|
+
if (!disposed) {
|
|
654
|
+
measure();
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
return () => {
|
|
659
|
+
disposed = true;
|
|
660
|
+
cancelAnimationFrame(frame);
|
|
661
|
+
resizeObserver.disconnect();
|
|
662
|
+
mutationObserver.disconnect();
|
|
663
|
+
};
|
|
664
|
+
}, [doc, scrollController, containerRef]);
|
|
665
|
+
useEffect3(() => {
|
|
510
666
|
if (!isControlled) return;
|
|
511
667
|
if (value === lastExternalValue.current) return;
|
|
512
668
|
suppressOnChange.current = true;
|
|
@@ -514,21 +670,24 @@ var OwoMarkEditor = forwardRef(
|
|
|
514
670
|
lastExternalValue.current = value;
|
|
515
671
|
suppressOnChange.current = false;
|
|
516
672
|
}, [core, value, isControlled]);
|
|
517
|
-
|
|
673
|
+
useEffect3(() => {
|
|
518
674
|
core.setIndentMode(indentMode);
|
|
519
675
|
}, [core, indentMode]);
|
|
520
|
-
|
|
676
|
+
useEffect3(() => {
|
|
521
677
|
core.setEnableSideAnnotation(enableSideAnnotation);
|
|
522
678
|
}, [core, enableSideAnnotation]);
|
|
523
|
-
|
|
679
|
+
useEffect3(() => {
|
|
524
680
|
core.setEnableMath(enableMath);
|
|
525
681
|
}, [core, enableMath]);
|
|
526
|
-
|
|
682
|
+
useEffect3(() => {
|
|
683
|
+
core.setEnableComponents(enableComponents);
|
|
684
|
+
}, [core, enableComponents]);
|
|
685
|
+
useEffect3(() => {
|
|
527
686
|
if (containerRef.current && ariaLabel) {
|
|
528
687
|
containerRef.current.setAttribute("aria-label", ariaLabel);
|
|
529
688
|
}
|
|
530
689
|
}, [ariaLabel, containerRef]);
|
|
531
|
-
|
|
690
|
+
useEffect3(() => {
|
|
532
691
|
if (containerRef.current) {
|
|
533
692
|
containerRef.current.contentEditable = readOnly ? "false" : "true";
|
|
534
693
|
if (readOnly) {
|
|
@@ -558,13 +717,23 @@ var OwoMarkEditor = forwardRef(
|
|
|
558
717
|
themeClassName,
|
|
559
718
|
className
|
|
560
719
|
].filter(Boolean).join(" ");
|
|
561
|
-
const setRefs =
|
|
720
|
+
const setRefs = useCallback2((el) => {
|
|
721
|
+
if (scrollController && boundSurfaceRef.current !== el) {
|
|
722
|
+
if (boundSurfaceRef.current) {
|
|
723
|
+
scrollController.unbindSurface("editor", boundSurfaceRef.current);
|
|
724
|
+
}
|
|
725
|
+
if (el) {
|
|
726
|
+
scrollController.bindSurface("editor", el);
|
|
727
|
+
}
|
|
728
|
+
boundSurfaceRef.current = el;
|
|
729
|
+
}
|
|
562
730
|
containerRef.current = el;
|
|
563
731
|
if (typeof ref === "function") ref(el);
|
|
564
732
|
else if (ref) ref.current = el;
|
|
565
|
-
}, [ref, containerRef]);
|
|
566
|
-
return /* @__PURE__ */
|
|
567
|
-
/* @__PURE__ */
|
|
733
|
+
}, [ref, containerRef, scrollController]);
|
|
734
|
+
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
735
|
+
/* @__PURE__ */ jsx6(Toolbar, { core, toolbarConfig }),
|
|
736
|
+
/* @__PURE__ */ jsx6(
|
|
568
737
|
"div",
|
|
569
738
|
{
|
|
570
739
|
ref: setRefs,
|
|
@@ -572,10 +741,10 @@ var OwoMarkEditor = forwardRef(
|
|
|
572
741
|
"data-placeholder": placeholder,
|
|
573
742
|
onScroll,
|
|
574
743
|
suppressContentEditableWarning: true,
|
|
575
|
-
children: doc.blocks.map((block, i) => /* @__PURE__ */
|
|
744
|
+
children: doc.blocks.map((block, i) => /* @__PURE__ */ jsx6(EditorBlock, { block, blockIndex: i }, block.id))
|
|
576
745
|
}
|
|
577
746
|
),
|
|
578
|
-
/* @__PURE__ */
|
|
747
|
+
/* @__PURE__ */ jsx6(
|
|
579
748
|
SlashMenu,
|
|
580
749
|
{
|
|
581
750
|
state: slashState,
|
|
@@ -588,7 +757,7 @@ var OwoMarkEditor = forwardRef(
|
|
|
588
757
|
);
|
|
589
758
|
|
|
590
759
|
// src/OwoMarkPreview.tsx
|
|
591
|
-
import {
|
|
760
|
+
import { forwardRef as forwardRef3, useEffect as useEffect8, useRef as useRef11 } from "react";
|
|
592
761
|
import {
|
|
593
762
|
createOwoMarkPreviewEngine
|
|
594
763
|
} from "@owomark/view";
|
|
@@ -596,31 +765,31 @@ import {
|
|
|
596
765
|
// src/MdxPreview.tsx
|
|
597
766
|
import {
|
|
598
767
|
Component,
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
768
|
+
forwardRef as forwardRef2,
|
|
769
|
+
useEffect as useEffect7,
|
|
770
|
+
useMemo as useMemo6,
|
|
771
|
+
useRef as useRef10,
|
|
772
|
+
useState as useState7
|
|
604
773
|
} from "react";
|
|
605
774
|
import {
|
|
606
775
|
DEFAULT_MDX_COMPONENTS
|
|
607
776
|
} from "@owomark/processor";
|
|
608
777
|
|
|
609
778
|
// src/MdxSkeleton.tsx
|
|
610
|
-
import { useMemo as
|
|
779
|
+
import { useMemo as useMemo3, useRef as useRef5 } from "react";
|
|
611
780
|
import { createSkeletonHtml, ensureSkeletonStyles } from "@owomark/view";
|
|
612
|
-
import { jsx as
|
|
781
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
613
782
|
function MdxSkeleton({ height, lines, className, style }) {
|
|
614
|
-
const injected =
|
|
783
|
+
const injected = useRef5(false);
|
|
615
784
|
if (!injected.current && typeof document !== "undefined") {
|
|
616
785
|
ensureSkeletonStyles(document);
|
|
617
786
|
injected.current = true;
|
|
618
787
|
}
|
|
619
|
-
const html =
|
|
788
|
+
const html = useMemo3(
|
|
620
789
|
() => createSkeletonHtml({ height, lines: lines ?? (height == null ? 3 : void 0) }),
|
|
621
790
|
[height, lines]
|
|
622
791
|
);
|
|
623
|
-
return /* @__PURE__ */
|
|
792
|
+
return /* @__PURE__ */ jsx7(
|
|
624
793
|
"div",
|
|
625
794
|
{
|
|
626
795
|
className: className ?? void 0,
|
|
@@ -632,13 +801,13 @@ function MdxSkeleton({ height, lines, className, style }) {
|
|
|
632
801
|
|
|
633
802
|
// src/MdxComponentShell.tsx
|
|
634
803
|
import {
|
|
635
|
-
useRef as
|
|
636
|
-
useState as
|
|
637
|
-
useEffect as
|
|
804
|
+
useRef as useRef6,
|
|
805
|
+
useState as useState4,
|
|
806
|
+
useEffect as useEffect4,
|
|
638
807
|
useLayoutEffect as useLayoutEffect3,
|
|
639
|
-
useCallback as
|
|
808
|
+
useCallback as useCallback3
|
|
640
809
|
} from "react";
|
|
641
|
-
import { jsx as
|
|
810
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
642
811
|
var componentHeightCache = /* @__PURE__ */ new Map();
|
|
643
812
|
var DEFAULT_ESTIMATED_HEIGHT = 80;
|
|
644
813
|
function shallowPropsFingerprint(props) {
|
|
@@ -652,11 +821,11 @@ function shallowPropsFingerprint(props) {
|
|
|
652
821
|
return parts.join("&");
|
|
653
822
|
}
|
|
654
823
|
function MdxComponentShell({ name, propsFingerprint, children }) {
|
|
655
|
-
const containerRef =
|
|
656
|
-
const [status, setStatus] =
|
|
824
|
+
const containerRef = useRef6(null);
|
|
825
|
+
const [status, setStatus] = useState4("skeleton");
|
|
657
826
|
const cacheKey = propsFingerprint ? `${name}:${propsFingerprint}` : name;
|
|
658
827
|
const estimatedHeight = componentHeightCache.get(cacheKey) ?? DEFAULT_ESTIMATED_HEIGHT;
|
|
659
|
-
|
|
828
|
+
useEffect4(() => {
|
|
660
829
|
const id = requestAnimationFrame(() => setStatus("mounting"));
|
|
661
830
|
return () => cancelAnimationFrame(id);
|
|
662
831
|
}, []);
|
|
@@ -665,7 +834,7 @@ function MdxComponentShell({ name, propsFingerprint, children }) {
|
|
|
665
834
|
const id = requestAnimationFrame(() => setStatus("ready"));
|
|
666
835
|
return () => cancelAnimationFrame(id);
|
|
667
836
|
}, [status]);
|
|
668
|
-
const measureHeight =
|
|
837
|
+
const measureHeight = useCallback3(() => {
|
|
669
838
|
const el = containerRef.current;
|
|
670
839
|
if (!el) return;
|
|
671
840
|
const h = el.getBoundingClientRect().height;
|
|
@@ -673,7 +842,7 @@ function MdxComponentShell({ name, propsFingerprint, children }) {
|
|
|
673
842
|
componentHeightCache.set(cacheKey, h);
|
|
674
843
|
}
|
|
675
844
|
}, [cacheKey]);
|
|
676
|
-
|
|
845
|
+
useEffect4(() => {
|
|
677
846
|
const el = containerRef.current;
|
|
678
847
|
if (!el || status !== "ready") return;
|
|
679
848
|
measureHeight();
|
|
@@ -682,14 +851,14 @@ function MdxComponentShell({ name, propsFingerprint, children }) {
|
|
|
682
851
|
return () => ro.disconnect();
|
|
683
852
|
}, [status, measureHeight]);
|
|
684
853
|
if (status === "skeleton") {
|
|
685
|
-
return /* @__PURE__ */
|
|
854
|
+
return /* @__PURE__ */ jsx8(MdxSkeleton, { height: estimatedHeight });
|
|
686
855
|
}
|
|
687
|
-
return /* @__PURE__ */
|
|
856
|
+
return /* @__PURE__ */ jsx8("div", { ref: containerRef, children });
|
|
688
857
|
}
|
|
689
858
|
function wrapWithShell(name, Component2) {
|
|
690
859
|
function ShellWrapped(props) {
|
|
691
860
|
const fingerprint = shallowPropsFingerprint(props);
|
|
692
|
-
return /* @__PURE__ */
|
|
861
|
+
return /* @__PURE__ */ jsx8(MdxComponentShell, { name, propsFingerprint: fingerprint, children: /* @__PURE__ */ jsx8(Component2, { ...props }) });
|
|
693
862
|
}
|
|
694
863
|
ShellWrapped.displayName = `MdxShell(${name})`;
|
|
695
864
|
return ShellWrapped;
|
|
@@ -730,8 +899,8 @@ function parseCompileError(error, kind) {
|
|
|
730
899
|
}
|
|
731
900
|
|
|
732
901
|
// src/useMdxPreviewCompilation.ts
|
|
733
|
-
import { useEffect as
|
|
734
|
-
import { deriveRenderKey } from "@owomark/core";
|
|
902
|
+
import { useEffect as useEffect6, useMemo as useMemo5, useRef as useRef8, useState as useState6 } from "react";
|
|
903
|
+
import { deriveRenderKey as deriveRenderKey2 } from "@owomark/core";
|
|
735
904
|
|
|
736
905
|
// src/mdx-runtime.ts
|
|
737
906
|
import { compile, run } from "@mdx-js/mdx";
|
|
@@ -745,6 +914,7 @@ import {
|
|
|
745
914
|
var workerInstance = null;
|
|
746
915
|
var workerRefCount = 0;
|
|
747
916
|
var requestCounter = 0;
|
|
917
|
+
var WORKER_COMPILE_TIMEOUT_MS = 5e3;
|
|
748
918
|
var MAX_CRASHES = 3;
|
|
749
919
|
var CRASH_COOLDOWN_MS = 5e3;
|
|
750
920
|
var CRASH_WINDOW_MS = 3e4;
|
|
@@ -783,6 +953,7 @@ function tryCreateWorker() {
|
|
|
783
953
|
const pending = pendingRequests.get(response.id);
|
|
784
954
|
if (!pending) return;
|
|
785
955
|
pendingRequests.delete(response.id);
|
|
956
|
+
pending.cleanup?.();
|
|
786
957
|
if (response.ok) {
|
|
787
958
|
pending.resolve(response.code);
|
|
788
959
|
} else {
|
|
@@ -795,6 +966,7 @@ function tryCreateWorker() {
|
|
|
795
966
|
workerInstance = null;
|
|
796
967
|
for (const [id, pending] of pendingRequests) {
|
|
797
968
|
pendingRequests.delete(id);
|
|
969
|
+
pending.cleanup?.();
|
|
798
970
|
pending.reject({ message: "MDX compile worker crashed", kind: "compile" });
|
|
799
971
|
}
|
|
800
972
|
};
|
|
@@ -833,8 +1005,15 @@ function compileInWorker(markdown, options, inspection) {
|
|
|
833
1005
|
const workerRef = maybeWorker;
|
|
834
1006
|
const id = ++requestCounter;
|
|
835
1007
|
let cancelled = false;
|
|
1008
|
+
let timeoutId = null;
|
|
1009
|
+
const cleanup = () => {
|
|
1010
|
+
if (timeoutId !== null) {
|
|
1011
|
+
clearTimeout(timeoutId);
|
|
1012
|
+
timeoutId = null;
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
836
1015
|
const promise = new Promise((resolve, reject) => {
|
|
837
|
-
pendingRequests.set(id, { resolve, reject });
|
|
1016
|
+
pendingRequests.set(id, { resolve, reject, cleanup });
|
|
838
1017
|
workerRef.postMessage({
|
|
839
1018
|
type: "compile",
|
|
840
1019
|
id,
|
|
@@ -850,6 +1029,17 @@ function compileInWorker(markdown, options, inspection) {
|
|
|
850
1029
|
sourceMap: inspection?.sourceMap
|
|
851
1030
|
}
|
|
852
1031
|
});
|
|
1032
|
+
timeoutId = setTimeout(() => {
|
|
1033
|
+
const pending = pendingRequests.get(id);
|
|
1034
|
+
if (!pending) return;
|
|
1035
|
+
pendingRequests.delete(id);
|
|
1036
|
+
cleanup();
|
|
1037
|
+
try {
|
|
1038
|
+
workerRef.postMessage({ type: "cancel", id });
|
|
1039
|
+
} catch {
|
|
1040
|
+
}
|
|
1041
|
+
pending.reject({ message: "MDX compile worker timed out", kind: "compile" });
|
|
1042
|
+
}, WORKER_COMPILE_TIMEOUT_MS);
|
|
853
1043
|
});
|
|
854
1044
|
function cancel() {
|
|
855
1045
|
if (cancelled) return;
|
|
@@ -857,6 +1047,7 @@ function compileInWorker(markdown, options, inspection) {
|
|
|
857
1047
|
const pending = pendingRequests.get(id);
|
|
858
1048
|
if (pending) {
|
|
859
1049
|
pendingRequests.delete(id);
|
|
1050
|
+
pending.cleanup?.();
|
|
860
1051
|
pending.reject({ message: "Compilation cancelled", kind: "compile" });
|
|
861
1052
|
}
|
|
862
1053
|
workerRef.postMessage({ type: "cancel", id });
|
|
@@ -894,6 +1085,7 @@ async function compileOnMainThread(markdown, options, inspection) {
|
|
|
894
1085
|
}
|
|
895
1086
|
}
|
|
896
1087
|
function requiresMainThread(options) {
|
|
1088
|
+
if (options.preferWorker !== true) return true;
|
|
897
1089
|
if (options.extraRemarkPlugins?.length || options.extraRehypePlugins?.length) return true;
|
|
898
1090
|
if (options.extraRemarkDescriptors?.length && !allBuiltinDescriptors(options.extraRemarkDescriptors)) return true;
|
|
899
1091
|
if (options.extraRehypeDescriptors?.length && !allBuiltinDescriptors(options.extraRehypeDescriptors)) return true;
|
|
@@ -933,7 +1125,439 @@ async function runMdxCode(code) {
|
|
|
933
1125
|
|
|
934
1126
|
// src/mdx-preview-utils.tsx
|
|
935
1127
|
import { renderBlockDefault } from "@owomark/view";
|
|
936
|
-
|
|
1128
|
+
|
|
1129
|
+
// src/AsyncCodeBlock.tsx
|
|
1130
|
+
import {
|
|
1131
|
+
Children,
|
|
1132
|
+
isValidElement,
|
|
1133
|
+
useEffect as useEffect5,
|
|
1134
|
+
useMemo as useMemo4,
|
|
1135
|
+
useRef as useRef7,
|
|
1136
|
+
useState as useState5
|
|
1137
|
+
} from "react";
|
|
1138
|
+
|
|
1139
|
+
// src/highlight-cache-key.ts
|
|
1140
|
+
import { deriveRenderKey } from "@owomark/core";
|
|
1141
|
+
|
|
1142
|
+
// src/highlight-types.ts
|
|
1143
|
+
var HIGHLIGHT_WORKER_TIMEOUT_MS = 3500;
|
|
1144
|
+
var HIGHLIGHT_COMPONENT_DEBOUNCE_MS = 120;
|
|
1145
|
+
var HIGHLIGHT_CACHE_MAX_ENTRIES = 200;
|
|
1146
|
+
var DEFAULT_HIGHLIGHT_THEME = "vitesse-light";
|
|
1147
|
+
var HIGHLIGHTER_VERSION = "owomark-shiki-js-v1";
|
|
1148
|
+
|
|
1149
|
+
// src/highlight-cache-key.ts
|
|
1150
|
+
function buildHighlightCacheKey(input) {
|
|
1151
|
+
return deriveRenderKey(
|
|
1152
|
+
[
|
|
1153
|
+
input.code,
|
|
1154
|
+
input.language ?? "",
|
|
1155
|
+
input.themeKey,
|
|
1156
|
+
input.meta ?? "",
|
|
1157
|
+
HIGHLIGHTER_VERSION
|
|
1158
|
+
].join("\u241F"),
|
|
1159
|
+
"code-fence",
|
|
1160
|
+
""
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// src/highlight-worker-manager.ts
|
|
1165
|
+
var HighlightWorkerManager = class {
|
|
1166
|
+
timeoutMs;
|
|
1167
|
+
cacheMaxEntries;
|
|
1168
|
+
workerFactory;
|
|
1169
|
+
worker = null;
|
|
1170
|
+
workerFailureCount = 0;
|
|
1171
|
+
requestCounter = 0;
|
|
1172
|
+
cache = /* @__PURE__ */ new Map();
|
|
1173
|
+
inflightByCacheKey = /* @__PURE__ */ new Map();
|
|
1174
|
+
inflightByTaskId = /* @__PURE__ */ new Map();
|
|
1175
|
+
constructor(options = {}) {
|
|
1176
|
+
this.timeoutMs = options.timeoutMs ?? HIGHLIGHT_WORKER_TIMEOUT_MS;
|
|
1177
|
+
this.cacheMaxEntries = options.cacheMaxEntries ?? HIGHLIGHT_CACHE_MAX_ENTRIES;
|
|
1178
|
+
this.workerFactory = options.workerFactory;
|
|
1179
|
+
}
|
|
1180
|
+
highlight(input) {
|
|
1181
|
+
const request = {
|
|
1182
|
+
...input,
|
|
1183
|
+
requestId: `highlight-${++this.requestCounter}`,
|
|
1184
|
+
themeKey: input.themeKey?.trim() || DEFAULT_HIGHLIGHT_THEME
|
|
1185
|
+
};
|
|
1186
|
+
const cached = this.getCached(request.cacheKey);
|
|
1187
|
+
if (cached) {
|
|
1188
|
+
return {
|
|
1189
|
+
request,
|
|
1190
|
+
promise: Promise.resolve({
|
|
1191
|
+
...request,
|
|
1192
|
+
ok: true,
|
|
1193
|
+
html: cached
|
|
1194
|
+
}),
|
|
1195
|
+
cancel: () => {
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
let task = this.inflightByCacheKey.get(request.cacheKey);
|
|
1200
|
+
if (!task) {
|
|
1201
|
+
task = this.startTask(request);
|
|
1202
|
+
}
|
|
1203
|
+
const promise = new Promise((resolve, reject) => {
|
|
1204
|
+
task.consumers.set(request.requestId, {
|
|
1205
|
+
request,
|
|
1206
|
+
resolve,
|
|
1207
|
+
reject
|
|
1208
|
+
});
|
|
1209
|
+
});
|
|
1210
|
+
return {
|
|
1211
|
+
request,
|
|
1212
|
+
promise,
|
|
1213
|
+
cancel: () => this.cancelRequest(request.requestId)
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
resetForTesting() {
|
|
1217
|
+
this.worker?.terminate();
|
|
1218
|
+
this.worker = null;
|
|
1219
|
+
this.workerFailureCount = 0;
|
|
1220
|
+
this.requestCounter = 0;
|
|
1221
|
+
this.cache.clear();
|
|
1222
|
+
this.inflightByCacheKey.clear();
|
|
1223
|
+
this.inflightByTaskId.clear();
|
|
1224
|
+
}
|
|
1225
|
+
ensureWorker() {
|
|
1226
|
+
if (this.worker) {
|
|
1227
|
+
return this.worker;
|
|
1228
|
+
}
|
|
1229
|
+
if (!this.workerFactory && typeof Worker === "undefined") {
|
|
1230
|
+
return null;
|
|
1231
|
+
}
|
|
1232
|
+
try {
|
|
1233
|
+
const worker = this.workerFactory ? this.workerFactory() : new Worker(
|
|
1234
|
+
new URL("./highlight.worker.js", import.meta.url),
|
|
1235
|
+
{ type: "module" }
|
|
1236
|
+
);
|
|
1237
|
+
if (!worker) {
|
|
1238
|
+
return null;
|
|
1239
|
+
}
|
|
1240
|
+
worker.onmessage = (event) => {
|
|
1241
|
+
this.handleWorkerMessage(event.data);
|
|
1242
|
+
};
|
|
1243
|
+
worker.onerror = () => {
|
|
1244
|
+
this.workerFailureCount += 1;
|
|
1245
|
+
this.worker?.terminate();
|
|
1246
|
+
this.worker = null;
|
|
1247
|
+
const tasks = [...this.inflightByTaskId.values()];
|
|
1248
|
+
for (const task of tasks) {
|
|
1249
|
+
this.finishTaskWithError(task, "Code highlight worker crashed");
|
|
1250
|
+
}
|
|
1251
|
+
};
|
|
1252
|
+
this.worker = worker;
|
|
1253
|
+
return worker;
|
|
1254
|
+
} catch {
|
|
1255
|
+
this.workerFailureCount += 1;
|
|
1256
|
+
return null;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
startTask(request) {
|
|
1260
|
+
const task = {
|
|
1261
|
+
taskId: request.requestId,
|
|
1262
|
+
cacheKey: request.cacheKey,
|
|
1263
|
+
request,
|
|
1264
|
+
consumers: /* @__PURE__ */ new Map(),
|
|
1265
|
+
timeoutId: null
|
|
1266
|
+
};
|
|
1267
|
+
this.inflightByCacheKey.set(task.cacheKey, task);
|
|
1268
|
+
this.inflightByTaskId.set(task.taskId, task);
|
|
1269
|
+
const worker = this.ensureWorker();
|
|
1270
|
+
if (!worker) {
|
|
1271
|
+
queueMicrotask(() => {
|
|
1272
|
+
this.finishTaskWithError(task, "Code highlight worker unavailable");
|
|
1273
|
+
});
|
|
1274
|
+
return task;
|
|
1275
|
+
}
|
|
1276
|
+
task.timeoutId = setTimeout(() => {
|
|
1277
|
+
this.cancelTask(task, "Code highlight timed out");
|
|
1278
|
+
}, this.timeoutMs);
|
|
1279
|
+
worker.postMessage({
|
|
1280
|
+
type: "highlight",
|
|
1281
|
+
...request
|
|
1282
|
+
});
|
|
1283
|
+
return task;
|
|
1284
|
+
}
|
|
1285
|
+
handleWorkerMessage(message) {
|
|
1286
|
+
const task = this.inflightByTaskId.get(message.taskId);
|
|
1287
|
+
if (!task) {
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
this.clearTaskTimeout(task);
|
|
1291
|
+
this.inflightByTaskId.delete(task.taskId);
|
|
1292
|
+
this.inflightByCacheKey.delete(task.cacheKey);
|
|
1293
|
+
if (message.ok) {
|
|
1294
|
+
this.setCached(task.cacheKey, message.html);
|
|
1295
|
+
}
|
|
1296
|
+
for (const consumer of task.consumers.values()) {
|
|
1297
|
+
consumer.resolve(message.ok ? {
|
|
1298
|
+
...consumer.request,
|
|
1299
|
+
ok: true,
|
|
1300
|
+
html: message.html
|
|
1301
|
+
} : {
|
|
1302
|
+
...consumer.request,
|
|
1303
|
+
ok: false,
|
|
1304
|
+
error: message.error
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
cancelRequest(requestId) {
|
|
1309
|
+
for (const task of this.inflightByTaskId.values()) {
|
|
1310
|
+
if (!task.consumers.has(requestId)) {
|
|
1311
|
+
continue;
|
|
1312
|
+
}
|
|
1313
|
+
const consumer = task.consumers.get(requestId);
|
|
1314
|
+
task.consumers.delete(requestId);
|
|
1315
|
+
consumer.reject(new Error("Highlight request cancelled"));
|
|
1316
|
+
if (task.consumers.size === 0) {
|
|
1317
|
+
this.cancelTask(task, "Highlight request cancelled");
|
|
1318
|
+
}
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
cancelTask(task, error) {
|
|
1323
|
+
this.worker?.postMessage({
|
|
1324
|
+
type: "cancel",
|
|
1325
|
+
taskId: task.taskId
|
|
1326
|
+
});
|
|
1327
|
+
this.finishTaskWithError(task, error);
|
|
1328
|
+
}
|
|
1329
|
+
finishTaskWithError(task, error) {
|
|
1330
|
+
this.clearTaskTimeout(task);
|
|
1331
|
+
this.inflightByTaskId.delete(task.taskId);
|
|
1332
|
+
this.inflightByCacheKey.delete(task.cacheKey);
|
|
1333
|
+
for (const consumer of task.consumers.values()) {
|
|
1334
|
+
consumer.resolve({
|
|
1335
|
+
...consumer.request,
|
|
1336
|
+
ok: false,
|
|
1337
|
+
error
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
task.consumers.clear();
|
|
1341
|
+
}
|
|
1342
|
+
clearTaskTimeout(task) {
|
|
1343
|
+
if (task.timeoutId !== null) {
|
|
1344
|
+
clearTimeout(task.timeoutId);
|
|
1345
|
+
task.timeoutId = null;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
getCached(cacheKey) {
|
|
1349
|
+
const cached = this.cache.get(cacheKey);
|
|
1350
|
+
if (!cached) {
|
|
1351
|
+
return null;
|
|
1352
|
+
}
|
|
1353
|
+
cached.touchedAt = Date.now();
|
|
1354
|
+
this.cache.delete(cacheKey);
|
|
1355
|
+
this.cache.set(cacheKey, cached);
|
|
1356
|
+
return cached.html;
|
|
1357
|
+
}
|
|
1358
|
+
setCached(cacheKey, html) {
|
|
1359
|
+
this.cache.delete(cacheKey);
|
|
1360
|
+
this.cache.set(cacheKey, {
|
|
1361
|
+
html,
|
|
1362
|
+
touchedAt: Date.now()
|
|
1363
|
+
});
|
|
1364
|
+
while (this.cache.size > this.cacheMaxEntries) {
|
|
1365
|
+
const oldestKey = this.cache.keys().next().value;
|
|
1366
|
+
if (!oldestKey) {
|
|
1367
|
+
break;
|
|
1368
|
+
}
|
|
1369
|
+
this.cache.delete(oldestKey);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
var singletonManager = null;
|
|
1374
|
+
function getHighlightWorkerManager() {
|
|
1375
|
+
if (!singletonManager) {
|
|
1376
|
+
singletonManager = new HighlightWorkerManager();
|
|
1377
|
+
}
|
|
1378
|
+
return singletonManager;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
// src/AsyncCodeBlock.tsx
|
|
1382
|
+
import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1383
|
+
var nextBlockInstanceId = 1;
|
|
1384
|
+
function pickProps(source, omitKeys) {
|
|
1385
|
+
if (!source) {
|
|
1386
|
+
return {};
|
|
1387
|
+
}
|
|
1388
|
+
const next = {};
|
|
1389
|
+
for (const [key, value] of Object.entries(source)) {
|
|
1390
|
+
if (omitKeys.includes(key)) {
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
next[key] = value;
|
|
1394
|
+
}
|
|
1395
|
+
return next;
|
|
1396
|
+
}
|
|
1397
|
+
function renderPlainCode(props, status) {
|
|
1398
|
+
const preProps = pickProps(props.preProps, ["children"]);
|
|
1399
|
+
const codeProps = pickProps(props.codeProps, ["children", "className"]);
|
|
1400
|
+
return /* @__PURE__ */ jsx9(
|
|
1401
|
+
"figure",
|
|
1402
|
+
{
|
|
1403
|
+
"data-rehype-pretty-code-figure": "",
|
|
1404
|
+
"data-owo-code-block-state": status,
|
|
1405
|
+
className: "owo-async-code-block",
|
|
1406
|
+
children: /* @__PURE__ */ jsx9(
|
|
1407
|
+
"pre",
|
|
1408
|
+
{
|
|
1409
|
+
...preProps,
|
|
1410
|
+
className: ["shiki", props.themeKey, props.className].filter(Boolean).join(" "),
|
|
1411
|
+
"data-language": props.language ?? "text",
|
|
1412
|
+
"data-theme": props.themeKey,
|
|
1413
|
+
children: /* @__PURE__ */ jsx9(
|
|
1414
|
+
"code",
|
|
1415
|
+
{
|
|
1416
|
+
...codeProps,
|
|
1417
|
+
"data-language": props.language ?? "text",
|
|
1418
|
+
"data-theme": props.themeKey,
|
|
1419
|
+
style: { display: "grid" },
|
|
1420
|
+
children: props.code.split("\n").map((line, index, allLines) => /* @__PURE__ */ jsxs4("span", { "data-line": "", children: [
|
|
1421
|
+
line,
|
|
1422
|
+
index < allLines.length - 1 ? "\n" : ""
|
|
1423
|
+
] }, `${index}:${line}`))
|
|
1424
|
+
}
|
|
1425
|
+
)
|
|
1426
|
+
}
|
|
1427
|
+
)
|
|
1428
|
+
}
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
function AsyncCodeBlock(props) {
|
|
1432
|
+
const {
|
|
1433
|
+
code,
|
|
1434
|
+
language,
|
|
1435
|
+
meta,
|
|
1436
|
+
themeKey,
|
|
1437
|
+
documentVersion,
|
|
1438
|
+
manager: managerProp
|
|
1439
|
+
} = props;
|
|
1440
|
+
const manager = managerProp ?? getHighlightWorkerManager();
|
|
1441
|
+
const blockInstanceIdRef = useRef7(`async-code-block-${nextBlockInstanceId++}`);
|
|
1442
|
+
const activeRequestRef = useRef7(null);
|
|
1443
|
+
const [state, setState] = useState5({ status: "plain", html: null });
|
|
1444
|
+
const cacheKey = useMemo4(() => buildHighlightCacheKey({
|
|
1445
|
+
code,
|
|
1446
|
+
language,
|
|
1447
|
+
themeKey,
|
|
1448
|
+
meta
|
|
1449
|
+
}), [code, language, meta, themeKey]);
|
|
1450
|
+
useEffect5(() => {
|
|
1451
|
+
setState((current) => current.status === "highlighted" && current.html ? current : { status: "loading", html: null });
|
|
1452
|
+
let activeCancel = null;
|
|
1453
|
+
const timer = setTimeout(() => {
|
|
1454
|
+
const task = manager.highlight({
|
|
1455
|
+
documentVersion,
|
|
1456
|
+
blockInstanceId: blockInstanceIdRef.current,
|
|
1457
|
+
cacheKey,
|
|
1458
|
+
code,
|
|
1459
|
+
language,
|
|
1460
|
+
themeKey,
|
|
1461
|
+
meta
|
|
1462
|
+
});
|
|
1463
|
+
activeRequestRef.current = task.request.requestId;
|
|
1464
|
+
activeCancel = task.cancel;
|
|
1465
|
+
void task.promise.then((response) => {
|
|
1466
|
+
if (activeRequestRef.current !== response.requestId) {
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
if (response.documentVersion !== documentVersion) {
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
if (response.cacheKey !== cacheKey) {
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
if (response.ok) {
|
|
1476
|
+
setState({ status: "highlighted", html: response.html });
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
setState({ status: "plain", html: null });
|
|
1480
|
+
}).catch(() => {
|
|
1481
|
+
if (activeRequestRef.current === task.request.requestId) {
|
|
1482
|
+
setState({ status: "plain", html: null });
|
|
1483
|
+
}
|
|
1484
|
+
});
|
|
1485
|
+
}, HIGHLIGHT_COMPONENT_DEBOUNCE_MS);
|
|
1486
|
+
return () => {
|
|
1487
|
+
clearTimeout(timer);
|
|
1488
|
+
activeCancel?.();
|
|
1489
|
+
activeRequestRef.current = null;
|
|
1490
|
+
};
|
|
1491
|
+
}, [cacheKey, code, documentVersion, language, manager, meta, themeKey]);
|
|
1492
|
+
if (state.status === "highlighted") {
|
|
1493
|
+
return /* @__PURE__ */ jsx9(
|
|
1494
|
+
"figure",
|
|
1495
|
+
{
|
|
1496
|
+
"data-rehype-pretty-code-figure": "",
|
|
1497
|
+
"data-owo-code-block-state": "highlighted",
|
|
1498
|
+
className: "owo-async-code-block",
|
|
1499
|
+
dangerouslySetInnerHTML: { __html: state.html }
|
|
1500
|
+
}
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
return renderPlainCode(props, state.status);
|
|
1504
|
+
}
|
|
1505
|
+
function readCodeText(children) {
|
|
1506
|
+
if (typeof children === "string") {
|
|
1507
|
+
return children;
|
|
1508
|
+
}
|
|
1509
|
+
if (Array.isArray(children)) {
|
|
1510
|
+
return children.map(readCodeText).join("");
|
|
1511
|
+
}
|
|
1512
|
+
if (isValidElement(children)) {
|
|
1513
|
+
return readCodeText(children.props.children);
|
|
1514
|
+
}
|
|
1515
|
+
return "";
|
|
1516
|
+
}
|
|
1517
|
+
function getLanguageFromClassName(className) {
|
|
1518
|
+
if (!className) {
|
|
1519
|
+
return null;
|
|
1520
|
+
}
|
|
1521
|
+
const match = className.match(/language-([A-Za-z0-9_-]+)/);
|
|
1522
|
+
return match?.[1] ?? null;
|
|
1523
|
+
}
|
|
1524
|
+
function MdxAsyncPre(props) {
|
|
1525
|
+
const {
|
|
1526
|
+
themeKey,
|
|
1527
|
+
documentVersion,
|
|
1528
|
+
children,
|
|
1529
|
+
...nativePreProps
|
|
1530
|
+
} = props;
|
|
1531
|
+
if (Children.count(children) !== 1) {
|
|
1532
|
+
return /* @__PURE__ */ jsx9("pre", { ...nativePreProps, children });
|
|
1533
|
+
}
|
|
1534
|
+
const child = Children.only(children);
|
|
1535
|
+
if (!isValidElement(child)) {
|
|
1536
|
+
return /* @__PURE__ */ jsx9("pre", { ...nativePreProps, children });
|
|
1537
|
+
}
|
|
1538
|
+
const language = getLanguageFromClassName(child.props.className);
|
|
1539
|
+
const meta = typeof child.props["data-meta"] === "string" ? child.props["data-meta"] : null;
|
|
1540
|
+
const code = readCodeText(child.props.children);
|
|
1541
|
+
return /* @__PURE__ */ jsx9(
|
|
1542
|
+
AsyncCodeBlock,
|
|
1543
|
+
{
|
|
1544
|
+
code,
|
|
1545
|
+
language,
|
|
1546
|
+
meta,
|
|
1547
|
+
themeKey,
|
|
1548
|
+
documentVersion,
|
|
1549
|
+
className: child.props.className,
|
|
1550
|
+
codeProps: child.props,
|
|
1551
|
+
preProps: nativePreProps
|
|
1552
|
+
}
|
|
1553
|
+
);
|
|
1554
|
+
}
|
|
1555
|
+
function MdxInlineCode(props) {
|
|
1556
|
+
return /* @__PURE__ */ jsx9("code", { ...props });
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
// src/mdx-preview-utils.tsx
|
|
1560
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
937
1561
|
var objectIdentityMap = /* @__PURE__ */ new WeakMap();
|
|
938
1562
|
var nextObjectIdentity = 1;
|
|
939
1563
|
function getObjectIdentity(value) {
|
|
@@ -944,8 +1568,21 @@ function getObjectIdentity(value) {
|
|
|
944
1568
|
objectIdentityMap.set(value, id);
|
|
945
1569
|
return String(id);
|
|
946
1570
|
}
|
|
947
|
-
function buildRuntimeComponents(mdxAnalysis, baseComponents) {
|
|
948
|
-
const components = {
|
|
1571
|
+
function buildRuntimeComponents(mdxAnalysis, baseComponents, runtimeContext) {
|
|
1572
|
+
const components = {
|
|
1573
|
+
...baseComponents,
|
|
1574
|
+
pre: function RuntimeAsyncPre(props) {
|
|
1575
|
+
return /* @__PURE__ */ jsx10(
|
|
1576
|
+
MdxAsyncPre,
|
|
1577
|
+
{
|
|
1578
|
+
...props,
|
|
1579
|
+
themeKey: runtimeContext.themeKey,
|
|
1580
|
+
documentVersion: runtimeContext.documentVersion
|
|
1581
|
+
}
|
|
1582
|
+
);
|
|
1583
|
+
},
|
|
1584
|
+
code: MdxInlineCode
|
|
1585
|
+
};
|
|
949
1586
|
function registerMissingComponentPath(componentName) {
|
|
950
1587
|
const parts = componentName.split(".");
|
|
951
1588
|
let cursor = components;
|
|
@@ -957,7 +1594,7 @@ function buildRuntimeComponents(mdxAnalysis, baseComponents) {
|
|
|
957
1594
|
if (existing === void 0) {
|
|
958
1595
|
cursor[part] = function MissingComponent(props) {
|
|
959
1596
|
const { children, ...restProps } = props;
|
|
960
|
-
return /* @__PURE__ */
|
|
1597
|
+
return /* @__PURE__ */ jsx10("div", { "data-mdx-missing": componentName, ...restProps, children });
|
|
961
1598
|
};
|
|
962
1599
|
}
|
|
963
1600
|
return;
|
|
@@ -1010,10 +1647,10 @@ function useMdxPreviewCompilation({
|
|
|
1010
1647
|
mdx,
|
|
1011
1648
|
wrappedComponents
|
|
1012
1649
|
}) {
|
|
1013
|
-
const requestIdRef =
|
|
1014
|
-
const cancelRef =
|
|
1015
|
-
const blockCacheRef =
|
|
1016
|
-
const [mdxDisplay, setMdxDisplay] =
|
|
1650
|
+
const requestIdRef = useRef8(0);
|
|
1651
|
+
const cancelRef = useRef8(null);
|
|
1652
|
+
const blockCacheRef = useRef8(/* @__PURE__ */ new Map());
|
|
1653
|
+
const [mdxDisplay, setMdxDisplay] = useState6({
|
|
1017
1654
|
code: null,
|
|
1018
1655
|
Content: null,
|
|
1019
1656
|
runtimeComponents: null,
|
|
@@ -1022,8 +1659,8 @@ function useMdxPreviewCompilation({
|
|
|
1022
1659
|
pending: false,
|
|
1023
1660
|
error: null
|
|
1024
1661
|
});
|
|
1025
|
-
const [markdownHtml, setMarkdownHtml] =
|
|
1026
|
-
|
|
1662
|
+
const [markdownHtml, setMarkdownHtml] = useState6(null);
|
|
1663
|
+
useEffect6(() => {
|
|
1027
1664
|
acquireMdxWorker();
|
|
1028
1665
|
return () => {
|
|
1029
1666
|
cancelRef.current?.();
|
|
@@ -1031,7 +1668,7 @@ function useMdxPreviewCompilation({
|
|
|
1031
1668
|
releaseMdxWorker();
|
|
1032
1669
|
};
|
|
1033
1670
|
}, []);
|
|
1034
|
-
const mdxInspection =
|
|
1671
|
+
const mdxInspection = useMemo5(
|
|
1035
1672
|
() => inspectMdxSource2(snapshot.markdown, {
|
|
1036
1673
|
enableMath: mdx?.enableMath,
|
|
1037
1674
|
enableSideAnnotation: mdx?.enableSideAnnotation,
|
|
@@ -1046,14 +1683,13 @@ function useMdxPreviewCompilation({
|
|
|
1046
1683
|
snapshot.markdown
|
|
1047
1684
|
]
|
|
1048
1685
|
);
|
|
1049
|
-
const hasMdx = mdxInspection.hasMdxSyntax;
|
|
1050
|
-
const optionsFingerprint =
|
|
1686
|
+
const hasMdx = mdx?.forceFullDocumentCompile === true || mdxInspection.hasMdxSyntax;
|
|
1687
|
+
const optionsFingerprint = useMemo5(() => {
|
|
1051
1688
|
const b = (v) => v === true ? "1" : v === false ? "0" : "_";
|
|
1052
1689
|
const ds = (arr) => arr?.length ? arr.map((d) => d.options ? `${d.name}(${JSON.stringify(d.options)})` : d.name).join(",") : "";
|
|
1053
1690
|
return [
|
|
1054
1691
|
b(mdx?.enableMath),
|
|
1055
1692
|
b(mdx?.enableSideAnnotation),
|
|
1056
|
-
b(mdx?.enableCodeHighlight),
|
|
1057
1693
|
b(mdx?.sourceAnchors),
|
|
1058
1694
|
ds(mdx?.extraRemarkDescriptors),
|
|
1059
1695
|
ds(mdx?.extraRehypeDescriptors),
|
|
@@ -1063,26 +1699,25 @@ function useMdxPreviewCompilation({
|
|
|
1063
1699
|
}, [
|
|
1064
1700
|
mdx?.enableMath,
|
|
1065
1701
|
mdx?.enableSideAnnotation,
|
|
1066
|
-
mdx?.enableCodeHighlight,
|
|
1067
1702
|
mdx?.sourceAnchors,
|
|
1068
1703
|
mdx?.extraRemarkDescriptors,
|
|
1069
1704
|
mdx?.extraRehypeDescriptors,
|
|
1070
1705
|
mdx?.extraRemarkPlugins,
|
|
1071
1706
|
mdx?.extraRehypePlugins
|
|
1072
1707
|
]);
|
|
1073
|
-
const compileKey =
|
|
1074
|
-
() =>
|
|
1708
|
+
const compileKey = useMemo5(
|
|
1709
|
+
() => deriveRenderKey2(`${snapshot.markdown}:${optionsFingerprint}`, "custom", ""),
|
|
1075
1710
|
[snapshot.markdown, optionsFingerprint]
|
|
1076
1711
|
);
|
|
1077
|
-
const componentFingerprint =
|
|
1078
|
-
() => `cp${getObjectIdentity(mdx?.components)}`,
|
|
1079
|
-
[mdx?.components]
|
|
1712
|
+
const componentFingerprint = useMemo5(
|
|
1713
|
+
() => `cp${getObjectIdentity(mdx?.components)}:theme:${themeKey}`,
|
|
1714
|
+
[mdx?.components, themeKey]
|
|
1080
1715
|
);
|
|
1081
|
-
const renderKey =
|
|
1716
|
+
const renderKey = useMemo5(
|
|
1082
1717
|
() => `${compileKey}:${componentFingerprint}`,
|
|
1083
1718
|
[compileKey, componentFingerprint]
|
|
1084
1719
|
);
|
|
1085
|
-
|
|
1720
|
+
useEffect6(() => {
|
|
1086
1721
|
if (!snapshot.markdown.trim()) {
|
|
1087
1722
|
cancelRef.current?.();
|
|
1088
1723
|
cancelRef.current = null;
|
|
@@ -1131,7 +1766,10 @@ function useMdxPreviewCompilation({
|
|
|
1131
1766
|
return {
|
|
1132
1767
|
...prev,
|
|
1133
1768
|
Content,
|
|
1134
|
-
runtimeComponents: buildRuntimeComponents(mdxInspection, wrappedComponents
|
|
1769
|
+
runtimeComponents: buildRuntimeComponents(mdxInspection, wrappedComponents, {
|
|
1770
|
+
themeKey,
|
|
1771
|
+
documentVersion: snapshot.contentVersion
|
|
1772
|
+
}),
|
|
1135
1773
|
renderKey,
|
|
1136
1774
|
pending: false,
|
|
1137
1775
|
error: null
|
|
@@ -1157,7 +1795,7 @@ function useMdxPreviewCompilation({
|
|
|
1157
1795
|
const { promise, cancel } = compileMdx(snapshot.markdown, {
|
|
1158
1796
|
enableMath: mdx?.enableMath,
|
|
1159
1797
|
enableSideAnnotation: mdx?.enableSideAnnotation,
|
|
1160
|
-
enableCodeHighlight:
|
|
1798
|
+
enableCodeHighlight: false,
|
|
1161
1799
|
sourceAnchors: mdx?.sourceAnchors,
|
|
1162
1800
|
extraRemarkDescriptors: mdx?.extraRemarkDescriptors,
|
|
1163
1801
|
extraRehypeDescriptors: mdx?.extraRehypeDescriptors,
|
|
@@ -1174,7 +1812,10 @@ function useMdxPreviewCompilation({
|
|
|
1174
1812
|
setMdxDisplay({
|
|
1175
1813
|
code,
|
|
1176
1814
|
Content,
|
|
1177
|
-
runtimeComponents: buildRuntimeComponents(mdxInspection, wrappedComponents
|
|
1815
|
+
runtimeComponents: buildRuntimeComponents(mdxInspection, wrappedComponents, {
|
|
1816
|
+
themeKey,
|
|
1817
|
+
documentVersion: snapshot.contentVersion
|
|
1818
|
+
}),
|
|
1178
1819
|
compileKey,
|
|
1179
1820
|
renderKey,
|
|
1180
1821
|
pending: false,
|
|
@@ -1198,9 +1839,9 @@ function useMdxPreviewCompilation({
|
|
|
1198
1839
|
}, [
|
|
1199
1840
|
compileKey,
|
|
1200
1841
|
hasMdx,
|
|
1201
|
-
mdx?.enableCodeHighlight,
|
|
1202
1842
|
mdx?.enableMath,
|
|
1203
1843
|
mdx?.enableSideAnnotation,
|
|
1844
|
+
mdx?.forceFullDocumentCompile,
|
|
1204
1845
|
mdx?.extraRehypeDescriptors,
|
|
1205
1846
|
mdx?.extraRehypePlugins,
|
|
1206
1847
|
mdx?.extraRemarkDescriptors,
|
|
@@ -1213,6 +1854,7 @@ function useMdxPreviewCompilation({
|
|
|
1213
1854
|
renderBlock,
|
|
1214
1855
|
renderKey,
|
|
1215
1856
|
snapshot.markdown,
|
|
1857
|
+
snapshot.contentVersion,
|
|
1216
1858
|
snapshot.previewBlocks,
|
|
1217
1859
|
themeKey,
|
|
1218
1860
|
wrappedComponents
|
|
@@ -1224,8 +1866,43 @@ function useMdxPreviewCompilation({
|
|
|
1224
1866
|
};
|
|
1225
1867
|
}
|
|
1226
1868
|
|
|
1869
|
+
// src/useOwoMarkSharedState.ts
|
|
1870
|
+
import { useRef as useRef9, useSyncExternalStore, useCallback as useCallback4 } from "react";
|
|
1871
|
+
import {
|
|
1872
|
+
createSharedStateStore
|
|
1873
|
+
} from "@owomark/core";
|
|
1874
|
+
function useOwoMarkSharedState(options) {
|
|
1875
|
+
const controllerRef = useRef9(null);
|
|
1876
|
+
if (!controllerRef.current) {
|
|
1877
|
+
controllerRef.current = createSharedStateStore(options);
|
|
1878
|
+
}
|
|
1879
|
+
return controllerRef.current;
|
|
1880
|
+
}
|
|
1881
|
+
function useSharedStateSnapshot(controller) {
|
|
1882
|
+
const subscribe = useCallback4(
|
|
1883
|
+
(onStoreChange) => controller.subscribe(onStoreChange),
|
|
1884
|
+
[controller]
|
|
1885
|
+
);
|
|
1886
|
+
const getSnapshot = useCallback4(() => controller.getState(), [controller]);
|
|
1887
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
1888
|
+
}
|
|
1889
|
+
function useSharedStateSelector(store, selector, isEqual = Object.is) {
|
|
1890
|
+
const selectedRef = useRef9(selector(store.getState()));
|
|
1891
|
+
const selected = selector(store.getState());
|
|
1892
|
+
if (!isEqual(selectedRef.current, selected)) {
|
|
1893
|
+
selectedRef.current = selected;
|
|
1894
|
+
}
|
|
1895
|
+
const subscribe = useCallback4((onStoreChange) => store.subscribeWithSelector(selector, (nextSelected) => {
|
|
1896
|
+
if (isEqual(selectedRef.current, nextSelected)) return;
|
|
1897
|
+
selectedRef.current = nextSelected;
|
|
1898
|
+
onStoreChange();
|
|
1899
|
+
}, isEqual), [isEqual, selector, store]);
|
|
1900
|
+
const getSnapshot = useCallback4(() => selectedRef.current, []);
|
|
1901
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1227
1904
|
// src/MdxPreview.tsx
|
|
1228
|
-
import { jsx as
|
|
1905
|
+
import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1229
1906
|
function locationText(error) {
|
|
1230
1907
|
if (error.line == null) return null;
|
|
1231
1908
|
if (error.column == null) return `\u884C ${error.line}`;
|
|
@@ -1234,11 +1911,11 @@ function locationText(error) {
|
|
|
1234
1911
|
function ErrorNotice({ error }) {
|
|
1235
1912
|
const location = locationText(error);
|
|
1236
1913
|
const label = error.kind === "compile" ? "MDX Compile" : "MDX Runtime";
|
|
1237
|
-
return /* @__PURE__ */
|
|
1238
|
-
/* @__PURE__ */
|
|
1239
|
-
/* @__PURE__ */
|
|
1240
|
-
location && /* @__PURE__ */
|
|
1241
|
-
/* @__PURE__ */
|
|
1914
|
+
return /* @__PURE__ */ jsx11("div", { className: "sticky top-0 z-10 mb-4 rounded-md border border-[var(--owo-cmp-danger-border)] bg-[var(--owo-cmp-danger-bg)] px-4 py-3 text-sm text-[var(--owo-cmp-danger-text)]", children: /* @__PURE__ */ jsxs5("div", { className: "flex items-start gap-2", children: [
|
|
1915
|
+
/* @__PURE__ */ jsx11("span", { className: "mt-0.5 shrink-0 font-mono text-[11px] font-bold uppercase", children: label }),
|
|
1916
|
+
/* @__PURE__ */ jsxs5("div", { className: "min-w-0 flex-1", children: [
|
|
1917
|
+
location && /* @__PURE__ */ jsx11("span", { className: "mr-2 rounded bg-[var(--owo-cmp-danger-border)] px-1.5 py-0.5 font-mono text-xs", children: location }),
|
|
1918
|
+
/* @__PURE__ */ jsx11("span", { className: "break-words", children: error.message })
|
|
1242
1919
|
] })
|
|
1243
1920
|
] }) });
|
|
1244
1921
|
}
|
|
@@ -1252,28 +1929,41 @@ var RuntimeErrorBoundary = class extends Component {
|
|
|
1252
1929
|
}
|
|
1253
1930
|
render() {
|
|
1254
1931
|
if (this.state.error) {
|
|
1255
|
-
return /* @__PURE__ */
|
|
1932
|
+
return /* @__PURE__ */ jsx11(ErrorNotice, { error: this.state.error });
|
|
1256
1933
|
}
|
|
1257
1934
|
return this.props.children;
|
|
1258
1935
|
}
|
|
1259
1936
|
};
|
|
1260
|
-
function
|
|
1261
|
-
return
|
|
1937
|
+
function useMdxPreviewContentSnapshot(store, markdownOverride) {
|
|
1938
|
+
return useSharedStateSelector(
|
|
1939
|
+
store,
|
|
1940
|
+
(snapshot) => ({
|
|
1941
|
+
markdown: markdownOverride ?? snapshot.markdown,
|
|
1942
|
+
previewBlocks: snapshot.previewBlocks,
|
|
1943
|
+
contentVersion: snapshot.contentVersion
|
|
1944
|
+
}),
|
|
1945
|
+
(prev, next) => prev.markdown === next.markdown && prev.previewBlocks === next.previewBlocks && prev.contentVersion === next.contentVersion
|
|
1946
|
+
);
|
|
1262
1947
|
}
|
|
1263
|
-
|
|
1948
|
+
var MdxPreview = forwardRef2(function MdxPreview2(props, ref) {
|
|
1264
1949
|
const {
|
|
1265
1950
|
state,
|
|
1951
|
+
markdown,
|
|
1266
1952
|
className,
|
|
1267
1953
|
themeKey = "",
|
|
1268
1954
|
ariaLabel,
|
|
1955
|
+
onScroll,
|
|
1956
|
+
scrollController,
|
|
1957
|
+
scrollSync = "bidirectional",
|
|
1269
1958
|
renderBlock,
|
|
1270
1959
|
mdx,
|
|
1271
1960
|
onContentUpdate
|
|
1272
1961
|
} = props;
|
|
1273
|
-
const snapshot =
|
|
1274
|
-
const rootRef =
|
|
1275
|
-
const
|
|
1276
|
-
const
|
|
1962
|
+
const snapshot = useMdxPreviewContentSnapshot(state, markdown);
|
|
1963
|
+
const rootRef = useRef10(null);
|
|
1964
|
+
const boundSurfaceRef = useRef10(null);
|
|
1965
|
+
const [runtimeError, setRuntimeError] = useState7(null);
|
|
1966
|
+
const wrappedComponents = useMemo6(() => {
|
|
1277
1967
|
const wrapComponentMap = (componentMap) => {
|
|
1278
1968
|
const wrapped = {};
|
|
1279
1969
|
for (const [name, component] of Object.entries(componentMap)) {
|
|
@@ -1298,192 +1988,277 @@ function MdxPreview(props) {
|
|
|
1298
1988
|
mdx,
|
|
1299
1989
|
wrappedComponents
|
|
1300
1990
|
});
|
|
1301
|
-
|
|
1991
|
+
useEffect7(() => {
|
|
1302
1992
|
setRuntimeError(null);
|
|
1303
1993
|
}, [mdxDisplay.renderKey]);
|
|
1304
|
-
|
|
1994
|
+
useEffect7(() => {
|
|
1305
1995
|
const root = rootRef.current;
|
|
1306
|
-
if (!root || !onContentUpdate) return;
|
|
1307
|
-
const
|
|
1996
|
+
if (!root || !onContentUpdate && (!scrollController || scrollSync === "off")) return;
|
|
1997
|
+
const notify = () => {
|
|
1998
|
+
onContentUpdate?.();
|
|
1999
|
+
if (scrollController && scrollSync !== "off") {
|
|
2000
|
+
scrollController.measureSurface("preview");
|
|
2001
|
+
}
|
|
2002
|
+
};
|
|
2003
|
+
const observer = new ResizeObserver(() => notify());
|
|
1308
2004
|
observer.observe(root);
|
|
1309
2005
|
return () => observer.disconnect();
|
|
1310
|
-
}, [onContentUpdate]);
|
|
1311
|
-
|
|
1312
|
-
if (!onContentUpdate) return;
|
|
1313
|
-
const id = requestAnimationFrame(() =>
|
|
2006
|
+
}, [onContentUpdate, scrollController, scrollSync]);
|
|
2007
|
+
useEffect7(() => {
|
|
2008
|
+
if (!onContentUpdate && (!scrollController || scrollSync === "off")) return;
|
|
2009
|
+
const id = requestAnimationFrame(() => {
|
|
2010
|
+
onContentUpdate?.();
|
|
2011
|
+
if (scrollController && scrollSync !== "off") {
|
|
2012
|
+
scrollController.measureSurface("preview");
|
|
2013
|
+
}
|
|
2014
|
+
});
|
|
1314
2015
|
return () => cancelAnimationFrame(id);
|
|
1315
|
-
}, [mdxDisplay, markdownHtml, runtimeError, onContentUpdate]);
|
|
2016
|
+
}, [mdxDisplay, markdownHtml, runtimeError, onContentUpdate, scrollController, scrollSync]);
|
|
1316
2017
|
const hasContent = mdxDisplay.Content !== null || markdownHtml !== null;
|
|
1317
|
-
return /* @__PURE__ */
|
|
2018
|
+
return /* @__PURE__ */ jsxs5(
|
|
1318
2019
|
"div",
|
|
1319
2020
|
{
|
|
1320
|
-
ref:
|
|
2021
|
+
ref: (node) => {
|
|
2022
|
+
if (scrollController && scrollSync !== "off" && boundSurfaceRef.current !== node) {
|
|
2023
|
+
if (boundSurfaceRef.current) {
|
|
2024
|
+
scrollController.unbindSurface("preview", boundSurfaceRef.current);
|
|
2025
|
+
}
|
|
2026
|
+
if (node) {
|
|
2027
|
+
scrollController.bindSurface("preview", node);
|
|
2028
|
+
}
|
|
2029
|
+
boundSurfaceRef.current = node;
|
|
2030
|
+
} else if ((!scrollController || scrollSync === "off") && boundSurfaceRef.current) {
|
|
2031
|
+
scrollController?.unbindSurface("preview", boundSurfaceRef.current);
|
|
2032
|
+
boundSurfaceRef.current = null;
|
|
2033
|
+
}
|
|
2034
|
+
rootRef.current = node;
|
|
2035
|
+
if (typeof ref === "function") ref(node);
|
|
2036
|
+
else if (ref) ref.current = node;
|
|
2037
|
+
},
|
|
1321
2038
|
className,
|
|
1322
2039
|
role: "document",
|
|
1323
2040
|
"aria-label": ariaLabel ?? "Preview",
|
|
1324
2041
|
"aria-live": "polite",
|
|
2042
|
+
onScroll,
|
|
1325
2043
|
children: [
|
|
1326
|
-
mdxDisplay.pending && hasContent && /* @__PURE__ */
|
|
1327
|
-
mdxDisplay.error && /* @__PURE__ */
|
|
1328
|
-
runtimeError && /* @__PURE__ */
|
|
1329
|
-
!hasContent && mdxDisplay.pending && /* @__PURE__ */
|
|
1330
|
-
markdownHtml !== null && markdownHtml.map(({ blockId, html }) => /* @__PURE__ */
|
|
1331
|
-
mdxDisplay.Content && mdxDisplay.runtimeComponents && /* @__PURE__ */
|
|
2044
|
+
mdxDisplay.pending && hasContent && /* @__PURE__ */ jsx11("div", { className: "pointer-events-none sticky top-0 z-10 mb-3 h-0.5 w-full animate-pulse rounded-full bg-p400" }),
|
|
2045
|
+
mdxDisplay.error && /* @__PURE__ */ jsx11(ErrorNotice, { error: mdxDisplay.error }),
|
|
2046
|
+
runtimeError && /* @__PURE__ */ jsx11(ErrorNotice, { error: runtimeError }),
|
|
2047
|
+
!hasContent && mdxDisplay.pending && /* @__PURE__ */ jsx11(MdxSkeleton, { lines: 8 }),
|
|
2048
|
+
markdownHtml !== null && markdownHtml.map(({ blockId, html }) => /* @__PURE__ */ jsx11("div", { dangerouslySetInnerHTML: { __html: html } }, blockId)),
|
|
2049
|
+
mdxDisplay.Content && mdxDisplay.runtimeComponents && /* @__PURE__ */ jsx11(RuntimeErrorBoundary, { onError: setRuntimeError, children: /* @__PURE__ */ jsx11(mdxDisplay.Content, { components: mdxDisplay.runtimeComponents }) }, mdxDisplay.renderKey)
|
|
1332
2050
|
]
|
|
1333
2051
|
}
|
|
1334
2052
|
);
|
|
1335
|
-
}
|
|
2053
|
+
});
|
|
1336
2054
|
|
|
1337
2055
|
// src/OwoMarkPreview.tsx
|
|
1338
|
-
import { jsx as
|
|
2056
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1339
2057
|
function isController(store) {
|
|
1340
2058
|
return typeof store.updateVisibleBlockIds === "function";
|
|
1341
2059
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
onContentUpdate
|
|
2060
|
+
function usePreviewSurfaceAdapter(rootRef, scrollController, scrollSync = "bidirectional", observeDeps = []) {
|
|
2061
|
+
useEffect8(() => {
|
|
2062
|
+
const root = rootRef.current;
|
|
2063
|
+
if (!root || !scrollController || scrollSync === "off") return;
|
|
2064
|
+
const measure = () => {
|
|
2065
|
+
scrollController.measureSurface("preview");
|
|
2066
|
+
};
|
|
2067
|
+
const resizeObserver = new ResizeObserver(() => measure());
|
|
2068
|
+
resizeObserver.observe(root);
|
|
2069
|
+
const observeChildren = () => {
|
|
2070
|
+
resizeObserver.disconnect();
|
|
2071
|
+
resizeObserver.observe(root);
|
|
2072
|
+
Array.from(root.children).forEach((child) => resizeObserver.observe(child));
|
|
2073
|
+
};
|
|
2074
|
+
observeChildren();
|
|
2075
|
+
const mutationObserver = new MutationObserver(() => {
|
|
2076
|
+
observeChildren();
|
|
2077
|
+
measure();
|
|
2078
|
+
});
|
|
2079
|
+
mutationObserver.observe(root, { childList: true, subtree: true, characterData: true });
|
|
2080
|
+
const onLoad = (event) => {
|
|
2081
|
+
if (event.target?.tagName === "IMG") {
|
|
2082
|
+
measure();
|
|
1366
2083
|
}
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
2084
|
+
};
|
|
2085
|
+
root.addEventListener("load", onLoad, true);
|
|
2086
|
+
const frame = requestAnimationFrame(measure);
|
|
2087
|
+
let disposed = false;
|
|
2088
|
+
if (typeof document !== "undefined" && document.fonts) {
|
|
2089
|
+
document.fonts.ready.then(() => {
|
|
2090
|
+
if (!disposed) measure();
|
|
2091
|
+
});
|
|
2092
|
+
}
|
|
2093
|
+
return () => {
|
|
2094
|
+
disposed = true;
|
|
2095
|
+
cancelAnimationFrame(frame);
|
|
2096
|
+
resizeObserver.disconnect();
|
|
2097
|
+
mutationObserver.disconnect();
|
|
2098
|
+
root.removeEventListener("load", onLoad, true);
|
|
2099
|
+
};
|
|
2100
|
+
}, [rootRef, scrollController, scrollSync, ...observeDeps]);
|
|
2101
|
+
}
|
|
2102
|
+
var OwoMarkPreview = forwardRef3(
|
|
2103
|
+
function OwoMarkPreview2(props, ref) {
|
|
2104
|
+
const {
|
|
2105
|
+
state,
|
|
2106
|
+
markdown,
|
|
2107
|
+
className,
|
|
1379
2108
|
strategy,
|
|
1380
2109
|
themeKey,
|
|
1381
2110
|
registry,
|
|
1382
2111
|
viewportFirst,
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
if (!viewportFirst || !container || !isController(state)) return;
|
|
1408
|
-
const controller = state;
|
|
1409
|
-
const observer = new IntersectionObserver(
|
|
1410
|
-
(entries) => {
|
|
1411
|
-
const visible = /* @__PURE__ */ new Set();
|
|
1412
|
-
for (const id of controller.getState().visibleBlockIds) {
|
|
1413
|
-
visible.add(id);
|
|
1414
|
-
}
|
|
1415
|
-
for (const entry of entries) {
|
|
1416
|
-
const blockId = entry.target.getAttribute("data-block-id");
|
|
1417
|
-
if (!blockId) continue;
|
|
1418
|
-
if (entry.isIntersecting) {
|
|
1419
|
-
visible.add(blockId);
|
|
1420
|
-
} else {
|
|
1421
|
-
visible.delete(blockId);
|
|
1422
|
-
}
|
|
2112
|
+
ariaLabel,
|
|
2113
|
+
scrollController,
|
|
2114
|
+
scrollSync = "bidirectional",
|
|
2115
|
+
onScroll,
|
|
2116
|
+
renderBlock,
|
|
2117
|
+
mdx,
|
|
2118
|
+
onContentUpdate
|
|
2119
|
+
} = props;
|
|
2120
|
+
if (strategy === "mdx") {
|
|
2121
|
+
return /* @__PURE__ */ jsx12(
|
|
2122
|
+
MdxPreview,
|
|
2123
|
+
{
|
|
2124
|
+
ref,
|
|
2125
|
+
state,
|
|
2126
|
+
markdown,
|
|
2127
|
+
className,
|
|
2128
|
+
themeKey,
|
|
2129
|
+
ariaLabel,
|
|
2130
|
+
onScroll,
|
|
2131
|
+
scrollController,
|
|
2132
|
+
scrollSync,
|
|
2133
|
+
onContentUpdate,
|
|
2134
|
+
renderBlock,
|
|
2135
|
+
mdx
|
|
1423
2136
|
}
|
|
1424
|
-
|
|
1425
|
-
},
|
|
1426
|
-
{ root: container, rootMargin: "200px 0px" }
|
|
1427
|
-
);
|
|
1428
|
-
const wrappers = container.querySelectorAll("[data-block-id]");
|
|
1429
|
-
for (const wrapper of wrappers) {
|
|
1430
|
-
observer.observe(wrapper);
|
|
2137
|
+
);
|
|
1431
2138
|
}
|
|
1432
|
-
const
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
2139
|
+
const renderBlockRef = useRef11(renderBlock);
|
|
2140
|
+
renderBlockRef.current = renderBlock;
|
|
2141
|
+
const hasRenderBlock = !!renderBlock;
|
|
2142
|
+
const containerRef = useRef11(null);
|
|
2143
|
+
const boundSurfaceRef = useRef11(null);
|
|
2144
|
+
const engineRef = useRef11(null);
|
|
2145
|
+
usePreviewSurfaceAdapter(containerRef, scrollController, scrollSync, [
|
|
2146
|
+
strategy,
|
|
2147
|
+
themeKey,
|
|
2148
|
+
registry,
|
|
2149
|
+
viewportFirst,
|
|
2150
|
+
hasRenderBlock
|
|
2151
|
+
]);
|
|
2152
|
+
useEffect8(() => {
|
|
2153
|
+
if (!containerRef.current) return;
|
|
2154
|
+
const engineOptions = {
|
|
2155
|
+
strategy,
|
|
2156
|
+
themeKey,
|
|
2157
|
+
registry,
|
|
2158
|
+
viewportFirst,
|
|
2159
|
+
renderBlock: hasRenderBlock ? (block, ctx) => renderBlockRef.current(block, ctx) : void 0,
|
|
2160
|
+
onContentUpdate
|
|
2161
|
+
};
|
|
2162
|
+
const engine = createOwoMarkPreviewEngine(engineOptions);
|
|
2163
|
+
engine.mount(containerRef.current);
|
|
2164
|
+
engineRef.current = engine;
|
|
2165
|
+
void engine.update(state.getState());
|
|
2166
|
+
return () => {
|
|
2167
|
+
engine.destroy();
|
|
2168
|
+
engineRef.current = null;
|
|
2169
|
+
};
|
|
2170
|
+
}, [strategy, themeKey, registry, viewportFirst, hasRenderBlock]);
|
|
2171
|
+
useEffect8(() => {
|
|
2172
|
+
const unsub = state.subscribe((newState) => {
|
|
2173
|
+
const engine = engineRef.current;
|
|
2174
|
+
if (engine) {
|
|
2175
|
+
engine.update(newState);
|
|
2176
|
+
requestAnimationFrame(() => {
|
|
2177
|
+
if (scrollController && scrollSync !== "off") {
|
|
2178
|
+
scrollController.measureSurface("preview");
|
|
2179
|
+
}
|
|
2180
|
+
onContentUpdate?.();
|
|
2181
|
+
});
|
|
2182
|
+
}
|
|
2183
|
+
});
|
|
2184
|
+
return unsub;
|
|
2185
|
+
}, [state, scrollController, scrollSync, onContentUpdate]);
|
|
2186
|
+
useEffect8(() => {
|
|
2187
|
+
const container = containerRef.current;
|
|
2188
|
+
if (!viewportFirst || !container || !isController(state)) return;
|
|
2189
|
+
const controller = state;
|
|
2190
|
+
const observer = new IntersectionObserver(
|
|
2191
|
+
(entries) => {
|
|
2192
|
+
const visible = /* @__PURE__ */ new Set();
|
|
2193
|
+
for (const id of controller.getState().visibleBlockIds) {
|
|
2194
|
+
visible.add(id);
|
|
2195
|
+
}
|
|
2196
|
+
for (const entry of entries) {
|
|
2197
|
+
const blockId = entry.target.getAttribute("data-block-id");
|
|
2198
|
+
if (!blockId) continue;
|
|
2199
|
+
if (entry.isIntersecting) visible.add(blockId);
|
|
2200
|
+
else visible.delete(blockId);
|
|
2201
|
+
}
|
|
2202
|
+
controller.updateVisibleBlockIds([...visible]);
|
|
2203
|
+
},
|
|
2204
|
+
{ root: container, rootMargin: "200px 0px" }
|
|
2205
|
+
);
|
|
2206
|
+
const wrappers = container.querySelectorAll("[data-block-id]");
|
|
2207
|
+
for (const wrapper of wrappers) {
|
|
2208
|
+
observer.observe(wrapper);
|
|
2209
|
+
}
|
|
2210
|
+
const mutation = new MutationObserver((mutations) => {
|
|
2211
|
+
for (const m of mutations) {
|
|
2212
|
+
for (const node of m.addedNodes) {
|
|
2213
|
+
if (node instanceof HTMLElement && node.hasAttribute("data-block-id")) {
|
|
2214
|
+
observer.observe(node);
|
|
2215
|
+
}
|
|
1437
2216
|
}
|
|
1438
2217
|
}
|
|
2218
|
+
});
|
|
2219
|
+
mutation.observe(container, { childList: true, subtree: true });
|
|
2220
|
+
return () => {
|
|
2221
|
+
observer.disconnect();
|
|
2222
|
+
mutation.disconnect();
|
|
2223
|
+
};
|
|
2224
|
+
}, [viewportFirst, state]);
|
|
2225
|
+
return /* @__PURE__ */ jsx12(
|
|
2226
|
+
"div",
|
|
2227
|
+
{
|
|
2228
|
+
ref: (node) => {
|
|
2229
|
+
if (scrollController && scrollSync !== "off" && boundSurfaceRef.current !== node) {
|
|
2230
|
+
if (boundSurfaceRef.current) {
|
|
2231
|
+
scrollController.unbindSurface("preview", boundSurfaceRef.current);
|
|
2232
|
+
}
|
|
2233
|
+
if (node) {
|
|
2234
|
+
scrollController.bindSurface("preview", node);
|
|
2235
|
+
}
|
|
2236
|
+
boundSurfaceRef.current = node;
|
|
2237
|
+
} else if ((!scrollController || scrollSync === "off") && boundSurfaceRef.current) {
|
|
2238
|
+
scrollController?.unbindSurface("preview", boundSurfaceRef.current);
|
|
2239
|
+
boundSurfaceRef.current = null;
|
|
2240
|
+
}
|
|
2241
|
+
containerRef.current = node;
|
|
2242
|
+
if (typeof ref === "function") ref(node);
|
|
2243
|
+
else if (ref) ref.current = node;
|
|
2244
|
+
},
|
|
2245
|
+
className,
|
|
2246
|
+
role: "document",
|
|
2247
|
+
"aria-label": ariaLabel ?? "Preview",
|
|
2248
|
+
"aria-live": "polite",
|
|
2249
|
+
onScroll
|
|
1439
2250
|
}
|
|
1440
|
-
|
|
1441
|
-
mutation.observe(container, { childList: true, subtree: true });
|
|
1442
|
-
return () => {
|
|
1443
|
-
observer.disconnect();
|
|
1444
|
-
mutation.disconnect();
|
|
1445
|
-
};
|
|
1446
|
-
}, [viewportFirst, state]);
|
|
1447
|
-
return /* @__PURE__ */ jsx10(
|
|
1448
|
-
"div",
|
|
1449
|
-
{
|
|
1450
|
-
ref: containerRef,
|
|
1451
|
-
className,
|
|
1452
|
-
role: "document",
|
|
1453
|
-
"aria-label": ariaLabel ?? "Preview",
|
|
1454
|
-
"aria-live": "polite"
|
|
1455
|
-
}
|
|
1456
|
-
);
|
|
1457
|
-
};
|
|
1458
|
-
|
|
1459
|
-
// src/useOwoMarkSharedState.ts
|
|
1460
|
-
import { useRef as useRef9, useSyncExternalStore as useSyncExternalStore2, useCallback as useCallback3 } from "react";
|
|
1461
|
-
import {
|
|
1462
|
-
createSharedStateStore
|
|
1463
|
-
} from "@owomark/core";
|
|
1464
|
-
function useOwoMarkSharedState(options) {
|
|
1465
|
-
const controllerRef = useRef9(null);
|
|
1466
|
-
if (!controllerRef.current) {
|
|
1467
|
-
controllerRef.current = createSharedStateStore(options);
|
|
2251
|
+
);
|
|
1468
2252
|
}
|
|
1469
|
-
|
|
1470
|
-
}
|
|
1471
|
-
function useSharedStateSnapshot(controller) {
|
|
1472
|
-
const subscribe = useCallback3(
|
|
1473
|
-
(onStoreChange) => controller.subscribe(onStoreChange),
|
|
1474
|
-
[controller]
|
|
1475
|
-
);
|
|
1476
|
-
const getSnapshot = useCallback3(() => controller.getState(), [controller]);
|
|
1477
|
-
return useSyncExternalStore2(subscribe, getSnapshot, getSnapshot);
|
|
1478
|
-
}
|
|
2253
|
+
);
|
|
1479
2254
|
|
|
1480
2255
|
// src/useBlockContext.ts
|
|
1481
|
-
import { useState as
|
|
2256
|
+
import { useState as useState8, useEffect as useEffect9 } from "react";
|
|
1482
2257
|
function useBlockContext(core) {
|
|
1483
|
-
const [blockContext, setBlockContext] =
|
|
2258
|
+
const [blockContext, setBlockContext] = useState8(
|
|
1484
2259
|
() => core.getBlockContext()
|
|
1485
2260
|
);
|
|
1486
|
-
|
|
2261
|
+
useEffect9(() => {
|
|
1487
2262
|
setBlockContext(core.getBlockContext());
|
|
1488
2263
|
return core.onBlockContextChange((ctx) => {
|
|
1489
2264
|
setBlockContext(ctx);
|
|
@@ -1495,24 +2270,52 @@ function useBlockContext(core) {
|
|
|
1495
2270
|
// src/index.ts
|
|
1496
2271
|
import { getThemeClassName as getThemeClassName2, THEME_LIGHT_CLASS, THEME_DARK_CLASS } from "@owomark/view";
|
|
1497
2272
|
|
|
2273
|
+
// src/useScrollController.ts
|
|
2274
|
+
import { useEffect as useEffect10, useRef as useRef12 } from "react";
|
|
2275
|
+
import {
|
|
2276
|
+
createScrollController
|
|
2277
|
+
} from "@owomark/core/browser";
|
|
2278
|
+
function useScrollController(controller) {
|
|
2279
|
+
const scrollControllerRef = useRef12(null);
|
|
2280
|
+
if (!scrollControllerRef.current) {
|
|
2281
|
+
scrollControllerRef.current = createScrollController();
|
|
2282
|
+
}
|
|
2283
|
+
const sc = scrollControllerRef.current;
|
|
2284
|
+
useEffect10(() => {
|
|
2285
|
+
const state = controller.getState();
|
|
2286
|
+
sc.updateProjection(state.previewBlocks, state.contentVersion);
|
|
2287
|
+
return controller.subscribeWithSelector(
|
|
2288
|
+
(snapshot) => ({
|
|
2289
|
+
previewBlocks: snapshot.previewBlocks,
|
|
2290
|
+
contentVersion: snapshot.contentVersion
|
|
2291
|
+
}),
|
|
2292
|
+
({ previewBlocks, contentVersion }) => {
|
|
2293
|
+
sc.updateProjection(previewBlocks, contentVersion);
|
|
2294
|
+
},
|
|
2295
|
+
(prev, next) => prev.contentVersion === next.contentVersion
|
|
2296
|
+
);
|
|
2297
|
+
}, [controller, sc]);
|
|
2298
|
+
return sc;
|
|
2299
|
+
}
|
|
2300
|
+
|
|
1498
2301
|
// src/useVirtualList.ts
|
|
1499
|
-
import { useState as
|
|
2302
|
+
import { useState as useState9, useCallback as useCallback5, useRef as useRef13, useEffect as useEffect11, useMemo as useMemo7 } from "react";
|
|
1500
2303
|
import {
|
|
1501
2304
|
buildVirtualRows,
|
|
1502
2305
|
computeVisibleRange
|
|
1503
2306
|
} from "@owomark/core";
|
|
1504
2307
|
function useVirtualList(options) {
|
|
1505
2308
|
const { blocks, containerRef, overscan = 5, charsPerLine = 80 } = options;
|
|
1506
|
-
const heightCacheRef =
|
|
1507
|
-
const [scrollTop, setScrollTop] =
|
|
1508
|
-
const [viewportHeight, setViewportHeight] =
|
|
1509
|
-
const [heightCacheVersion, setHeightCacheVersion] =
|
|
1510
|
-
const rafRef =
|
|
1511
|
-
const [container, setContainer] =
|
|
1512
|
-
|
|
2309
|
+
const heightCacheRef = useRef13(/* @__PURE__ */ new Map());
|
|
2310
|
+
const [scrollTop, setScrollTop] = useState9(0);
|
|
2311
|
+
const [viewportHeight, setViewportHeight] = useState9(0);
|
|
2312
|
+
const [heightCacheVersion, setHeightCacheVersion] = useState9(0);
|
|
2313
|
+
const rafRef = useRef13(0);
|
|
2314
|
+
const [container, setContainer] = useState9(null);
|
|
2315
|
+
useEffect11(() => {
|
|
1513
2316
|
setContainer(containerRef.current);
|
|
1514
2317
|
});
|
|
1515
|
-
|
|
2318
|
+
useEffect11(() => {
|
|
1516
2319
|
if (!container) return;
|
|
1517
2320
|
setViewportHeight(container.clientHeight);
|
|
1518
2321
|
const ro = new ResizeObserver((entries) => {
|
|
@@ -1523,7 +2326,7 @@ function useVirtualList(options) {
|
|
|
1523
2326
|
ro.observe(container);
|
|
1524
2327
|
return () => ro.disconnect();
|
|
1525
2328
|
}, [container]);
|
|
1526
|
-
|
|
2329
|
+
useEffect11(() => {
|
|
1527
2330
|
if (!container) return;
|
|
1528
2331
|
const onScroll = () => {
|
|
1529
2332
|
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
@@ -1537,27 +2340,27 @@ function useVirtualList(options) {
|
|
|
1537
2340
|
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
1538
2341
|
};
|
|
1539
2342
|
}, [container]);
|
|
1540
|
-
const rows =
|
|
2343
|
+
const rows = useMemo7(
|
|
1541
2344
|
() => buildVirtualRows(blocks, heightCacheRef.current, charsPerLine),
|
|
1542
2345
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1543
2346
|
[blocks, charsPerLine, heightCacheVersion]
|
|
1544
2347
|
);
|
|
1545
|
-
const visibleRange =
|
|
2348
|
+
const visibleRange = useMemo7(
|
|
1546
2349
|
() => computeVisibleRange(rows, scrollTop, viewportHeight, overscan),
|
|
1547
2350
|
[rows, scrollTop, viewportHeight, overscan]
|
|
1548
2351
|
);
|
|
1549
|
-
const updateBlockHeight =
|
|
2352
|
+
const updateBlockHeight = useCallback5((blockId, height) => {
|
|
1550
2353
|
const cache = heightCacheRef.current;
|
|
1551
2354
|
if (cache.get(blockId) !== height) {
|
|
1552
2355
|
cache.set(blockId, height);
|
|
1553
2356
|
setHeightCacheVersion((v) => v + 1);
|
|
1554
2357
|
}
|
|
1555
2358
|
}, []);
|
|
1556
|
-
const isBlockVisible =
|
|
2359
|
+
const isBlockVisible = useCallback5(
|
|
1557
2360
|
(blockIndex) => blockIndex >= visibleRange.startIndex && blockIndex <= visibleRange.endIndex,
|
|
1558
2361
|
[visibleRange]
|
|
1559
2362
|
);
|
|
1560
|
-
const invalidateAllHeights =
|
|
2363
|
+
const invalidateAllHeights = useCallback5(() => {
|
|
1561
2364
|
heightCacheRef.current.clear();
|
|
1562
2365
|
setHeightCacheVersion((v) => v + 1);
|
|
1563
2366
|
}, []);
|
|
@@ -1574,7 +2377,9 @@ function useVirtualList(options) {
|
|
|
1574
2377
|
// src/index.ts
|
|
1575
2378
|
import { registerPlugin } from "@owomark/processor";
|
|
1576
2379
|
export {
|
|
2380
|
+
AsyncCodeBlock,
|
|
1577
2381
|
DEFAULT_EDITOR_CONFIG,
|
|
2382
|
+
DEFAULT_TOOLBAR_COMPONENT_COMMANDS,
|
|
1578
2383
|
EditorBlock,
|
|
1579
2384
|
EditorDecorator,
|
|
1580
2385
|
EditorLeaf,
|
|
@@ -1585,6 +2390,7 @@ export {
|
|
|
1585
2390
|
SlashMenu,
|
|
1586
2391
|
THEME_DARK_CLASS,
|
|
1587
2392
|
THEME_LIGHT_CLASS,
|
|
2393
|
+
Toolbar,
|
|
1588
2394
|
acquireMdxWorker,
|
|
1589
2395
|
clearComponentHeightCache,
|
|
1590
2396
|
computeMenuPosition,
|
|
@@ -1597,6 +2403,8 @@ export {
|
|
|
1597
2403
|
useBlockContext,
|
|
1598
2404
|
useOwoMarkCore,
|
|
1599
2405
|
useOwoMarkSharedState,
|
|
2406
|
+
useScrollController,
|
|
2407
|
+
useSharedStateSelector,
|
|
1600
2408
|
useSharedStateSnapshot,
|
|
1601
2409
|
useVirtualList,
|
|
1602
2410
|
wrapWithShell
|