@owomark/view 0.1.5 → 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 +1 -1
- package/dist/.build-manifest.json +76 -0
- package/dist/{chunk-F3LG7AML.js → chunk-656BO747.js} +35 -13
- package/dist/{chunk-Y72HQJQI.js → chunk-BPOZMVU7.js} +2 -3
- package/dist/index.d.ts +29 -80
- package/dist/index.js +681 -54
- package/dist/internal/virtual/height-estimator.d.ts +2 -9
- package/dist/internal/virtual/height-estimator.js +1 -1
- package/dist/internal/virtual/viewport-manager.js +1 -1
- package/dist/preview-render.worker.js +369 -0
- package/dist/types-D4TVpyP7.d.ts +103 -0
- package/package.json +2 -2
- package/src/style.css +2 -0
- package/src/theme/cards.css +50 -0
- package/src/theme/dark.css +8 -0
- package/src/theme/light.css +8 -0
- package/src/theme/owomark.css +28 -2
- package/src/theme/preview.css +15 -0
- package/src/theme/toolbar.css +104 -0
package/dist/index.js
CHANGED
|
@@ -7,25 +7,27 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
FALLBACK_BLOCK_HEIGHT,
|
|
9
9
|
estimateBlockHeight
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-BPOZMVU7.js";
|
|
11
11
|
import {
|
|
12
|
-
VirtualViewportManager
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import { createDomAdapter } from "@owomark/core/internal/dom-adapter";
|
|
12
|
+
VirtualViewportManager,
|
|
13
|
+
createSkeletonHtml,
|
|
14
|
+
ensureSkeletonStyles
|
|
15
|
+
} from "./chunk-656BO747.js";
|
|
17
16
|
|
|
18
17
|
// src/view-engine.ts
|
|
19
18
|
import {
|
|
20
|
-
readSelection,
|
|
21
|
-
restoreSelection,
|
|
22
|
-
invalidateBlockCache,
|
|
23
19
|
linearToVirtual,
|
|
24
20
|
virtualToLinear,
|
|
25
21
|
getBlockStartOffset,
|
|
26
22
|
tokenizeBlock
|
|
27
23
|
} from "@owomark/core";
|
|
28
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
readSelection,
|
|
26
|
+
restoreSelection,
|
|
27
|
+
invalidateBlockCache,
|
|
28
|
+
createBlockElement,
|
|
29
|
+
updateBlockElement
|
|
30
|
+
} from "@owomark/core/browser";
|
|
29
31
|
|
|
30
32
|
// src/dom/slash-menu.ts
|
|
31
33
|
var MENU_GAP = 4;
|
|
@@ -633,22 +635,6 @@ function createOwoMarkView(core, element) {
|
|
|
633
635
|
engine.mount(element);
|
|
634
636
|
return engine;
|
|
635
637
|
}
|
|
636
|
-
function createOwoMarkVanillaEditor() {
|
|
637
|
-
const adapter = createDomAdapter();
|
|
638
|
-
let slashMenu = null;
|
|
639
|
-
return {
|
|
640
|
-
...adapter,
|
|
641
|
-
mount(element) {
|
|
642
|
-
adapter.mount(element);
|
|
643
|
-
slashMenu = createSlashMenuOverlay(element, adapter.getCore());
|
|
644
|
-
},
|
|
645
|
-
destroy() {
|
|
646
|
-
slashMenu?.destroy();
|
|
647
|
-
slashMenu = null;
|
|
648
|
-
adapter.destroy();
|
|
649
|
-
}
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
638
|
|
|
653
639
|
// src/dom/patcher.ts
|
|
654
640
|
var BLOCK_ATTR = "data-preview-block";
|
|
@@ -792,6 +778,11 @@ var PreviewDomPatcher = class {
|
|
|
792
778
|
createBlockWrapper(doc, block) {
|
|
793
779
|
const wrapper = doc.createElement("div");
|
|
794
780
|
wrapper.setAttribute(BLOCK_ATTR, block.blockId);
|
|
781
|
+
if ("style" in wrapper && wrapper.style) {
|
|
782
|
+
wrapper.style.display = "flow-root";
|
|
783
|
+
} else {
|
|
784
|
+
wrapper.setAttribute("style", "display: flow-root;");
|
|
785
|
+
}
|
|
795
786
|
this.updateBlockAttributes(wrapper, block);
|
|
796
787
|
return wrapper;
|
|
797
788
|
}
|
|
@@ -809,20 +800,13 @@ var PreviewDomPatcher = class {
|
|
|
809
800
|
};
|
|
810
801
|
|
|
811
802
|
// src/renderer/registry.ts
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
renderers.set(kind, renderer);
|
|
820
|
-
},
|
|
821
|
-
unregister(kind) {
|
|
822
|
-
renderers.delete(kind);
|
|
823
|
-
}
|
|
824
|
-
};
|
|
825
|
-
}
|
|
803
|
+
import {
|
|
804
|
+
CARDS_FAMILY_CUSTOM_BLOCK_KEY as CARDS_FAMILY_CUSTOM_BLOCK_KEY2,
|
|
805
|
+
runtimeBlockRegistry
|
|
806
|
+
} from "@owomark/core";
|
|
807
|
+
|
|
808
|
+
// src/renderer/side-annotation-renderer.ts
|
|
809
|
+
import { SIDE_ANNOTATION_RENDERER_KEY_BY_TYPE } from "@owomark/core/semantic/syntax";
|
|
826
810
|
|
|
827
811
|
// src/renderer/default-renderer.ts
|
|
828
812
|
function escapeHtml(text) {
|
|
@@ -832,6 +816,39 @@ var SAFE_URL_PATTERN = /^(?:https?:|mailto:|#|\/)/i;
|
|
|
832
816
|
function sanitizeUrl(url) {
|
|
833
817
|
return SAFE_URL_PATTERN.test(url) ? url : "";
|
|
834
818
|
}
|
|
819
|
+
function stripCommonBlockquotePrefixes(raw) {
|
|
820
|
+
let lines = raw.split("\n");
|
|
821
|
+
let depth = 0;
|
|
822
|
+
while (lines.length > 0 && lines.every((line) => line.trim() === "" || /^ {0,3}>\s?/.test(line))) {
|
|
823
|
+
lines = lines.map((line) => line.trim() === "" ? line : line.replace(/^ {0,3}>\s?/, ""));
|
|
824
|
+
depth += 1;
|
|
825
|
+
}
|
|
826
|
+
return {
|
|
827
|
+
depth,
|
|
828
|
+
content: lines.join("\n")
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
function wrapInBlockquotes(content, depth) {
|
|
832
|
+
let wrapped = content;
|
|
833
|
+
for (let index = 0; index < depth; index += 1) {
|
|
834
|
+
wrapped = `<blockquote>${wrapped}</blockquote>`;
|
|
835
|
+
}
|
|
836
|
+
return wrapped;
|
|
837
|
+
}
|
|
838
|
+
function extractHeadingText(raw) {
|
|
839
|
+
const { depth, content } = stripCommonBlockquotePrefixes(raw);
|
|
840
|
+
const setextMatch = content.match(/^([^\n]+)\n {0,3}(?:=+|-+)\s*$/);
|
|
841
|
+
if (setextMatch) {
|
|
842
|
+
return {
|
|
843
|
+
depth,
|
|
844
|
+
text: setextMatch[1]
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
return {
|
|
848
|
+
depth,
|
|
849
|
+
text: content.replace(/^#{1,6}\s*/, "")
|
|
850
|
+
};
|
|
851
|
+
}
|
|
835
852
|
function renderInline(text) {
|
|
836
853
|
let html = escapeHtml(text);
|
|
837
854
|
html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
@@ -852,8 +869,8 @@ function renderBlockDefault(block) {
|
|
|
852
869
|
}
|
|
853
870
|
case "heading": {
|
|
854
871
|
const level = block.headingLevel ?? 1;
|
|
855
|
-
const text = block.raw
|
|
856
|
-
return `<h${level}>${renderInline(text)}</h${level}
|
|
872
|
+
const { depth, text } = extractHeadingText(block.raw);
|
|
873
|
+
return wrapInBlockquotes(`<h${level}>${renderInline(text)}</h${level}>`, depth);
|
|
857
874
|
}
|
|
858
875
|
case "code-fence": {
|
|
859
876
|
const lines = block.raw.split("\n");
|
|
@@ -895,6 +912,409 @@ function renderBlockDefault(block) {
|
|
|
895
912
|
}
|
|
896
913
|
}
|
|
897
914
|
|
|
915
|
+
// src/renderer/side-annotation-renderer.ts
|
|
916
|
+
function escapeHtml2(text) {
|
|
917
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
918
|
+
}
|
|
919
|
+
function escapeAttribute(text) {
|
|
920
|
+
return escapeHtml2(text).replace(/'/g, "'");
|
|
921
|
+
}
|
|
922
|
+
function svgSegment(className, width, height, body, extraAttrs = "") {
|
|
923
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" class="${className}"${extraAttrs}>${body}</svg>`;
|
|
924
|
+
}
|
|
925
|
+
function stretchLineDiv() {
|
|
926
|
+
return '<div class="side-svg-line"></div>';
|
|
927
|
+
}
|
|
928
|
+
function segmented(width, axisPx, segments, extraClass = "") {
|
|
929
|
+
const className = ["side-annotation-svg", "side-svg-segmented", extraClass].filter(Boolean).join(" ");
|
|
930
|
+
return `<div class="${className}" style="width:${width};--side-svg-axis:${axisPx}px">${segments.join("")}</div>`;
|
|
931
|
+
}
|
|
932
|
+
function svgPath(d, extra = "") {
|
|
933
|
+
return `<path d="${d}" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"${extra}></path>`;
|
|
934
|
+
}
|
|
935
|
+
function svgLine(x1, y1, x2, y2, extra = "") {
|
|
936
|
+
return `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"${extra}></line>`;
|
|
937
|
+
}
|
|
938
|
+
function svgPolyline(points, extra = "") {
|
|
939
|
+
return `<polyline points="${points}" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" stroke-linejoin="miter" shape-rendering="geometricPrecision"${extra}></polyline>`;
|
|
940
|
+
}
|
|
941
|
+
function makeSvgForSideType(sideType, orphan) {
|
|
942
|
+
const rendererKey = SIDE_ANNOTATION_RENDERER_KEY_BY_TYPE[sideType] ?? sideType;
|
|
943
|
+
if (orphan) {
|
|
944
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="4" viewBox="0 0 4 100" preserveAspectRatio="none" class="side-annotation-svg side-svg-vline">${svgLine(2, "2", 2, "98", ' vector-effect="non-scaling-stroke"')}</svg>`;
|
|
945
|
+
}
|
|
946
|
+
switch (rendererKey) {
|
|
947
|
+
case "brace":
|
|
948
|
+
return segmented("12px", 6, [
|
|
949
|
+
svgSegment("side-svg-cap", 12, 6, svgPath("M 1 0.75 C 3.5 0.75, 6 2.5, 6 5.25")),
|
|
950
|
+
stretchLineDiv(),
|
|
951
|
+
svgSegment("side-svg-beak", 12, 8, svgPath("M 6 0 C 6 2.5, 11 4, 11 4 C 11 4, 6 5.5, 6 8")),
|
|
952
|
+
stretchLineDiv(),
|
|
953
|
+
svgSegment("side-svg-cap", 12, 6, svgPath("M 6 0.75 C 6 3.5, 3.5 5.25, 1 5.25"))
|
|
954
|
+
]);
|
|
955
|
+
case "left-brace":
|
|
956
|
+
return segmented("12px", 6, [
|
|
957
|
+
svgSegment("side-svg-cap", 12, 6, svgPath("M 11 0.75 C 8.5 0.75, 6 2.5, 6 5.25")),
|
|
958
|
+
stretchLineDiv(),
|
|
959
|
+
svgSegment("side-svg-beak", 12, 8, svgPath("M 6 0 C 6 2.5, 1 4, 1 4 C 1 4, 6 5.5, 6 8")),
|
|
960
|
+
stretchLineDiv(),
|
|
961
|
+
svgSegment("side-svg-cap", 12, 6, svgPath("M 6 0.75 C 6 3.5, 8.5 5.25, 11 5.25"))
|
|
962
|
+
]);
|
|
963
|
+
case "bracket":
|
|
964
|
+
return segmented("8px", 4, [
|
|
965
|
+
svgSegment("side-svg-cap", 8, 6, svgPolyline("1,0.75 4,0.75 4,5.25")),
|
|
966
|
+
stretchLineDiv(),
|
|
967
|
+
svgSegment("side-svg-cap", 8, 6, svgPolyline("4,0.75 4,5.25 1,5.25"))
|
|
968
|
+
], "side-svg-bracket");
|
|
969
|
+
case "left-bracket":
|
|
970
|
+
return segmented("8px", 4, [
|
|
971
|
+
svgSegment("side-svg-cap", 8, 6, svgPolyline("7,0.75 4,0.75 4,5.25")),
|
|
972
|
+
stretchLineDiv(),
|
|
973
|
+
svgSegment("side-svg-cap", 8, 6, svgPolyline("4,0.75 4,5.25 7,5.25"))
|
|
974
|
+
], "side-svg-left-bracket");
|
|
975
|
+
case "line":
|
|
976
|
+
case "warning":
|
|
977
|
+
case "question":
|
|
978
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="4" viewBox="0 0 4 100" preserveAspectRatio="none" class="side-annotation-svg side-svg-vline">${svgLine(2, "2", 2, "98", ' vector-effect="non-scaling-stroke"')}</svg>`;
|
|
979
|
+
case "dash":
|
|
980
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="4" viewBox="0 0 20 4" class="side-annotation-svg side-svg-dash">${svgLine(1, "2", 19, "2")}</svg>`;
|
|
981
|
+
case "arrow":
|
|
982
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="16" viewBox="0 0 24 16" class="side-annotation-svg side-svg-arrow">${svgPath("M 1 8 L 20 8 M 16 3 L 21 8 L 16 13")}</svg>`;
|
|
983
|
+
case "fat-arrow":
|
|
984
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="16" viewBox="0 0 24 16" class="side-annotation-svg side-svg-fat-arrow">${svgPath("M 1 5 L 16 5 M 1 11 L 16 11 M 15 2 L 22 8 L 15 14")}</svg>`;
|
|
985
|
+
case "wave-arrow":
|
|
986
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="16" viewBox="0 0 28 16" class="side-annotation-svg side-svg-wave-arrow">${svgPath("M 1 8 C 4 3, 8 13, 12 8 C 16 3, 20 8, 20 8 M 18 3 L 23 8 L 18 13")}</svg>`;
|
|
987
|
+
case "brace-arrow":
|
|
988
|
+
return segmented("12px", 6, [
|
|
989
|
+
svgSegment("side-svg-cap", 12, 6, svgPath("M 1 0.75 C 3.5 0.75, 6 2.5, 6 5.25")),
|
|
990
|
+
stretchLineDiv(),
|
|
991
|
+
svgSegment("side-svg-beak", 12, 8, `${svgLine(6, "0", 6, "8", ' stroke-linecap="square" shape-rendering="geometricPrecision"')}${svgPath("M 6 4 L 19 4 M 16 1.5 L 20 4 L 16 6.5")}`, ' overflow="visible"'),
|
|
992
|
+
stretchLineDiv(),
|
|
993
|
+
svgSegment("side-svg-cap", 12, 6, svgPath("M 6 0.75 C 6 3.5, 3.5 5.25, 1 5.25"))
|
|
994
|
+
], "side-svg-compound-arrow");
|
|
995
|
+
case "brace-fat-arrow":
|
|
996
|
+
return segmented("12px", 6, [
|
|
997
|
+
svgSegment("side-svg-cap", 12, 6, svgPath("M 1 0.75 C 3.5 0.75, 6 2.5, 6 5.25")),
|
|
998
|
+
stretchLineDiv(),
|
|
999
|
+
svgSegment("side-svg-beak", 12, 8, `${svgLine(6, "0", 6, "8", ' stroke-linecap="square" shape-rendering="geometricPrecision"')}${svgPath("M 6 2.5 L 14 2.5 M 6 5.5 L 14 5.5 M 13 0.5 L 18 4 L 13 7.5")}`, ' overflow="visible"'),
|
|
1000
|
+
stretchLineDiv(),
|
|
1001
|
+
svgSegment("side-svg-cap", 12, 6, svgPath("M 6 0.75 C 6 3.5, 3.5 5.25, 1 5.25"))
|
|
1002
|
+
], "side-svg-compound-arrow");
|
|
1003
|
+
case "bracket-arrow":
|
|
1004
|
+
return segmented("8px", 4, [
|
|
1005
|
+
svgSegment("side-svg-cap", 8, 6, svgPolyline("1,0.75 4,0.75 4,5.25")),
|
|
1006
|
+
stretchLineDiv(),
|
|
1007
|
+
svgSegment("side-svg-beak", 8, 8, `${svgLine(4, "0", 4, "8", ' stroke-linecap="square" shape-rendering="geometricPrecision"')}${svgPath("M 4 4 L 17 4 M 14 1.5 L 18 4 L 14 6.5")}`, ' overflow="visible"'),
|
|
1008
|
+
stretchLineDiv(),
|
|
1009
|
+
svgSegment("side-svg-cap", 8, 6, svgPolyline("4,0.75 4,5.25 1,5.25"))
|
|
1010
|
+
], "side-svg-bracket side-svg-compound-arrow");
|
|
1011
|
+
case "bracket-fat-arrow":
|
|
1012
|
+
return segmented("8px", 4, [
|
|
1013
|
+
svgSegment("side-svg-cap", 8, 6, svgPolyline("1,0.75 4,0.75 4,5.25")),
|
|
1014
|
+
stretchLineDiv(),
|
|
1015
|
+
svgSegment("side-svg-beak", 8, 8, `${svgLine(4, "0", 4, "8", ' stroke-linecap="square" shape-rendering="geometricPrecision"')}${svgPath("M 4 2.5 L 13 2.5 M 4 5.5 L 13 5.5 M 12 0.5 L 17 4 L 12 7.5")}`, ' overflow="visible"'),
|
|
1016
|
+
stretchLineDiv(),
|
|
1017
|
+
svgSegment("side-svg-cap", 8, 6, svgPolyline("4,0.75 4,5.25 1,5.25"))
|
|
1018
|
+
], "side-svg-bracket side-svg-compound-arrow");
|
|
1019
|
+
case "line-arrow":
|
|
1020
|
+
return segmented("4px", 2, [
|
|
1021
|
+
stretchLineDiv(),
|
|
1022
|
+
svgSegment("side-svg-beak", 4, 8, `${svgLine(2, "0", 2, "8", ' stroke-linecap="square" shape-rendering="geometricPrecision"')}${svgPath("M 2 4 L 15 4 M 12 1.5 L 16 4 L 12 6.5")}`, ' overflow="visible"'),
|
|
1023
|
+
stretchLineDiv()
|
|
1024
|
+
], "side-svg-compound-arrow");
|
|
1025
|
+
case "line-fat-arrow":
|
|
1026
|
+
return segmented("4px", 2, [
|
|
1027
|
+
stretchLineDiv(),
|
|
1028
|
+
svgSegment("side-svg-beak", 4, 8, `${svgLine(2, "0", 2, "8", ' stroke-linecap="square" shape-rendering="geometricPrecision"')}${svgPath("M 2 2.5 L 11 2.5 M 2 5.5 L 11 5.5 M 10 0.5 L 15 4 L 10 7.5")}`, ' overflow="visible"'),
|
|
1029
|
+
stretchLineDiv()
|
|
1030
|
+
], "side-svg-compound-arrow");
|
|
1031
|
+
case "brace-warning":
|
|
1032
|
+
case "brace-question":
|
|
1033
|
+
return makeSvgForSideType("brace", false);
|
|
1034
|
+
default:
|
|
1035
|
+
return "";
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
function toPreviewKind(type) {
|
|
1039
|
+
switch (type) {
|
|
1040
|
+
case "heading":
|
|
1041
|
+
return "heading";
|
|
1042
|
+
case "unordered-list":
|
|
1043
|
+
return "unordered-list";
|
|
1044
|
+
case "ordered-list":
|
|
1045
|
+
return "ordered-list";
|
|
1046
|
+
case "blockquote":
|
|
1047
|
+
return "blockquote";
|
|
1048
|
+
case "code-fence":
|
|
1049
|
+
return "code-fence";
|
|
1050
|
+
case "thematic-break":
|
|
1051
|
+
return "thematic-break";
|
|
1052
|
+
case "math-block":
|
|
1053
|
+
return "math-block";
|
|
1054
|
+
case "table":
|
|
1055
|
+
return "table";
|
|
1056
|
+
case "html-block":
|
|
1057
|
+
return "html-block";
|
|
1058
|
+
default:
|
|
1059
|
+
return "paragraph";
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
function renderMainBlocks(block) {
|
|
1063
|
+
const rawMembers = Array.isArray(block.attributes?.mainBlocks) ? block.attributes?.mainBlocks : [];
|
|
1064
|
+
return rawMembers.map((entry, index) => {
|
|
1065
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
1066
|
+
return "";
|
|
1067
|
+
}
|
|
1068
|
+
const type = typeof entry.type === "string" ? entry.type : "paragraph";
|
|
1069
|
+
const raw = typeof entry.raw === "string" ? entry.raw : "";
|
|
1070
|
+
const memberBlock = {
|
|
1071
|
+
blockId: `${block.blockId}:member:${index}`,
|
|
1072
|
+
kind: toPreviewKind(type),
|
|
1073
|
+
raw,
|
|
1074
|
+
startLine: block.startLine,
|
|
1075
|
+
endLine: block.endLine,
|
|
1076
|
+
renderKey: `${block.renderKey}:member:${index}`
|
|
1077
|
+
};
|
|
1078
|
+
if (type === "heading" && typeof entry.headingLevel === "number") {
|
|
1079
|
+
memberBlock.headingLevel = entry.headingLevel;
|
|
1080
|
+
}
|
|
1081
|
+
if (type === "code-fence" && typeof entry.language === "string") {
|
|
1082
|
+
memberBlock.language = entry.language;
|
|
1083
|
+
}
|
|
1084
|
+
return renderBlockDefault(memberBlock);
|
|
1085
|
+
}).join("");
|
|
1086
|
+
}
|
|
1087
|
+
function isSideAnnotationPreviewBlock(block) {
|
|
1088
|
+
return block.kind === "custom" && block.attributes?.customBlockKey === "side-annotation";
|
|
1089
|
+
}
|
|
1090
|
+
function renderSideAnnotationPreviewBlock(block) {
|
|
1091
|
+
if (!isSideAnnotationPreviewBlock(block)) {
|
|
1092
|
+
return `<div>${escapeHtml2(block.raw)}</div>`;
|
|
1093
|
+
}
|
|
1094
|
+
const sideType = typeof block.attributes?.sideType === "string" ? block.attributes.sideType : "plain";
|
|
1095
|
+
const annotationText = typeof block.attributes?.annotationText === "string" ? block.attributes.annotationText : "";
|
|
1096
|
+
const orphan = Boolean(block.attributes?.orphan);
|
|
1097
|
+
const className = ["side-annotation", `side-type-${sideType}`, orphan ? "side-orphan" : ""].filter(Boolean).join(" ");
|
|
1098
|
+
const asideClass = ["side-annotation-aside", orphan ? "side-annotation-orphan" : ""].filter(Boolean).join(" ");
|
|
1099
|
+
const decoration = makeSvgForSideType(sideType, orphan);
|
|
1100
|
+
const mainHtml = renderMainBlocks(block);
|
|
1101
|
+
const textHtml = annotationText ? `<span class="side-annotation-text">${escapeHtml2(annotationText)}</span>` : "";
|
|
1102
|
+
return [
|
|
1103
|
+
`<div class="${className}" data-side-type="${escapeAttribute(sideType)}"${orphan ? ' data-side-orphan="true"' : ""}${annotationText ? ` data-side-text="${escapeAttribute(annotationText)}"` : ""}>`,
|
|
1104
|
+
`<div class="side-annotation-main">${mainHtml}</div>`,
|
|
1105
|
+
`<div class="${asideClass}">${decoration}${textHtml}</div>`,
|
|
1106
|
+
"</div>"
|
|
1107
|
+
].join("");
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// src/renderer/cards-renderer.ts
|
|
1111
|
+
import {
|
|
1112
|
+
CARDS_FAMILY_CUSTOM_BLOCK_KEY,
|
|
1113
|
+
isCardsFamilyAttributes,
|
|
1114
|
+
parseMarkdownToDocument
|
|
1115
|
+
} from "@owomark/core";
|
|
1116
|
+
function escapeHtml3(text) {
|
|
1117
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1118
|
+
}
|
|
1119
|
+
function isCardsFamilyPreviewBlock(block) {
|
|
1120
|
+
return block.kind === "custom" && block.attributes?.customBlockKey === CARDS_FAMILY_CUSTOM_BLOCK_KEY && isCardsFamilyAttributes(block.attributes);
|
|
1121
|
+
}
|
|
1122
|
+
function renderCardBody(markdown) {
|
|
1123
|
+
if (!markdown.trim()) {
|
|
1124
|
+
return "";
|
|
1125
|
+
}
|
|
1126
|
+
const nestedBlocks = parseMarkdownToDocument(markdown).previewBlocks;
|
|
1127
|
+
return nestedBlocks.map((nestedBlock) => {
|
|
1128
|
+
if (isCardsFamilyPreviewBlock(nestedBlock)) {
|
|
1129
|
+
return renderCardsFamilyPreviewBlock(nestedBlock);
|
|
1130
|
+
}
|
|
1131
|
+
if (nestedBlock.kind === "custom" && nestedBlock.attributes?.customBlockKey === "side-annotation") {
|
|
1132
|
+
return renderSideAnnotationPreviewBlock(nestedBlock);
|
|
1133
|
+
}
|
|
1134
|
+
return renderBlockDefault(nestedBlock);
|
|
1135
|
+
}).join("");
|
|
1136
|
+
}
|
|
1137
|
+
function renderCardsFamilyPreviewBlock(block) {
|
|
1138
|
+
if (!isCardsFamilyPreviewBlock(block)) {
|
|
1139
|
+
throw new Error("[owomark/view] cards renderer requires a cards-family preview block");
|
|
1140
|
+
}
|
|
1141
|
+
const attributes = block.attributes;
|
|
1142
|
+
const cardsHtml = attributes.cards.map((card) => {
|
|
1143
|
+
const toneAttr = card.tone ? ` data-owo-card-tone="${card.tone}"` : "";
|
|
1144
|
+
const toneClass = card.tone ? ` owo-card-tone-${card.tone}` : "";
|
|
1145
|
+
const titleHtml = card.title ? `<header class="owo-card-title">${escapeHtml3(card.title)}</header>` : "";
|
|
1146
|
+
const bodyHtml = renderCardBody(card.bodyMarkdown);
|
|
1147
|
+
return [
|
|
1148
|
+
`<article class="owo-card${toneClass}" data-owo-card${toneAttr}>`,
|
|
1149
|
+
titleHtml,
|
|
1150
|
+
`<div class="owo-card-body">${bodyHtml}</div>`,
|
|
1151
|
+
"</article>"
|
|
1152
|
+
].join("");
|
|
1153
|
+
}).join("");
|
|
1154
|
+
return [
|
|
1155
|
+
`<section class="owo-cards" data-owo-cards data-owo-cards-cols="${attributes.layout.cols}">`,
|
|
1156
|
+
cardsHtml,
|
|
1157
|
+
"</section>"
|
|
1158
|
+
].join("");
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// src/renderer/registry.ts
|
|
1162
|
+
function normalizeTarget(target) {
|
|
1163
|
+
const normalized = {
|
|
1164
|
+
kind: target.kind,
|
|
1165
|
+
customBlockKey: target.customBlockKey ?? null
|
|
1166
|
+
};
|
|
1167
|
+
if (normalized.kind === "custom" && !normalized.customBlockKey) {
|
|
1168
|
+
throw new Error("[owomark/view] custom renderer targets must declare customBlockKey");
|
|
1169
|
+
}
|
|
1170
|
+
return normalized;
|
|
1171
|
+
}
|
|
1172
|
+
function resolveTargetFromLookup(lookup) {
|
|
1173
|
+
if ("blockId" in lookup) {
|
|
1174
|
+
const block = lookup;
|
|
1175
|
+
if (block.kind === "custom") {
|
|
1176
|
+
const customBlockKey = typeof block.attributes?.customBlockKey === "string" ? block.attributes.customBlockKey : null;
|
|
1177
|
+
if (!customBlockKey) {
|
|
1178
|
+
return {
|
|
1179
|
+
kind: "custom",
|
|
1180
|
+
customBlockKey: null
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
return {
|
|
1184
|
+
kind: "custom",
|
|
1185
|
+
customBlockKey
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
return {
|
|
1189
|
+
kind: block.kind
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
return normalizeTarget(lookup);
|
|
1193
|
+
}
|
|
1194
|
+
function toSpecificKey(target) {
|
|
1195
|
+
return target.kind === "custom" ? `custom:${target.customBlockKey}` : `kind:${target.kind}`;
|
|
1196
|
+
}
|
|
1197
|
+
function createRendererRegistry() {
|
|
1198
|
+
const renderers = /* @__PURE__ */ new Map();
|
|
1199
|
+
const metadata = /* @__PURE__ */ new Map();
|
|
1200
|
+
const targets = /* @__PURE__ */ new Map();
|
|
1201
|
+
for (const descriptor of runtimeBlockRegistry.list()) {
|
|
1202
|
+
const target = {
|
|
1203
|
+
kind: descriptor.previewKind,
|
|
1204
|
+
customBlockKey: descriptor.customBlockKey ?? null
|
|
1205
|
+
};
|
|
1206
|
+
const key = toSpecificKey(target);
|
|
1207
|
+
metadata.set(key, {
|
|
1208
|
+
mode: descriptor.rendererMode,
|
|
1209
|
+
priority: descriptor.rendererPriority,
|
|
1210
|
+
heavy: descriptor.heavy
|
|
1211
|
+
});
|
|
1212
|
+
targets.set(key, target);
|
|
1213
|
+
}
|
|
1214
|
+
renderers.set("custom:side-annotation", {
|
|
1215
|
+
mode: metadata.get("custom:side-annotation")?.mode ?? "html-worker-safe",
|
|
1216
|
+
priority: metadata.get("custom:side-annotation")?.priority ?? "realtime",
|
|
1217
|
+
version: "builtin-side-annotation-v1",
|
|
1218
|
+
workerRendererId: "builtin:side-annotation",
|
|
1219
|
+
render(block) {
|
|
1220
|
+
return {
|
|
1221
|
+
kind: "html",
|
|
1222
|
+
html: renderSideAnnotationPreviewBlock(block)
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
targets.set("custom:side-annotation", {
|
|
1227
|
+
kind: "custom",
|
|
1228
|
+
customBlockKey: "side-annotation"
|
|
1229
|
+
});
|
|
1230
|
+
renderers.set(`custom:${CARDS_FAMILY_CUSTOM_BLOCK_KEY2}`, {
|
|
1231
|
+
mode: "html-worker-safe",
|
|
1232
|
+
priority: "realtime",
|
|
1233
|
+
version: "builtin-cards-family-v1",
|
|
1234
|
+
render(block) {
|
|
1235
|
+
return {
|
|
1236
|
+
kind: "html",
|
|
1237
|
+
html: renderCardsFamilyPreviewBlock(block)
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
});
|
|
1241
|
+
metadata.set(`custom:${CARDS_FAMILY_CUSTOM_BLOCK_KEY2}`, {
|
|
1242
|
+
mode: "html-worker-safe",
|
|
1243
|
+
priority: "realtime"
|
|
1244
|
+
});
|
|
1245
|
+
targets.set(`custom:${CARDS_FAMILY_CUSTOM_BLOCK_KEY2}`, {
|
|
1246
|
+
kind: "custom",
|
|
1247
|
+
customBlockKey: CARDS_FAMILY_CUSTOM_BLOCK_KEY2
|
|
1248
|
+
});
|
|
1249
|
+
function getMetadata(target) {
|
|
1250
|
+
if (target.kind === "custom" && !target.customBlockKey) {
|
|
1251
|
+
return null;
|
|
1252
|
+
}
|
|
1253
|
+
const specificKey = toSpecificKey(target);
|
|
1254
|
+
return metadata.get(specificKey) ?? null;
|
|
1255
|
+
}
|
|
1256
|
+
function getRenderer(target) {
|
|
1257
|
+
if (target.kind === "custom" && !target.customBlockKey) {
|
|
1258
|
+
return null;
|
|
1259
|
+
}
|
|
1260
|
+
const specificKey = toSpecificKey(target);
|
|
1261
|
+
return renderers.get(specificKey) ?? null;
|
|
1262
|
+
}
|
|
1263
|
+
return {
|
|
1264
|
+
get(lookup) {
|
|
1265
|
+
return getRenderer(resolveTargetFromLookup(lookup));
|
|
1266
|
+
},
|
|
1267
|
+
register(targetLike, renderer) {
|
|
1268
|
+
const target = normalizeTarget(targetLike);
|
|
1269
|
+
const key = toSpecificKey(target);
|
|
1270
|
+
renderers.set(key, renderer);
|
|
1271
|
+
targets.set(key, target);
|
|
1272
|
+
if (!metadata.has(key)) {
|
|
1273
|
+
metadata.set(key, {
|
|
1274
|
+
mode: renderer.mode,
|
|
1275
|
+
priority: renderer.priority
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
},
|
|
1279
|
+
unregister(targetLike) {
|
|
1280
|
+
const key = toSpecificKey(normalizeTarget(targetLike));
|
|
1281
|
+
renderers.delete(key);
|
|
1282
|
+
},
|
|
1283
|
+
registerMetadata(targetLike, meta) {
|
|
1284
|
+
const target = normalizeTarget(targetLike);
|
|
1285
|
+
const key = toSpecificKey(target);
|
|
1286
|
+
metadata.set(key, meta);
|
|
1287
|
+
targets.set(key, target);
|
|
1288
|
+
},
|
|
1289
|
+
isHeavy(lookup) {
|
|
1290
|
+
const target = resolveTargetFromLookup(lookup);
|
|
1291
|
+
const meta = getMetadata(target);
|
|
1292
|
+
if (meta?.heavy) return true;
|
|
1293
|
+
const def = getRenderer(target);
|
|
1294
|
+
return def?.priority === "deferred" || false;
|
|
1295
|
+
},
|
|
1296
|
+
getMode(lookup) {
|
|
1297
|
+
const target = resolveTargetFromLookup(lookup);
|
|
1298
|
+
const def = getRenderer(target);
|
|
1299
|
+
if (def) return def.mode;
|
|
1300
|
+
return getMetadata(target)?.mode ?? null;
|
|
1301
|
+
},
|
|
1302
|
+
getPriority(lookup) {
|
|
1303
|
+
const target = resolveTargetFromLookup(lookup);
|
|
1304
|
+
const def = getRenderer(target);
|
|
1305
|
+
if (def) return def.priority;
|
|
1306
|
+
return getMetadata(target)?.priority ?? null;
|
|
1307
|
+
},
|
|
1308
|
+
listRegistered() {
|
|
1309
|
+
const keys = /* @__PURE__ */ new Set([
|
|
1310
|
+
...renderers.keys(),
|
|
1311
|
+
...metadata.keys()
|
|
1312
|
+
]);
|
|
1313
|
+
return Array.from(keys).map((key) => targets.get(key)).filter((target) => target != null);
|
|
1314
|
+
}
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
|
|
898
1318
|
// src/strategies/shared.ts
|
|
899
1319
|
var scheduleIdle = typeof globalThis.requestIdleCallback === "function" ? (fn) => globalThis.requestIdleCallback(fn) : (fn) => setTimeout(fn, 1);
|
|
900
1320
|
var cancelIdle = typeof globalThis.cancelIdleCallback === "function" ? (id) => globalThis.cancelIdleCallback(id) : (id) => clearTimeout(id);
|
|
@@ -925,17 +1345,30 @@ function cancelAllIdle(pendingIdleIds) {
|
|
|
925
1345
|
pendingIdleIds.length = 0;
|
|
926
1346
|
}
|
|
927
1347
|
function isDeferred(block, registry) {
|
|
928
|
-
const def = registry.get(block
|
|
1348
|
+
const def = registry.get(block);
|
|
929
1349
|
return def?.priority === "deferred";
|
|
930
1350
|
}
|
|
931
|
-
function createRenderBlockFull(registry, renderCache, externalRenderBlock) {
|
|
1351
|
+
function createRenderBlockFull(registry, renderCache, externalRenderBlock, scheduler) {
|
|
932
1352
|
return async function renderBlockFull(block, baseContext) {
|
|
933
1353
|
const context = {
|
|
934
1354
|
...baseContext,
|
|
935
1355
|
sourceLineOffset: block.startLine - 1
|
|
936
1356
|
};
|
|
937
|
-
|
|
1357
|
+
if (externalRenderBlock && block.kind === "custom") {
|
|
1358
|
+
const html2 = await externalRenderBlock(block, context);
|
|
1359
|
+
renderCache.set(block.renderKey, html2);
|
|
1360
|
+
return { kind: "html", html: html2 };
|
|
1361
|
+
}
|
|
1362
|
+
const customRenderer = registry.get(block);
|
|
938
1363
|
if (customRenderer) {
|
|
1364
|
+
if (customRenderer.mode === "html-worker-safe" && customRenderer.workerRendererId && scheduler) {
|
|
1365
|
+
try {
|
|
1366
|
+
const html2 = await scheduler.submitWorkerTask(block, customRenderer, context);
|
|
1367
|
+
renderCache.set(block.renderKey, html2);
|
|
1368
|
+
return { kind: "html", html: html2 };
|
|
1369
|
+
} catch {
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
939
1372
|
const result = await customRenderer.render(block, context);
|
|
940
1373
|
if (result.kind === "html") {
|
|
941
1374
|
renderCache.set(block.renderKey, result.html);
|
|
@@ -962,6 +1395,7 @@ function createIncrementalEngine(options) {
|
|
|
962
1395
|
const themeKey = options?.themeKey ?? "";
|
|
963
1396
|
const viewportFirst = options?.viewportFirst ?? false;
|
|
964
1397
|
const registry = options?.registry ?? createRendererRegistry();
|
|
1398
|
+
const scheduler = options?.scheduler;
|
|
965
1399
|
const externalRenderBlock = options?.renderBlock;
|
|
966
1400
|
const onContentUpdate = options?.onContentUpdate;
|
|
967
1401
|
const patcher = new PreviewDomPatcher();
|
|
@@ -972,7 +1406,7 @@ function createIncrementalEngine(options) {
|
|
|
972
1406
|
let pendingAbort = null;
|
|
973
1407
|
let pendingIdleIds = [];
|
|
974
1408
|
let pendingBackfill = /* @__PURE__ */ new Set();
|
|
975
|
-
const renderBlockFull = createRenderBlockFull(registry, renderCache, externalRenderBlock);
|
|
1409
|
+
const renderBlockFull = createRenderBlockFull(registry, renderCache, externalRenderBlock, scheduler);
|
|
976
1410
|
function applyResult(blockId, result) {
|
|
977
1411
|
if (result.kind === "html") {
|
|
978
1412
|
patcher.patchBlockHtml(blockId, result.html);
|
|
@@ -1018,12 +1452,14 @@ function createIncrementalEngine(options) {
|
|
|
1018
1452
|
return {
|
|
1019
1453
|
mount(root) {
|
|
1020
1454
|
patcher.mount(root);
|
|
1455
|
+
ensureSkeletonStyles(root.ownerDocument);
|
|
1021
1456
|
mounted = true;
|
|
1022
1457
|
},
|
|
1023
1458
|
destroy() {
|
|
1024
1459
|
pendingAbort?.abort();
|
|
1025
1460
|
pendingAbort = null;
|
|
1026
1461
|
cancelAllIdle(pendingIdleIds);
|
|
1462
|
+
scheduler?.release();
|
|
1027
1463
|
patcher.destroy();
|
|
1028
1464
|
renderCache.clear();
|
|
1029
1465
|
pendingBackfill.clear();
|
|
@@ -1101,18 +1537,26 @@ function createIncrementalEngine(options) {
|
|
|
1101
1537
|
}
|
|
1102
1538
|
}
|
|
1103
1539
|
if (signal.aborted) return;
|
|
1540
|
+
pendingBackfill = /* @__PURE__ */ new Set();
|
|
1541
|
+
for (const block of realtimeOffscreen) {
|
|
1542
|
+
const height = estimateBlockHeight(block, registry);
|
|
1543
|
+
htmlMap.set(block.blockId, createSkeletonHtml({ height }));
|
|
1544
|
+
pendingBackfill.add(block.blockId);
|
|
1545
|
+
}
|
|
1546
|
+
for (const block of deferredAll) {
|
|
1547
|
+
const height = estimateBlockHeight(block, registry);
|
|
1548
|
+
htmlMap.set(block.blockId, createSkeletonHtml({ height }));
|
|
1549
|
+
pendingBackfill.add(block.blockId);
|
|
1550
|
+
}
|
|
1104
1551
|
patcher.fullRender(blocks, htmlMap);
|
|
1105
1552
|
for (const { blockId, result } of domMounts) {
|
|
1106
1553
|
if (signal.aborted) return;
|
|
1107
1554
|
applyResult(blockId, result);
|
|
1108
1555
|
}
|
|
1109
|
-
pendingBackfill = /* @__PURE__ */ new Set();
|
|
1110
1556
|
for (const block of realtimeOffscreen) {
|
|
1111
|
-
pendingBackfill.add(block.blockId);
|
|
1112
1557
|
scheduleBlockRender(block, baseContext, signal, state.version);
|
|
1113
1558
|
}
|
|
1114
1559
|
for (const block of deferredAll) {
|
|
1115
|
-
pendingBackfill.add(block.blockId);
|
|
1116
1560
|
scheduleBlockRender(block, baseContext, signal, state.version);
|
|
1117
1561
|
}
|
|
1118
1562
|
}
|
|
@@ -1229,6 +1673,7 @@ var OffscreenMeasurer = class {
|
|
|
1229
1673
|
function createVirtualEngine(options) {
|
|
1230
1674
|
const themeKey = options?.themeKey ?? "";
|
|
1231
1675
|
const registry = options?.registry ?? createRendererRegistry();
|
|
1676
|
+
const scheduler = options?.scheduler;
|
|
1232
1677
|
const externalRenderBlock = options?.renderBlock;
|
|
1233
1678
|
const onContentUpdate = options?.onContentUpdate;
|
|
1234
1679
|
const layoutMap = new BlockLayoutMap();
|
|
@@ -1236,7 +1681,7 @@ function createVirtualEngine(options) {
|
|
|
1236
1681
|
const measurer = new OffscreenMeasurer();
|
|
1237
1682
|
const viewport = new VirtualViewportManager();
|
|
1238
1683
|
const renderCache = /* @__PURE__ */ new Map();
|
|
1239
|
-
const renderBlockFull = createRenderBlockFull(registry, renderCache, externalRenderBlock);
|
|
1684
|
+
const renderBlockFull = createRenderBlockFull(registry, renderCache, externalRenderBlock, scheduler);
|
|
1240
1685
|
let renderedVersion = 0;
|
|
1241
1686
|
let lastBlocks = [];
|
|
1242
1687
|
let blockMap = /* @__PURE__ */ new Map();
|
|
@@ -1253,7 +1698,7 @@ function createVirtualEngine(options) {
|
|
|
1253
1698
|
function getInitialHeight(block) {
|
|
1254
1699
|
const cached = heightCache.get(block.renderKey);
|
|
1255
1700
|
if (cached !== void 0) return cached;
|
|
1256
|
-
return estimateBlockHeight(block);
|
|
1701
|
+
return estimateBlockHeight(block, registry);
|
|
1257
1702
|
}
|
|
1258
1703
|
async function renderAndMountBlock(block, baseContext, signal) {
|
|
1259
1704
|
if (signal.aborted) return;
|
|
@@ -1365,7 +1810,7 @@ function createVirtualEngine(options) {
|
|
|
1365
1810
|
heightCache.clear();
|
|
1366
1811
|
layoutMap.invalidateAll((blockId) => {
|
|
1367
1812
|
const block = blockMap.get(blockId);
|
|
1368
|
-
return block ? estimateBlockHeight(block) : FALLBACK_BLOCK_HEIGHT;
|
|
1813
|
+
return block ? estimateBlockHeight(block, registry) : FALLBACK_BLOCK_HEIGHT;
|
|
1369
1814
|
});
|
|
1370
1815
|
return true;
|
|
1371
1816
|
}
|
|
@@ -1400,6 +1845,7 @@ function createVirtualEngine(options) {
|
|
|
1400
1845
|
blockResizeObserver?.disconnect();
|
|
1401
1846
|
blockResizeObserver = null;
|
|
1402
1847
|
observedWrappers.clear();
|
|
1848
|
+
scheduler?.release();
|
|
1403
1849
|
viewport.destroy();
|
|
1404
1850
|
measurer.destroy();
|
|
1405
1851
|
renderCache.clear();
|
|
@@ -1525,11 +1971,186 @@ function createOwoMarkPreviewEngine(options) {
|
|
|
1525
1971
|
return createIncrementalEngine(options);
|
|
1526
1972
|
case "virtual":
|
|
1527
1973
|
return createVirtualEngine(options);
|
|
1974
|
+
case "mdx":
|
|
1975
|
+
throw new Error(
|
|
1976
|
+
'The "mdx" strategy is implemented by @owomark/react OwoMarkPreview, not by @owomark/view createOwoMarkPreviewEngine().'
|
|
1977
|
+
);
|
|
1528
1978
|
default:
|
|
1529
1979
|
throw new Error(`Unknown preview strategy: ${strategy}`);
|
|
1530
1980
|
}
|
|
1531
1981
|
}
|
|
1532
1982
|
|
|
1983
|
+
// src/worker/preview-task-scheduler.ts
|
|
1984
|
+
var MAX_CONSECUTIVE_CRASHES = 3;
|
|
1985
|
+
var CRASH_WINDOW_MS = 3e4;
|
|
1986
|
+
function serializeBlock(block) {
|
|
1987
|
+
return {
|
|
1988
|
+
kind: block.kind,
|
|
1989
|
+
raw: block.raw,
|
|
1990
|
+
blockId: block.blockId,
|
|
1991
|
+
renderKey: block.renderKey,
|
|
1992
|
+
startLine: block.startLine,
|
|
1993
|
+
endLine: block.endLine
|
|
1994
|
+
};
|
|
1995
|
+
}
|
|
1996
|
+
function createPreviewTaskScheduler(options) {
|
|
1997
|
+
const poolSize = options?.poolSize ?? Math.max(1, (typeof navigator !== "undefined" ? navigator.hardwareConcurrency ?? 4 : 4) - 1);
|
|
1998
|
+
let refCount = 1;
|
|
1999
|
+
let taskCounter = 0;
|
|
2000
|
+
let permanentlyFailed = false;
|
|
2001
|
+
const crashTimestamps = [];
|
|
2002
|
+
const pendingTasks = /* @__PURE__ */ new Map();
|
|
2003
|
+
const workers = [];
|
|
2004
|
+
let nextWorkerIndex = 0;
|
|
2005
|
+
function createWorkerSlot() {
|
|
2006
|
+
try {
|
|
2007
|
+
if (typeof Worker === "undefined") return null;
|
|
2008
|
+
const worker = new Worker(
|
|
2009
|
+
new URL("./preview-render.worker.js", import.meta.url),
|
|
2010
|
+
{ type: "module" }
|
|
2011
|
+
);
|
|
2012
|
+
const slot = { worker, pendingCount: 0, dead: false };
|
|
2013
|
+
worker.onmessage = (e) => {
|
|
2014
|
+
const resp = e.data;
|
|
2015
|
+
slot.pendingCount--;
|
|
2016
|
+
const pending = pendingTasks.get(resp.taskId);
|
|
2017
|
+
if (!pending) return;
|
|
2018
|
+
pendingTasks.delete(resp.taskId);
|
|
2019
|
+
if (resp.ok) {
|
|
2020
|
+
pending.resolve(resp.html);
|
|
2021
|
+
} else {
|
|
2022
|
+
pending.reject(new Error(resp.error));
|
|
2023
|
+
}
|
|
2024
|
+
};
|
|
2025
|
+
worker.onerror = () => {
|
|
2026
|
+
slot.dead = true;
|
|
2027
|
+
slot.pendingCount = 0;
|
|
2028
|
+
worker.terminate();
|
|
2029
|
+
recordCrash();
|
|
2030
|
+
options?.onCrash?.();
|
|
2031
|
+
};
|
|
2032
|
+
return slot;
|
|
2033
|
+
} catch {
|
|
2034
|
+
return null;
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
function recordCrash() {
|
|
2038
|
+
const now = Date.now();
|
|
2039
|
+
crashTimestamps.push(now);
|
|
2040
|
+
while (crashTimestamps.length > 0 && now - crashTimestamps[0] > CRASH_WINDOW_MS) {
|
|
2041
|
+
crashTimestamps.shift();
|
|
2042
|
+
}
|
|
2043
|
+
if (crashTimestamps.length >= MAX_CONSECUTIVE_CRASHES) {
|
|
2044
|
+
permanentlyFailed = true;
|
|
2045
|
+
terminateAll();
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
function getLeastBusySlot() {
|
|
2049
|
+
if (permanentlyFailed) return null;
|
|
2050
|
+
let best = null;
|
|
2051
|
+
for (const slot of workers) {
|
|
2052
|
+
if (slot.dead) continue;
|
|
2053
|
+
if (!best || slot.pendingCount < best.pendingCount) {
|
|
2054
|
+
best = slot;
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
if (best) return best;
|
|
2058
|
+
if (workers.length < poolSize) {
|
|
2059
|
+
const slot = createWorkerSlot();
|
|
2060
|
+
if (slot) {
|
|
2061
|
+
workers.push(slot);
|
|
2062
|
+
return slot;
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
for (let i = 0; i < workers.length; i++) {
|
|
2066
|
+
if (workers[i].dead) {
|
|
2067
|
+
const slot = createWorkerSlot();
|
|
2068
|
+
if (slot) {
|
|
2069
|
+
workers[i] = slot;
|
|
2070
|
+
return slot;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
return null;
|
|
2075
|
+
}
|
|
2076
|
+
function terminateAll() {
|
|
2077
|
+
for (const slot of workers) {
|
|
2078
|
+
if (!slot.dead) {
|
|
2079
|
+
slot.dead = true;
|
|
2080
|
+
slot.worker.terminate();
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
workers.length = 0;
|
|
2084
|
+
for (const [, pending] of pendingTasks) {
|
|
2085
|
+
pending.reject(new Error("Scheduler terminated"));
|
|
2086
|
+
}
|
|
2087
|
+
pendingTasks.clear();
|
|
2088
|
+
}
|
|
2089
|
+
const scheduler = {
|
|
2090
|
+
submitWorkerTask(block, rendererDef, context) {
|
|
2091
|
+
const rendererId = rendererDef.workerRendererId;
|
|
2092
|
+
if (permanentlyFailed || !rendererId) {
|
|
2093
|
+
return Promise.reject(new Error("Worker unavailable"));
|
|
2094
|
+
}
|
|
2095
|
+
const slot = getLeastBusySlot();
|
|
2096
|
+
if (!slot) {
|
|
2097
|
+
return Promise.reject(new Error("No worker available"));
|
|
2098
|
+
}
|
|
2099
|
+
const taskId = ++taskCounter;
|
|
2100
|
+
return new Promise((resolve, reject) => {
|
|
2101
|
+
pendingTasks.set(taskId, { resolve, reject });
|
|
2102
|
+
slot.pendingCount++;
|
|
2103
|
+
slot.worker.postMessage({
|
|
2104
|
+
type: "render",
|
|
2105
|
+
taskId,
|
|
2106
|
+
block: serializeBlock(block),
|
|
2107
|
+
rendererId,
|
|
2108
|
+
context: {
|
|
2109
|
+
version: context.version,
|
|
2110
|
+
themeKey: context.themeKey,
|
|
2111
|
+
sourceLineOffset: context.sourceLineOffset
|
|
2112
|
+
}
|
|
2113
|
+
});
|
|
2114
|
+
});
|
|
2115
|
+
},
|
|
2116
|
+
cancel(taskId) {
|
|
2117
|
+
const pending = pendingTasks.get(taskId);
|
|
2118
|
+
if (pending) {
|
|
2119
|
+
pendingTasks.delete(taskId);
|
|
2120
|
+
pending.reject(new Error("Task cancelled"));
|
|
2121
|
+
}
|
|
2122
|
+
const msg = { type: "cancel", taskId };
|
|
2123
|
+
for (const slot of workers) {
|
|
2124
|
+
if (!slot.dead) slot.worker.postMessage(msg);
|
|
2125
|
+
}
|
|
2126
|
+
},
|
|
2127
|
+
cancelAll() {
|
|
2128
|
+
for (const [, pending] of pendingTasks) {
|
|
2129
|
+
pending.reject(new Error("All tasks cancelled"));
|
|
2130
|
+
}
|
|
2131
|
+
pendingTasks.clear();
|
|
2132
|
+
const msg = { type: "cancel-all" };
|
|
2133
|
+
for (const slot of workers) {
|
|
2134
|
+
if (!slot.dead) {
|
|
2135
|
+
slot.pendingCount = 0;
|
|
2136
|
+
slot.worker.postMessage(msg);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
},
|
|
2140
|
+
acquire() {
|
|
2141
|
+
refCount++;
|
|
2142
|
+
return scheduler;
|
|
2143
|
+
},
|
|
2144
|
+
release() {
|
|
2145
|
+
if (--refCount <= 0) {
|
|
2146
|
+
refCount = 0;
|
|
2147
|
+
terminateAll();
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
};
|
|
2151
|
+
return scheduler;
|
|
2152
|
+
}
|
|
2153
|
+
|
|
1533
2154
|
// src/dom/side-annotation-positioner.ts
|
|
1534
2155
|
var SideAnnotationPositioner = class {
|
|
1535
2156
|
container;
|
|
@@ -1639,10 +2260,16 @@ export {
|
|
|
1639
2260
|
THEME_DARK_CLASS,
|
|
1640
2261
|
THEME_LIGHT_CLASS,
|
|
1641
2262
|
createOwoMarkPreviewEngine,
|
|
1642
|
-
createOwoMarkVanillaEditor,
|
|
1643
2263
|
createOwoMarkView,
|
|
2264
|
+
createPreviewTaskScheduler,
|
|
1644
2265
|
createRendererRegistry,
|
|
2266
|
+
createSkeletonHtml,
|
|
1645
2267
|
createViewEngine,
|
|
2268
|
+
ensureSkeletonStyles,
|
|
1646
2269
|
getThemeClassName,
|
|
1647
|
-
|
|
2270
|
+
isCardsFamilyPreviewBlock,
|
|
2271
|
+
isSideAnnotationPreviewBlock,
|
|
2272
|
+
renderBlockDefault,
|
|
2273
|
+
renderCardsFamilyPreviewBlock,
|
|
2274
|
+
renderSideAnnotationPreviewBlock
|
|
1648
2275
|
};
|