@owomark/view 0.1.5 → 0.1.6
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/dist/{chunk-Y72HQJQI.js → chunk-KHKPOH74.js} +2 -3
- package/dist/{chunk-F3LG7AML.js → chunk-WA6XHBZS.js} +34 -13
- package/dist/index.d.ts +16 -73
- package/dist/index.js +254 -11
- 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/types-DsL_4tUb.d.ts +93 -0
- package/package.json +2 -2
- package/src/theme/dark.css +6 -0
- package/src/theme/light.css +6 -0
- package/src/theme/owomark.css +22 -2
|
@@ -11,10 +11,9 @@ var HEAVY_LARGE_THRESHOLD = 50;
|
|
|
11
11
|
var HEAVY_SMALL_HEIGHT = 60;
|
|
12
12
|
var HEAVY_MEDIUM_HEIGHT = 200;
|
|
13
13
|
var HEAVY_LARGE_HEIGHT = 500;
|
|
14
|
-
|
|
15
|
-
function estimateBlockHeight(block) {
|
|
14
|
+
function estimateBlockHeight(block, registry) {
|
|
16
15
|
const lineCount = block.endLine - block.startLine + 1;
|
|
17
|
-
if (
|
|
16
|
+
if (registry?.isHeavy(block.kind)) {
|
|
18
17
|
return estimateHeavyBlockHeight(lineCount);
|
|
19
18
|
}
|
|
20
19
|
if (block.kind === "code-fence") {
|
|
@@ -1,25 +1,44 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
var BLOCK_ID_ATTR = "data-block-id";
|
|
3
|
-
var SOURCE_START_ATTR = "data-source-line-start";
|
|
4
|
-
var SOURCE_END_ATTR = "data-source-line-end";
|
|
5
|
-
var HEIGHT_TRANSITION = "top 0.15s ease-out";
|
|
6
|
-
var TOTAL_HEIGHT_TRANSITION = "height 0.15s ease-out";
|
|
1
|
+
// src/dom/skeleton.ts
|
|
7
2
|
var SKELETON_GAP = 8;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
var SKELETON_LINE_HEIGHT = 14;
|
|
4
|
+
var SKELETON_PADDING = 24;
|
|
5
|
+
function createSkeletonHtml(options = {}) {
|
|
6
|
+
const { height, lines } = options;
|
|
7
|
+
const lineCount = lines ?? (height != null ? Math.max(1, Math.min(5, Math.round(Math.max(height - SKELETON_PADDING, 8) / (SKELETON_LINE_HEIGHT + SKELETON_GAP)))) : 3);
|
|
8
|
+
const heightStyle = height != null ? ` style="height:${height}px;min-height:${height}px"` : "";
|
|
9
|
+
const lineEls = [];
|
|
10
|
+
for (let i = 0; i < lineCount; i++) {
|
|
11
|
+
const isLast = i === lineCount - 1;
|
|
12
|
+
lineEls.push(`<div class="owo-mdx-skeleton owo-mdx-skeleton-line"${isLast ? ' style="width:60%"' : ""}></div>`);
|
|
13
|
+
}
|
|
14
|
+
return `<div class="owo-mdx-skeleton-block"${heightStyle} aria-hidden="true">${lineEls.join("")}</div>`;
|
|
11
15
|
}
|
|
16
|
+
var SKELETON_CSS = `
|
|
17
|
+
.owo-mdx-skeleton{background:linear-gradient(90deg,var(--owo-skeleton-base,#e5e7eb) 25%,var(--owo-skeleton-shine,#f3f4f6) 50%,var(--owo-skeleton-base,#e5e7eb) 75%);background-size:200% 100%;animation:owo-mdx-skeleton-shimmer 1.5s ease-in-out infinite;border-radius:4px}
|
|
18
|
+
@keyframes owo-mdx-skeleton-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
|
|
19
|
+
.owo-mdx-skeleton-block{display:flex;flex-direction:column;gap:8px;padding:12px 0}
|
|
20
|
+
.owo-mdx-skeleton-line{height:14px}
|
|
21
|
+
.owo-mdx-skeleton-line:last-child{width:60%}
|
|
22
|
+
`.trim();
|
|
23
|
+
var STYLE_ID = "owo-mdx-skeleton-styles";
|
|
12
24
|
function ensureSkeletonStyles(doc) {
|
|
13
25
|
try {
|
|
14
|
-
if (doc.getElementById?.(
|
|
26
|
+
if (doc.getElementById?.(STYLE_ID)) return;
|
|
15
27
|
const style = doc.createElement("style");
|
|
16
|
-
style.id =
|
|
17
|
-
style.textContent =
|
|
28
|
+
style.id = STYLE_ID;
|
|
29
|
+
style.textContent = SKELETON_CSS;
|
|
18
30
|
const target = doc.head ?? doc.body;
|
|
19
31
|
target?.appendChild(style);
|
|
20
32
|
} catch {
|
|
21
33
|
}
|
|
22
34
|
}
|
|
35
|
+
|
|
36
|
+
// src/virtual/viewport-manager.ts
|
|
37
|
+
var BLOCK_ID_ATTR = "data-block-id";
|
|
38
|
+
var SOURCE_START_ATTR = "data-source-line-start";
|
|
39
|
+
var SOURCE_END_ATTR = "data-source-line-end";
|
|
40
|
+
var HEIGHT_TRANSITION = "top 0.15s ease-out";
|
|
41
|
+
var TOTAL_HEIGHT_TRANSITION = "height 0.15s ease-out";
|
|
23
42
|
var VirtualViewportManager = class {
|
|
24
43
|
root = null;
|
|
25
44
|
scrollContainer = null;
|
|
@@ -93,7 +112,7 @@ var VirtualViewportManager = class {
|
|
|
93
112
|
wrapper.style.left = "0";
|
|
94
113
|
wrapper.style.width = "100%";
|
|
95
114
|
wrapper.style.transition = HEIGHT_TRANSITION;
|
|
96
|
-
wrapper.innerHTML = createSkeletonHtml(layout.height);
|
|
115
|
+
wrapper.innerHTML = createSkeletonHtml({ height: layout.height });
|
|
97
116
|
this.contentLayer.appendChild(wrapper);
|
|
98
117
|
this.mounted.set(block.blockId, wrapper);
|
|
99
118
|
newlyMounted.add(block.blockId);
|
|
@@ -188,5 +207,7 @@ var VirtualViewportManager = class {
|
|
|
188
207
|
};
|
|
189
208
|
|
|
190
209
|
export {
|
|
210
|
+
createSkeletonHtml,
|
|
211
|
+
ensureSkeletonStyles,
|
|
191
212
|
VirtualViewportManager
|
|
192
213
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { OwoMarkCore, OwoMarkEditorInstance,
|
|
1
|
+
import { OwoMarkCore, OwoMarkEditorInstance, PreviewBlock } from '@owomark/core';
|
|
2
2
|
export { DocumentChangeCallback } from '@owomark/core';
|
|
3
|
+
import { O as OwoMarkPreviewEngineOptions, a as OwoMarkPreviewEngine, P as PreviewRendererRegistry, b as PreviewTaskScheduler } from './types-DsL_4tUb.js';
|
|
4
|
+
export { c as PreviewBlockMetadata, d as PreviewBlockRenderer, e as PreviewCacheEntry, f as PreviewRenderContext, g as PreviewRenderPhase, h as PreviewRenderResult, i as PreviewRendererDefinition, j as PreviewRendererMode, k as PreviewStrategy, l as PreviewTaskPriority } from './types-DsL_4tUb.js';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Three-layer view engine for OwoMark.
|
|
@@ -45,77 +47,6 @@ declare function createOwoMarkView(core: OwoMarkCore, element: HTMLElement): Owo
|
|
|
45
47
|
*/
|
|
46
48
|
declare function createOwoMarkVanillaEditor(): OwoMarkEditorInstance;
|
|
47
49
|
|
|
48
|
-
type PreviewRenderPhase = 'idle' | 'rendering' | 'highlighting' | 'ready' | 'error';
|
|
49
|
-
type PreviewCacheEntry = {
|
|
50
|
-
blockId: string;
|
|
51
|
-
renderKey: string;
|
|
52
|
-
html: string;
|
|
53
|
-
highlighted: boolean;
|
|
54
|
-
themeKey: string;
|
|
55
|
-
updatedAt: number;
|
|
56
|
-
};
|
|
57
|
-
type PreviewRenderContext = {
|
|
58
|
-
version: number;
|
|
59
|
-
themeKey: string;
|
|
60
|
-
abortSignal?: AbortSignal;
|
|
61
|
-
/**
|
|
62
|
-
* Line offset to add to source-line attributes in the rendered HTML.
|
|
63
|
-
* When a block starts at line N in the document, the renderer processes
|
|
64
|
-
* its raw content starting from line 1; this offset (N - 1) must be
|
|
65
|
-
* added to `data-source-line-start/end` so scroll sync anchors map
|
|
66
|
-
* to the correct document lines.
|
|
67
|
-
*/
|
|
68
|
-
sourceLineOffset: number;
|
|
69
|
-
};
|
|
70
|
-
type PreviewRenderResult = {
|
|
71
|
-
kind: 'html';
|
|
72
|
-
html: string;
|
|
73
|
-
} | {
|
|
74
|
-
kind: 'dom';
|
|
75
|
-
mount: (container: HTMLElement) => void;
|
|
76
|
-
unmount?: () => void;
|
|
77
|
-
};
|
|
78
|
-
type PreviewRendererMode = 'html-worker-safe' | 'dom-main-thread';
|
|
79
|
-
type PreviewTaskPriority = 'realtime' | 'deferred';
|
|
80
|
-
type PreviewRendererDefinition = {
|
|
81
|
-
mode: PreviewRendererMode;
|
|
82
|
-
priority: PreviewTaskPriority;
|
|
83
|
-
render: PreviewBlockRenderer;
|
|
84
|
-
version: string;
|
|
85
|
-
};
|
|
86
|
-
type PreviewBlockRenderer = (block: PreviewBlock, context: PreviewRenderContext) => Promise<PreviewRenderResult> | PreviewRenderResult;
|
|
87
|
-
type PreviewRendererRegistry = {
|
|
88
|
-
get(kind: PreviewBlockKind): PreviewRendererDefinition | null;
|
|
89
|
-
register(kind: PreviewBlockKind, renderer: PreviewRendererDefinition): void;
|
|
90
|
-
unregister(kind: PreviewBlockKind): void;
|
|
91
|
-
};
|
|
92
|
-
type PreviewStrategy = 'incremental' | 'virtual';
|
|
93
|
-
type OwoMarkPreviewEngineOptions = {
|
|
94
|
-
strategy?: PreviewStrategy;
|
|
95
|
-
themeKey?: string;
|
|
96
|
-
registry?: PreviewRendererRegistry;
|
|
97
|
-
viewportFirst?: boolean;
|
|
98
|
-
/**
|
|
99
|
-
* External block renderer function. When provided, the engine uses this
|
|
100
|
-
* instead of the built-in default renderer. This allows the host to supply
|
|
101
|
-
* its own Markdown pipeline (unified, remark, rehype, Shiki, etc.) without
|
|
102
|
-
* the preview package bundling those heavy dependencies.
|
|
103
|
-
*/
|
|
104
|
-
renderBlock?: (block: PreviewBlock, context: PreviewRenderContext) => Promise<string>;
|
|
105
|
-
/**
|
|
106
|
-
* Called after every DOM mutation — including idle-backfilled and deferred
|
|
107
|
-
* block renders — not just after the synchronous update() return.
|
|
108
|
-
* Use for scroll sync or other post-DOM-update side effects.
|
|
109
|
-
*/
|
|
110
|
-
onContentUpdate?: () => void;
|
|
111
|
-
};
|
|
112
|
-
type OwoMarkPreviewEngine = {
|
|
113
|
-
mount(root: HTMLElement): void;
|
|
114
|
-
destroy(): void;
|
|
115
|
-
update(state: OwoMarkSharedState): void;
|
|
116
|
-
getRenderedVersion(): number;
|
|
117
|
-
};
|
|
118
|
-
|
|
119
50
|
/**
|
|
120
51
|
* OwoMark Preview Engine factory.
|
|
121
52
|
*
|
|
@@ -145,6 +76,18 @@ declare function createRendererRegistry(): PreviewRendererRegistry;
|
|
|
145
76
|
*/
|
|
146
77
|
declare function renderBlockDefault(block: PreviewBlock): string;
|
|
147
78
|
|
|
79
|
+
declare function createPreviewTaskScheduler(options?: {
|
|
80
|
+
poolSize?: number;
|
|
81
|
+
onCrash?: () => void;
|
|
82
|
+
}): PreviewTaskScheduler;
|
|
83
|
+
|
|
84
|
+
type SkeletonOptions = {
|
|
85
|
+
height?: number;
|
|
86
|
+
lines?: number;
|
|
87
|
+
};
|
|
88
|
+
declare function createSkeletonHtml(options?: SkeletonOptions): string;
|
|
89
|
+
declare function ensureSkeletonStyles(doc: Document): void;
|
|
90
|
+
|
|
148
91
|
/**
|
|
149
92
|
* DOM patcher: blockId-driven incremental DOM updates.
|
|
150
93
|
*
|
|
@@ -226,4 +169,4 @@ declare const THEME_DARK_CLASS = "owo-theme-dark";
|
|
|
226
169
|
type OwoMarkThemeName = 'light' | 'dark' | string;
|
|
227
170
|
declare function getThemeClassName(theme: OwoMarkThemeName): string;
|
|
228
171
|
|
|
229
|
-
export {
|
|
172
|
+
export { OwoMarkPreviewEngine, OwoMarkPreviewEngineOptions, type OwoMarkThemeName, type OwoMarkView, type OwoMarkViewEngine, PreviewDomPatcher, PreviewRendererRegistry, PreviewTaskScheduler, SideAnnotationPositioner, type SkeletonOptions, THEME_DARK_CLASS, THEME_LIGHT_CLASS, type ViewEngineOptions, createOwoMarkPreviewEngine, createOwoMarkVanillaEditor, createOwoMarkView, createPreviewTaskScheduler, createRendererRegistry, createSkeletonHtml, createViewEngine, ensureSkeletonStyles, getThemeClassName, renderBlockDefault };
|
package/dist/index.js
CHANGED
|
@@ -7,10 +7,12 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
FALLBACK_BLOCK_HEIGHT,
|
|
9
9
|
estimateBlockHeight
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-KHKPOH74.js";
|
|
11
11
|
import {
|
|
12
|
-
VirtualViewportManager
|
|
13
|
-
|
|
12
|
+
VirtualViewportManager,
|
|
13
|
+
createSkeletonHtml,
|
|
14
|
+
ensureSkeletonStyles
|
|
15
|
+
} from "./chunk-WA6XHBZS.js";
|
|
14
16
|
|
|
15
17
|
// src/editor.ts
|
|
16
18
|
import { createDomAdapter } from "@owomark/core/internal/dom-adapter";
|
|
@@ -809,17 +811,60 @@ var PreviewDomPatcher = class {
|
|
|
809
811
|
};
|
|
810
812
|
|
|
811
813
|
// src/renderer/registry.ts
|
|
814
|
+
var DEFAULT_HEAVY_KINDS = [
|
|
815
|
+
{ kind: "mermaid", meta: { mode: "html-worker-safe", priority: "deferred", heavy: true } },
|
|
816
|
+
{ kind: "katex", meta: { mode: "html-worker-safe", priority: "deferred", heavy: true } },
|
|
817
|
+
{ kind: "math-block", meta: { mode: "html-worker-safe", priority: "deferred", heavy: true } },
|
|
818
|
+
{ kind: "chart", meta: { mode: "html-worker-safe", priority: "deferred", heavy: true } },
|
|
819
|
+
{ kind: "embed", meta: { mode: "html-worker-safe", priority: "deferred", heavy: true } }
|
|
820
|
+
];
|
|
812
821
|
function createRendererRegistry() {
|
|
813
822
|
const renderers = /* @__PURE__ */ new Map();
|
|
823
|
+
const metadata = /* @__PURE__ */ new Map();
|
|
824
|
+
for (const { kind, meta } of DEFAULT_HEAVY_KINDS) {
|
|
825
|
+
metadata.set(kind, meta);
|
|
826
|
+
}
|
|
814
827
|
return {
|
|
815
828
|
get(kind) {
|
|
816
829
|
return renderers.get(kind) ?? null;
|
|
817
830
|
},
|
|
818
831
|
register(kind, renderer) {
|
|
819
832
|
renderers.set(kind, renderer);
|
|
833
|
+
if (!metadata.has(kind)) {
|
|
834
|
+
metadata.set(kind, {
|
|
835
|
+
mode: renderer.mode,
|
|
836
|
+
priority: renderer.priority
|
|
837
|
+
});
|
|
838
|
+
}
|
|
820
839
|
},
|
|
821
840
|
unregister(kind) {
|
|
822
841
|
renderers.delete(kind);
|
|
842
|
+
},
|
|
843
|
+
registerMetadata(kind, meta) {
|
|
844
|
+
metadata.set(kind, meta);
|
|
845
|
+
},
|
|
846
|
+
isHeavy(kind) {
|
|
847
|
+
const meta = metadata.get(kind);
|
|
848
|
+
if (meta?.heavy) return true;
|
|
849
|
+
const def = renderers.get(kind);
|
|
850
|
+
return def?.priority === "deferred" || false;
|
|
851
|
+
},
|
|
852
|
+
getMode(kind) {
|
|
853
|
+
const def = renderers.get(kind);
|
|
854
|
+
if (def) return def.mode;
|
|
855
|
+
return metadata.get(kind)?.mode ?? null;
|
|
856
|
+
},
|
|
857
|
+
getPriority(kind) {
|
|
858
|
+
const def = renderers.get(kind);
|
|
859
|
+
if (def) return def.priority;
|
|
860
|
+
return metadata.get(kind)?.priority ?? null;
|
|
861
|
+
},
|
|
862
|
+
listRegistered() {
|
|
863
|
+
const kinds = /* @__PURE__ */ new Set([
|
|
864
|
+
...renderers.keys(),
|
|
865
|
+
...metadata.keys()
|
|
866
|
+
]);
|
|
867
|
+
return Array.from(kinds);
|
|
823
868
|
}
|
|
824
869
|
};
|
|
825
870
|
}
|
|
@@ -928,7 +973,7 @@ function isDeferred(block, registry) {
|
|
|
928
973
|
const def = registry.get(block.kind);
|
|
929
974
|
return def?.priority === "deferred";
|
|
930
975
|
}
|
|
931
|
-
function createRenderBlockFull(registry, renderCache, externalRenderBlock) {
|
|
976
|
+
function createRenderBlockFull(registry, renderCache, externalRenderBlock, scheduler) {
|
|
932
977
|
return async function renderBlockFull(block, baseContext) {
|
|
933
978
|
const context = {
|
|
934
979
|
...baseContext,
|
|
@@ -936,6 +981,14 @@ function createRenderBlockFull(registry, renderCache, externalRenderBlock) {
|
|
|
936
981
|
};
|
|
937
982
|
const customRenderer = registry.get(block.kind);
|
|
938
983
|
if (customRenderer) {
|
|
984
|
+
if (customRenderer.mode === "html-worker-safe" && customRenderer.workerModuleUrl && scheduler) {
|
|
985
|
+
try {
|
|
986
|
+
const html2 = await scheduler.submitWorkerTask(block, customRenderer, context);
|
|
987
|
+
renderCache.set(block.renderKey, html2);
|
|
988
|
+
return { kind: "html", html: html2 };
|
|
989
|
+
} catch {
|
|
990
|
+
}
|
|
991
|
+
}
|
|
939
992
|
const result = await customRenderer.render(block, context);
|
|
940
993
|
if (result.kind === "html") {
|
|
941
994
|
renderCache.set(block.renderKey, result.html);
|
|
@@ -962,6 +1015,7 @@ function createIncrementalEngine(options) {
|
|
|
962
1015
|
const themeKey = options?.themeKey ?? "";
|
|
963
1016
|
const viewportFirst = options?.viewportFirst ?? false;
|
|
964
1017
|
const registry = options?.registry ?? createRendererRegistry();
|
|
1018
|
+
const scheduler = options?.scheduler;
|
|
965
1019
|
const externalRenderBlock = options?.renderBlock;
|
|
966
1020
|
const onContentUpdate = options?.onContentUpdate;
|
|
967
1021
|
const patcher = new PreviewDomPatcher();
|
|
@@ -972,7 +1026,7 @@ function createIncrementalEngine(options) {
|
|
|
972
1026
|
let pendingAbort = null;
|
|
973
1027
|
let pendingIdleIds = [];
|
|
974
1028
|
let pendingBackfill = /* @__PURE__ */ new Set();
|
|
975
|
-
const renderBlockFull = createRenderBlockFull(registry, renderCache, externalRenderBlock);
|
|
1029
|
+
const renderBlockFull = createRenderBlockFull(registry, renderCache, externalRenderBlock, scheduler);
|
|
976
1030
|
function applyResult(blockId, result) {
|
|
977
1031
|
if (result.kind === "html") {
|
|
978
1032
|
patcher.patchBlockHtml(blockId, result.html);
|
|
@@ -1018,12 +1072,14 @@ function createIncrementalEngine(options) {
|
|
|
1018
1072
|
return {
|
|
1019
1073
|
mount(root) {
|
|
1020
1074
|
patcher.mount(root);
|
|
1075
|
+
ensureSkeletonStyles(root.ownerDocument);
|
|
1021
1076
|
mounted = true;
|
|
1022
1077
|
},
|
|
1023
1078
|
destroy() {
|
|
1024
1079
|
pendingAbort?.abort();
|
|
1025
1080
|
pendingAbort = null;
|
|
1026
1081
|
cancelAllIdle(pendingIdleIds);
|
|
1082
|
+
scheduler?.release();
|
|
1027
1083
|
patcher.destroy();
|
|
1028
1084
|
renderCache.clear();
|
|
1029
1085
|
pendingBackfill.clear();
|
|
@@ -1101,18 +1157,26 @@ function createIncrementalEngine(options) {
|
|
|
1101
1157
|
}
|
|
1102
1158
|
}
|
|
1103
1159
|
if (signal.aborted) return;
|
|
1160
|
+
pendingBackfill = /* @__PURE__ */ new Set();
|
|
1161
|
+
for (const block of realtimeOffscreen) {
|
|
1162
|
+
const height = estimateBlockHeight(block, registry);
|
|
1163
|
+
htmlMap.set(block.blockId, createSkeletonHtml({ height }));
|
|
1164
|
+
pendingBackfill.add(block.blockId);
|
|
1165
|
+
}
|
|
1166
|
+
for (const block of deferredAll) {
|
|
1167
|
+
const height = estimateBlockHeight(block, registry);
|
|
1168
|
+
htmlMap.set(block.blockId, createSkeletonHtml({ height }));
|
|
1169
|
+
pendingBackfill.add(block.blockId);
|
|
1170
|
+
}
|
|
1104
1171
|
patcher.fullRender(blocks, htmlMap);
|
|
1105
1172
|
for (const { blockId, result } of domMounts) {
|
|
1106
1173
|
if (signal.aborted) return;
|
|
1107
1174
|
applyResult(blockId, result);
|
|
1108
1175
|
}
|
|
1109
|
-
pendingBackfill = /* @__PURE__ */ new Set();
|
|
1110
1176
|
for (const block of realtimeOffscreen) {
|
|
1111
|
-
pendingBackfill.add(block.blockId);
|
|
1112
1177
|
scheduleBlockRender(block, baseContext, signal, state.version);
|
|
1113
1178
|
}
|
|
1114
1179
|
for (const block of deferredAll) {
|
|
1115
|
-
pendingBackfill.add(block.blockId);
|
|
1116
1180
|
scheduleBlockRender(block, baseContext, signal, state.version);
|
|
1117
1181
|
}
|
|
1118
1182
|
}
|
|
@@ -1229,6 +1293,7 @@ var OffscreenMeasurer = class {
|
|
|
1229
1293
|
function createVirtualEngine(options) {
|
|
1230
1294
|
const themeKey = options?.themeKey ?? "";
|
|
1231
1295
|
const registry = options?.registry ?? createRendererRegistry();
|
|
1296
|
+
const scheduler = options?.scheduler;
|
|
1232
1297
|
const externalRenderBlock = options?.renderBlock;
|
|
1233
1298
|
const onContentUpdate = options?.onContentUpdate;
|
|
1234
1299
|
const layoutMap = new BlockLayoutMap();
|
|
@@ -1236,7 +1301,7 @@ function createVirtualEngine(options) {
|
|
|
1236
1301
|
const measurer = new OffscreenMeasurer();
|
|
1237
1302
|
const viewport = new VirtualViewportManager();
|
|
1238
1303
|
const renderCache = /* @__PURE__ */ new Map();
|
|
1239
|
-
const renderBlockFull = createRenderBlockFull(registry, renderCache, externalRenderBlock);
|
|
1304
|
+
const renderBlockFull = createRenderBlockFull(registry, renderCache, externalRenderBlock, scheduler);
|
|
1240
1305
|
let renderedVersion = 0;
|
|
1241
1306
|
let lastBlocks = [];
|
|
1242
1307
|
let blockMap = /* @__PURE__ */ new Map();
|
|
@@ -1253,7 +1318,7 @@ function createVirtualEngine(options) {
|
|
|
1253
1318
|
function getInitialHeight(block) {
|
|
1254
1319
|
const cached = heightCache.get(block.renderKey);
|
|
1255
1320
|
if (cached !== void 0) return cached;
|
|
1256
|
-
return estimateBlockHeight(block);
|
|
1321
|
+
return estimateBlockHeight(block, registry);
|
|
1257
1322
|
}
|
|
1258
1323
|
async function renderAndMountBlock(block, baseContext, signal) {
|
|
1259
1324
|
if (signal.aborted) return;
|
|
@@ -1365,7 +1430,7 @@ function createVirtualEngine(options) {
|
|
|
1365
1430
|
heightCache.clear();
|
|
1366
1431
|
layoutMap.invalidateAll((blockId) => {
|
|
1367
1432
|
const block = blockMap.get(blockId);
|
|
1368
|
-
return block ? estimateBlockHeight(block) : FALLBACK_BLOCK_HEIGHT;
|
|
1433
|
+
return block ? estimateBlockHeight(block, registry) : FALLBACK_BLOCK_HEIGHT;
|
|
1369
1434
|
});
|
|
1370
1435
|
return true;
|
|
1371
1436
|
}
|
|
@@ -1400,6 +1465,7 @@ function createVirtualEngine(options) {
|
|
|
1400
1465
|
blockResizeObserver?.disconnect();
|
|
1401
1466
|
blockResizeObserver = null;
|
|
1402
1467
|
observedWrappers.clear();
|
|
1468
|
+
scheduler?.release();
|
|
1403
1469
|
viewport.destroy();
|
|
1404
1470
|
measurer.destroy();
|
|
1405
1471
|
renderCache.clear();
|
|
@@ -1525,11 +1591,185 @@ function createOwoMarkPreviewEngine(options) {
|
|
|
1525
1591
|
return createIncrementalEngine(options);
|
|
1526
1592
|
case "virtual":
|
|
1527
1593
|
return createVirtualEngine(options);
|
|
1594
|
+
case "mdx":
|
|
1595
|
+
throw new Error(
|
|
1596
|
+
'The "mdx" strategy is implemented by @owomark/react OwoMarkPreview, not by @owomark/view createOwoMarkPreviewEngine().'
|
|
1597
|
+
);
|
|
1528
1598
|
default:
|
|
1529
1599
|
throw new Error(`Unknown preview strategy: ${strategy}`);
|
|
1530
1600
|
}
|
|
1531
1601
|
}
|
|
1532
1602
|
|
|
1603
|
+
// src/worker/preview-task-scheduler.ts
|
|
1604
|
+
var MAX_CONSECUTIVE_CRASHES = 3;
|
|
1605
|
+
var CRASH_WINDOW_MS = 3e4;
|
|
1606
|
+
function serializeBlock(block) {
|
|
1607
|
+
return {
|
|
1608
|
+
kind: block.kind,
|
|
1609
|
+
raw: block.raw,
|
|
1610
|
+
blockId: block.blockId,
|
|
1611
|
+
renderKey: block.renderKey,
|
|
1612
|
+
startLine: block.startLine,
|
|
1613
|
+
endLine: block.endLine
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
function createPreviewTaskScheduler(options) {
|
|
1617
|
+
const poolSize = options?.poolSize ?? Math.max(1, (typeof navigator !== "undefined" ? navigator.hardwareConcurrency ?? 4 : 4) - 1);
|
|
1618
|
+
let refCount = 1;
|
|
1619
|
+
let taskCounter = 0;
|
|
1620
|
+
let permanentlyFailed = false;
|
|
1621
|
+
const crashTimestamps = [];
|
|
1622
|
+
const pendingTasks = /* @__PURE__ */ new Map();
|
|
1623
|
+
const workers = [];
|
|
1624
|
+
let nextWorkerIndex = 0;
|
|
1625
|
+
function createWorkerSlot() {
|
|
1626
|
+
try {
|
|
1627
|
+
if (typeof Worker === "undefined") return null;
|
|
1628
|
+
const worker = new Worker(
|
|
1629
|
+
new URL("./preview-render.worker.js", import.meta.url),
|
|
1630
|
+
{ type: "module" }
|
|
1631
|
+
);
|
|
1632
|
+
const slot = { worker, pendingCount: 0, dead: false };
|
|
1633
|
+
worker.onmessage = (e) => {
|
|
1634
|
+
const resp = e.data;
|
|
1635
|
+
slot.pendingCount--;
|
|
1636
|
+
const pending = pendingTasks.get(resp.taskId);
|
|
1637
|
+
if (!pending) return;
|
|
1638
|
+
pendingTasks.delete(resp.taskId);
|
|
1639
|
+
if (resp.ok) {
|
|
1640
|
+
pending.resolve(resp.html);
|
|
1641
|
+
} else {
|
|
1642
|
+
pending.reject(new Error(resp.error));
|
|
1643
|
+
}
|
|
1644
|
+
};
|
|
1645
|
+
worker.onerror = () => {
|
|
1646
|
+
slot.dead = true;
|
|
1647
|
+
slot.pendingCount = 0;
|
|
1648
|
+
worker.terminate();
|
|
1649
|
+
recordCrash();
|
|
1650
|
+
options?.onCrash?.();
|
|
1651
|
+
};
|
|
1652
|
+
return slot;
|
|
1653
|
+
} catch {
|
|
1654
|
+
return null;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
function recordCrash() {
|
|
1658
|
+
const now = Date.now();
|
|
1659
|
+
crashTimestamps.push(now);
|
|
1660
|
+
while (crashTimestamps.length > 0 && now - crashTimestamps[0] > CRASH_WINDOW_MS) {
|
|
1661
|
+
crashTimestamps.shift();
|
|
1662
|
+
}
|
|
1663
|
+
if (crashTimestamps.length >= MAX_CONSECUTIVE_CRASHES) {
|
|
1664
|
+
permanentlyFailed = true;
|
|
1665
|
+
terminateAll();
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
function getLeastBusySlot() {
|
|
1669
|
+
if (permanentlyFailed) return null;
|
|
1670
|
+
let best = null;
|
|
1671
|
+
for (const slot of workers) {
|
|
1672
|
+
if (slot.dead) continue;
|
|
1673
|
+
if (!best || slot.pendingCount < best.pendingCount) {
|
|
1674
|
+
best = slot;
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
if (best) return best;
|
|
1678
|
+
if (workers.length < poolSize) {
|
|
1679
|
+
const slot = createWorkerSlot();
|
|
1680
|
+
if (slot) {
|
|
1681
|
+
workers.push(slot);
|
|
1682
|
+
return slot;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
for (let i = 0; i < workers.length; i++) {
|
|
1686
|
+
if (workers[i].dead) {
|
|
1687
|
+
const slot = createWorkerSlot();
|
|
1688
|
+
if (slot) {
|
|
1689
|
+
workers[i] = slot;
|
|
1690
|
+
return slot;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
return null;
|
|
1695
|
+
}
|
|
1696
|
+
function terminateAll() {
|
|
1697
|
+
for (const slot of workers) {
|
|
1698
|
+
if (!slot.dead) {
|
|
1699
|
+
slot.dead = true;
|
|
1700
|
+
slot.worker.terminate();
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
workers.length = 0;
|
|
1704
|
+
for (const [, pending] of pendingTasks) {
|
|
1705
|
+
pending.reject(new Error("Scheduler terminated"));
|
|
1706
|
+
}
|
|
1707
|
+
pendingTasks.clear();
|
|
1708
|
+
}
|
|
1709
|
+
const scheduler = {
|
|
1710
|
+
submitWorkerTask(block, rendererDef, context) {
|
|
1711
|
+
if (permanentlyFailed || !rendererDef.workerModuleUrl) {
|
|
1712
|
+
return Promise.reject(new Error("Worker unavailable"));
|
|
1713
|
+
}
|
|
1714
|
+
const slot = getLeastBusySlot();
|
|
1715
|
+
if (!slot) {
|
|
1716
|
+
return Promise.reject(new Error("No worker available"));
|
|
1717
|
+
}
|
|
1718
|
+
const taskId = ++taskCounter;
|
|
1719
|
+
return new Promise((resolve, reject) => {
|
|
1720
|
+
pendingTasks.set(taskId, { resolve, reject });
|
|
1721
|
+
slot.pendingCount++;
|
|
1722
|
+
slot.worker.postMessage({
|
|
1723
|
+
type: "render",
|
|
1724
|
+
taskId,
|
|
1725
|
+
block: serializeBlock(block),
|
|
1726
|
+
rendererModuleUrl: rendererDef.workerModuleUrl,
|
|
1727
|
+
context: {
|
|
1728
|
+
version: context.version,
|
|
1729
|
+
themeKey: context.themeKey,
|
|
1730
|
+
sourceLineOffset: context.sourceLineOffset
|
|
1731
|
+
}
|
|
1732
|
+
});
|
|
1733
|
+
});
|
|
1734
|
+
},
|
|
1735
|
+
cancel(taskId) {
|
|
1736
|
+
const pending = pendingTasks.get(taskId);
|
|
1737
|
+
if (pending) {
|
|
1738
|
+
pendingTasks.delete(taskId);
|
|
1739
|
+
pending.reject(new Error("Task cancelled"));
|
|
1740
|
+
}
|
|
1741
|
+
const msg = { type: "cancel", taskId };
|
|
1742
|
+
for (const slot of workers) {
|
|
1743
|
+
if (!slot.dead) slot.worker.postMessage(msg);
|
|
1744
|
+
}
|
|
1745
|
+
},
|
|
1746
|
+
cancelAll() {
|
|
1747
|
+
for (const [, pending] of pendingTasks) {
|
|
1748
|
+
pending.reject(new Error("All tasks cancelled"));
|
|
1749
|
+
}
|
|
1750
|
+
pendingTasks.clear();
|
|
1751
|
+
const msg = { type: "cancel-all" };
|
|
1752
|
+
for (const slot of workers) {
|
|
1753
|
+
if (!slot.dead) {
|
|
1754
|
+
slot.pendingCount = 0;
|
|
1755
|
+
slot.worker.postMessage(msg);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
},
|
|
1759
|
+
acquire() {
|
|
1760
|
+
refCount++;
|
|
1761
|
+
return scheduler;
|
|
1762
|
+
},
|
|
1763
|
+
release() {
|
|
1764
|
+
if (--refCount <= 0) {
|
|
1765
|
+
refCount = 0;
|
|
1766
|
+
terminateAll();
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
return scheduler;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1533
1773
|
// src/dom/side-annotation-positioner.ts
|
|
1534
1774
|
var SideAnnotationPositioner = class {
|
|
1535
1775
|
container;
|
|
@@ -1641,8 +1881,11 @@ export {
|
|
|
1641
1881
|
createOwoMarkPreviewEngine,
|
|
1642
1882
|
createOwoMarkVanillaEditor,
|
|
1643
1883
|
createOwoMarkView,
|
|
1884
|
+
createPreviewTaskScheduler,
|
|
1644
1885
|
createRendererRegistry,
|
|
1886
|
+
createSkeletonHtml,
|
|
1645
1887
|
createViewEngine,
|
|
1888
|
+
ensureSkeletonStyles,
|
|
1646
1889
|
getThemeClassName,
|
|
1647
1890
|
renderBlockDefault
|
|
1648
1891
|
};
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import { PreviewBlock } from '@owomark/core';
|
|
2
|
+
import { P as PreviewRendererRegistry } from '../../types-DsL_4tUb.js';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
* Heuristic height estimation for blocks before measurement.
|
|
5
|
-
*
|
|
6
|
-
* Uses block kind and source line count to produce a rough height estimate.
|
|
7
|
-
* These estimates are used as initial placeholder heights in the coordinate
|
|
8
|
-
* table until real measurement completes.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
declare function estimateBlockHeight(block: PreviewBlock): number;
|
|
4
|
+
declare function estimateBlockHeight(block: PreviewBlock, registry?: PreviewRendererRegistry): number;
|
|
12
5
|
|
|
13
6
|
export { estimateBlockHeight };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { OwoMarkSharedState, PreviewBlockKind, PreviewBlock } from '@owomark/core';
|
|
2
|
+
|
|
3
|
+
type PreviewRenderPhase = 'idle' | 'rendering' | 'highlighting' | 'ready' | 'error';
|
|
4
|
+
type PreviewCacheEntry = {
|
|
5
|
+
blockId: string;
|
|
6
|
+
renderKey: string;
|
|
7
|
+
html: string;
|
|
8
|
+
highlighted: boolean;
|
|
9
|
+
themeKey: string;
|
|
10
|
+
updatedAt: number;
|
|
11
|
+
};
|
|
12
|
+
type PreviewRenderContext = {
|
|
13
|
+
version: number;
|
|
14
|
+
themeKey: string;
|
|
15
|
+
abortSignal?: AbortSignal;
|
|
16
|
+
/**
|
|
17
|
+
* Line offset to add to source-line attributes in the rendered HTML.
|
|
18
|
+
* When a block starts at line N in the document, the renderer processes
|
|
19
|
+
* its raw content starting from line 1; this offset (N - 1) must be
|
|
20
|
+
* added to `data-source-line-start/end` so scroll sync anchors map
|
|
21
|
+
* to the correct document lines.
|
|
22
|
+
*/
|
|
23
|
+
sourceLineOffset: number;
|
|
24
|
+
};
|
|
25
|
+
type PreviewRenderResult = {
|
|
26
|
+
kind: 'html';
|
|
27
|
+
html: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'dom';
|
|
30
|
+
mount: (container: HTMLElement) => void;
|
|
31
|
+
unmount?: () => void;
|
|
32
|
+
};
|
|
33
|
+
type PreviewRendererMode = 'html-worker-safe' | 'dom-main-thread';
|
|
34
|
+
type PreviewTaskPriority = 'realtime' | 'deferred';
|
|
35
|
+
type PreviewRendererDefinition = {
|
|
36
|
+
mode: PreviewRendererMode;
|
|
37
|
+
priority: PreviewTaskPriority;
|
|
38
|
+
render: PreviewBlockRenderer;
|
|
39
|
+
version: string;
|
|
40
|
+
workerModuleUrl?: string;
|
|
41
|
+
};
|
|
42
|
+
type PreviewBlockRenderer = (block: PreviewBlock, context: PreviewRenderContext) => Promise<PreviewRenderResult> | PreviewRenderResult;
|
|
43
|
+
type PreviewBlockMetadata = {
|
|
44
|
+
mode: PreviewRendererMode;
|
|
45
|
+
priority: PreviewTaskPriority;
|
|
46
|
+
heavy?: boolean;
|
|
47
|
+
};
|
|
48
|
+
type PreviewRendererRegistry = {
|
|
49
|
+
get(kind: PreviewBlockKind): PreviewRendererDefinition | null;
|
|
50
|
+
register(kind: PreviewBlockKind, renderer: PreviewRendererDefinition): void;
|
|
51
|
+
unregister(kind: PreviewBlockKind): void;
|
|
52
|
+
registerMetadata(kind: PreviewBlockKind, meta: PreviewBlockMetadata): void;
|
|
53
|
+
isHeavy(kind: PreviewBlockKind): boolean;
|
|
54
|
+
getMode(kind: PreviewBlockKind): PreviewRendererMode | null;
|
|
55
|
+
getPriority(kind: PreviewBlockKind): PreviewTaskPriority | null;
|
|
56
|
+
listRegistered(): PreviewBlockKind[];
|
|
57
|
+
};
|
|
58
|
+
type PreviewTaskScheduler = {
|
|
59
|
+
submitWorkerTask(block: PreviewBlock, rendererDef: PreviewRendererDefinition, context: PreviewRenderContext): Promise<string>;
|
|
60
|
+
cancel(taskId: number): void;
|
|
61
|
+
cancelAll(): void;
|
|
62
|
+
acquire(): PreviewTaskScheduler;
|
|
63
|
+
release(): void;
|
|
64
|
+
};
|
|
65
|
+
type PreviewStrategy = 'incremental' | 'virtual' | 'mdx';
|
|
66
|
+
type OwoMarkPreviewEngineOptions = {
|
|
67
|
+
strategy?: PreviewStrategy;
|
|
68
|
+
themeKey?: string;
|
|
69
|
+
registry?: PreviewRendererRegistry;
|
|
70
|
+
scheduler?: PreviewTaskScheduler;
|
|
71
|
+
viewportFirst?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* External block renderer function. When provided, the engine uses this
|
|
74
|
+
* instead of the built-in default renderer. This allows the host to supply
|
|
75
|
+
* its own Markdown pipeline (unified, remark, rehype, Shiki, etc.) without
|
|
76
|
+
* the preview package bundling those heavy dependencies.
|
|
77
|
+
*/
|
|
78
|
+
renderBlock?: (block: PreviewBlock, context: PreviewRenderContext) => Promise<string>;
|
|
79
|
+
/**
|
|
80
|
+
* Called after every DOM mutation — including idle-backfilled and deferred
|
|
81
|
+
* block renders — not just after the synchronous update() return.
|
|
82
|
+
* Use for scroll sync or other post-DOM-update side effects.
|
|
83
|
+
*/
|
|
84
|
+
onContentUpdate?: () => void;
|
|
85
|
+
};
|
|
86
|
+
type OwoMarkPreviewEngine = {
|
|
87
|
+
mount(root: HTMLElement): void;
|
|
88
|
+
destroy(): void;
|
|
89
|
+
update(state: OwoMarkSharedState): void;
|
|
90
|
+
getRenderedVersion(): number;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export type { OwoMarkPreviewEngineOptions as O, PreviewRendererRegistry as P, OwoMarkPreviewEngine as a, PreviewTaskScheduler as b, PreviewBlockMetadata as c, PreviewBlockRenderer as d, PreviewCacheEntry as e, PreviewRenderContext as f, PreviewRenderPhase as g, PreviewRenderResult as h, PreviewRendererDefinition as i, PreviewRendererMode as j, PreviewStrategy as k, PreviewTaskPriority as l };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owomark/view",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Rendering engine, preview engine, DOM view layer, and official base theme for OwoMark.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"access": "public"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@owomark/core": "^0.1.
|
|
65
|
+
"@owomark/core": "^0.1.6"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"tsup": "^8.5.1"
|
package/src/theme/dark.css
CHANGED
|
@@ -59,8 +59,14 @@
|
|
|
59
59
|
--owo-syntax-code-fence: #7f849c;
|
|
60
60
|
--owo-syntax-code-lang: #fab387;
|
|
61
61
|
--owo-syntax-list-marker: #fab387;
|
|
62
|
+
--owo-syntax-task-marker: #f9c74f;
|
|
63
|
+
--owo-syntax-task-marker-checked: #a6e3a1;
|
|
64
|
+
--owo-syntax-table-separator: #6c7086;
|
|
65
|
+
--owo-syntax-strikethrough: #cdd6f4;
|
|
62
66
|
--owo-syntax-blockquote-marker: #585b70;
|
|
63
67
|
--owo-syntax-hr: #45475a;
|
|
68
|
+
--owo-syntax-html: #cba6f7;
|
|
69
|
+
--owo-syntax-math: #94e2d5;
|
|
64
70
|
|
|
65
71
|
/* Toolbar */
|
|
66
72
|
--owo-toolbar-bg: #252536;
|
package/src/theme/light.css
CHANGED
|
@@ -59,8 +59,14 @@
|
|
|
59
59
|
--owo-syntax-code-fence: #64748b;
|
|
60
60
|
--owo-syntax-code-lang: #ea580c;
|
|
61
61
|
--owo-syntax-list-marker: #ea580c;
|
|
62
|
+
--owo-syntax-task-marker: #d97706;
|
|
63
|
+
--owo-syntax-task-marker-checked: #16a34a;
|
|
64
|
+
--owo-syntax-table-separator: #94a3b8;
|
|
65
|
+
--owo-syntax-strikethrough: #1f2937;
|
|
62
66
|
--owo-syntax-blockquote-marker: #9ca3af;
|
|
63
67
|
--owo-syntax-hr: #d1d5db;
|
|
68
|
+
--owo-syntax-html: #7c3aed;
|
|
69
|
+
--owo-syntax-math: #0f766e;
|
|
64
70
|
|
|
65
71
|
/* Toolbar */
|
|
66
72
|
--owo-toolbar-bg: #f8fafc;
|
package/src/theme/owomark.css
CHANGED
|
@@ -126,6 +126,20 @@
|
|
|
126
126
|
font-weight: 600;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
.owo-syntax-task-marker {
|
|
130
|
+
color: var(--owo-syntax-task-marker, var(--owo-syntax-list-marker, currentColor));
|
|
131
|
+
font-weight: 600;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.owo-syntax-task-marker-checked {
|
|
135
|
+
color: var(--owo-syntax-task-marker-checked, var(--owo-success, currentColor));
|
|
136
|
+
font-weight: 700;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.owo-syntax-table-separator {
|
|
140
|
+
color: var(--owo-syntax-table-separator, var(--owo-syntax-marker, currentColor));
|
|
141
|
+
}
|
|
142
|
+
|
|
129
143
|
.owo-syntax-blockquote-marker {
|
|
130
144
|
color: transparent;
|
|
131
145
|
font-size: inherit;
|
|
@@ -149,15 +163,21 @@
|
|
|
149
163
|
font-size: 0.9em;
|
|
150
164
|
}
|
|
151
165
|
|
|
166
|
+
.owo-syntax-strikethrough {
|
|
167
|
+
color: var(--owo-syntax-strikethrough, var(--owo-editor-text, currentColor));
|
|
168
|
+
text-decoration-line: line-through;
|
|
169
|
+
text-decoration-thickness: 0.08em;
|
|
170
|
+
}
|
|
171
|
+
|
|
152
172
|
.owo-syntax-hr {
|
|
153
173
|
color: var(--owo-syntax-hr, #d1d5db);
|
|
154
174
|
}
|
|
155
175
|
|
|
156
176
|
.owo-syntax-html {
|
|
157
|
-
color: var(--owo-syntax-keyword,
|
|
177
|
+
color: var(--owo-syntax-html, var(--owo-syntax-keyword, currentColor));
|
|
158
178
|
}
|
|
159
179
|
|
|
160
180
|
.owo-syntax-math {
|
|
161
|
-
color: var(--owo-syntax-string,
|
|
181
|
+
color: var(--owo-syntax-math, var(--owo-syntax-string, currentColor));
|
|
162
182
|
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
|
|
163
183
|
}
|