@owomark/core 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.
Files changed (38) hide show
  1. package/README.md +30 -10
  2. package/dist/.build-manifest.json +130 -0
  3. package/dist/browser.d.ts +66 -0
  4. package/dist/browser.js +396 -0
  5. package/dist/chunk-3KTK7CSS.js +82 -0
  6. package/dist/chunk-5JNL3LHV.js +215 -0
  7. package/dist/chunk-ASRCHEFF.js +0 -0
  8. package/dist/chunk-BKJCBEI7.js +397 -0
  9. package/dist/chunk-CJSBFWKS.js +549 -0
  10. package/dist/chunk-GA5EFGSZ.js +5820 -0
  11. package/dist/chunk-OOH46GIF.js +95 -0
  12. package/dist/chunk-ROJILHRQ.js +192 -0
  13. package/dist/chunk-WFPUIPWU.js +34 -0
  14. package/dist/chunk-WXVKSKP3.js +191 -0
  15. package/dist/chunk-YZYJIXGO.js +0 -0
  16. package/dist/editor-core-DbPhn6aI.d.ts +249 -0
  17. package/dist/index.d.ts +77 -86
  18. package/dist/index.js +161 -245
  19. package/dist/internal/dom-adapter.d.ts +37 -1
  20. package/dist/internal/dom-adapter.js +9 -2
  21. package/dist/public-zMo7BR9l.d.ts +469 -0
  22. package/dist/registry-C849sxCo.d.ts +74 -0
  23. package/dist/semantic/components/index.d.ts +9 -0
  24. package/dist/semantic/components/index.js +11 -0
  25. package/dist/semantic/editor/index.d.ts +9 -0
  26. package/dist/semantic/editor/index.js +13 -0
  27. package/dist/semantic/index.d.ts +7 -0
  28. package/dist/semantic/index.js +106 -0
  29. package/dist/semantic/runtime/index.d.ts +9 -0
  30. package/dist/semantic/runtime/index.js +13 -0
  31. package/dist/semantic/shared/index.d.ts +10 -0
  32. package/dist/semantic/shared/index.js +17 -0
  33. package/dist/semantic/syntax/index.d.ts +151 -0
  34. package/dist/semantic/syntax/index.js +63 -0
  35. package/dist/types-DMqYF6Zn.d.ts +83 -0
  36. package/package.json +29 -1
  37. package/dist/chunk-TRLKIMRD.js +0 -3227
  38. package/dist/dom-adapter-CTSJe5Uo.d.ts +0 -469
package/README.md CHANGED
@@ -48,16 +48,7 @@ view.destroy();
48
48
  core.destroy();
49
49
  ```
50
50
 
51
- The standalone DOM editor API is also available through `@owomark/view`:
52
-
53
- ```ts
54
- import { createOwoMarkVanillaEditor } from '@owomark/view';
55
-
56
- const editor = createOwoMarkVanillaEditor();
57
- editor.setMarkdown('# Hello');
58
- editor.mount(document.getElementById('editor')!);
59
- editor.destroy();
60
- ```
51
+ Browser hosts should compose `createOwoMarkCore()` with `createOwoMarkView()`. The public API no longer includes a standalone DOM editor entry.
61
52
 
62
53
  ## Core API
63
54
 
@@ -126,6 +117,29 @@ import {
126
117
  import { tokenizeBlock, tokenizeInline } from '@owomark/core';
127
118
  ```
128
119
 
120
+ Parser extension contracts are also exposed for industrial custom syntax integration:
121
+
122
+ ```ts
123
+ import type {
124
+ InlineSyntaxExtension,
125
+ BlockSyntaxExtension,
126
+ ContainerBlockSyntaxExtension,
127
+ CustomBlockRuntimeBinding,
128
+ } from '@owomark/core';
129
+ ```
130
+
131
+ Current execution support is live for `inline.extensions`, `block.extensions`,
132
+ and `block.containerExtensions`. Block/container extensions now execute through
133
+ the parser, emit formal `custom-block` nodes, and project runtime metadata into
134
+ preview `custom` blocks under the shared `'custom-block-change'` invalidation
135
+ contract.
136
+
137
+ For the formal syntax-layer contracts that now anchor this extension surface, see:
138
+
139
+ - [docs/specs/owomark-math.md](/home/qq/proj/qblog/docs/specs/owomark-math.md)
140
+ - [docs/specs/owomark-side-annotation.md](/home/qq/proj/qblog/docs/specs/owomark-side-annotation.md)
141
+ - [docs/specs/owomark-components-extension.md](/home/qq/proj/qblog/docs/specs/owomark-components-extension.md)
142
+
129
143
  ### Model
130
144
 
131
145
  ```ts
@@ -153,6 +167,12 @@ import { normalizeMarkdownPaste } from '@owomark/core';
153
167
  - Fenced code blocks (`` ``` ``)
154
168
  - Thematic breaks (`---`)
155
169
 
170
+ Formal extension specs:
171
+
172
+ - Math: [docs/specs/owomark-math.md](/home/qq/proj/qblog/docs/specs/owomark-math.md)
173
+ - Side Annotation: [docs/specs/owomark-side-annotation.md](/home/qq/proj/qblog/docs/specs/owomark-side-annotation.md)
174
+ - Components Extension: [docs/specs/owomark-components-extension.md](/home/qq/proj/qblog/docs/specs/owomark-components-extension.md)
175
+
156
176
  ## Preview Projection
157
177
 
158
178
  Convert a parsed document into preview-ready blocks for incremental rendering:
@@ -0,0 +1,130 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "packageName": "@owomark/core",
4
+ "packagePath": "owomark/packages/owomark-core",
5
+ "packageFingerprint": "32024bd874bc287d8de92d6142487720dd3e31af4477e6f91a54b8e8db3aec4e",
6
+ "workspaceFingerprint": "29c91b05781cfd51984ade38b2b582f8dab1013dfaaf87ed5b2b6e9723653157",
7
+ "builtAt": "2026-04-23T03:06:11.526Z",
8
+ "builderVersion": "2026-04-22-industrialization-v1",
9
+ "inputFiles": [
10
+ "owomark/package.json",
11
+ "owomark/packages/owomark-core/package.json",
12
+ "owomark/packages/owomark-core/src/browser-types.ts",
13
+ "owomark/packages/owomark-core/src/browser.ts",
14
+ "owomark/packages/owomark-core/src/clipboard/paste.ts",
15
+ "owomark/packages/owomark-core/src/commands/backspace.ts",
16
+ "owomark/packages/owomark-core/src/commands/char-input.ts",
17
+ "owomark/packages/owomark-core/src/commands/enter.ts",
18
+ "owomark/packages/owomark-core/src/commands/indent.ts",
19
+ "owomark/packages/owomark-core/src/commands/inline-format.ts",
20
+ "owomark/packages/owomark-core/src/commands/slash/builtin-commands.ts",
21
+ "owomark/packages/owomark-core/src/commands/slash/index.ts",
22
+ "owomark/packages/owomark-core/src/commands/slash/registry.ts",
23
+ "owomark/packages/owomark-core/src/commands/slash/slash-manager.ts",
24
+ "owomark/packages/owomark-core/src/commands/slash/types.ts",
25
+ "owomark/packages/owomark-core/src/commands/word-boundary.ts",
26
+ "owomark/packages/owomark-core/src/conformance/document.ts",
27
+ "owomark/packages/owomark-core/src/core/block-context.ts",
28
+ "owomark/packages/owomark-core/src/core/core-events.ts",
29
+ "owomark/packages/owomark-core/src/core/core-selection.ts",
30
+ "owomark/packages/owomark-core/src/core/core-types.ts",
31
+ "owomark/packages/owomark-core/src/core/editor-core.ts",
32
+ "owomark/packages/owomark-core/src/core/index.ts",
33
+ "owomark/packages/owomark-core/src/index.ts",
34
+ "owomark/packages/owomark-core/src/internal/clipboard/paste.ts",
35
+ "owomark/packages/owomark-core/src/internal/commands/word-boundary.ts",
36
+ "owomark/packages/owomark-core/src/internal/dom-adapter.ts",
37
+ "owomark/packages/owomark-core/src/model/dirty-range.ts",
38
+ "owomark/packages/owomark-core/src/model/document/block-factory.ts",
39
+ "owomark/packages/owomark-core/src/model/document/block-id.ts",
40
+ "owomark/packages/owomark-core/src/model/document/container-parser.ts",
41
+ "owomark/packages/owomark-core/src/model/document/container-rules.ts",
42
+ "owomark/packages/owomark-core/src/model/document/decorators.ts",
43
+ "owomark/packages/owomark-core/src/model/document/index.ts",
44
+ "owomark/packages/owomark-core/src/model/document/offsets.ts",
45
+ "owomark/packages/owomark-core/src/model/document/parse-markdown.ts",
46
+ "owomark/packages/owomark-core/src/model/document/reconcile.ts",
47
+ "owomark/packages/owomark-core/src/model/document/serialize.ts",
48
+ "owomark/packages/owomark-core/src/model/document/side-annotations.ts",
49
+ "owomark/packages/owomark-core/src/model/selection/block-cache.ts",
50
+ "owomark/packages/owomark-core/src/model/selection/dom-range.ts",
51
+ "owomark/packages/owomark-core/src/model/selection/index.ts",
52
+ "owomark/packages/owomark-core/src/model/selection/linear-dom.ts",
53
+ "owomark/packages/owomark-core/src/model/virtual-selection.ts",
54
+ "owomark/packages/owomark-core/src/parser/blocks.ts",
55
+ "owomark/packages/owomark-core/src/parser/inline-tokens.ts",
56
+ "owomark/packages/owomark-core/src/patterns/side-annotation.ts",
57
+ "owomark/packages/owomark-core/src/preview/block-id.ts",
58
+ "owomark/packages/owomark-core/src/preview/projection.ts",
59
+ "owomark/packages/owomark-core/src/preview/render-key.ts",
60
+ "owomark/packages/owomark-core/src/render/block-render.ts",
61
+ "owomark/packages/owomark-core/src/render/blockquote-bars.ts",
62
+ "owomark/packages/owomark-core/src/render/dom-patch.ts",
63
+ "owomark/packages/owomark-core/src/scroll/index.ts",
64
+ "owomark/packages/owomark-core/src/scroll/progress-engine.ts",
65
+ "owomark/packages/owomark-core/src/scroll/projection.ts",
66
+ "owomark/packages/owomark-core/src/scroll/scroll-controller.ts",
67
+ "owomark/packages/owomark-core/src/scroll/surface-geometry.ts",
68
+ "owomark/packages/owomark-core/src/scroll/types.ts",
69
+ "owomark/packages/owomark-core/src/semantic/components/index.ts",
70
+ "owomark/packages/owomark-core/src/semantic/editor/index.ts",
71
+ "owomark/packages/owomark-core/src/semantic/index.ts",
72
+ "owomark/packages/owomark-core/src/semantic/runtime/index.ts",
73
+ "owomark/packages/owomark-core/src/semantic/shared/index.ts",
74
+ "owomark/packages/owomark-core/src/semantic/shared/registry.ts",
75
+ "owomark/packages/owomark-core/src/semantic/shared/types.ts",
76
+ "owomark/packages/owomark-core/src/semantic/shared/validation.ts",
77
+ "owomark/packages/owomark-core/src/semantic/syntax/builtin/math.ts",
78
+ "owomark/packages/owomark-core/src/semantic/syntax/builtin/side-annotation.ts",
79
+ "owomark/packages/owomark-core/src/semantic/syntax/index.ts",
80
+ "owomark/packages/owomark-core/src/state/shared-state.ts",
81
+ "owomark/packages/owomark-core/src/transforms/image-size.ts",
82
+ "owomark/packages/owomark-core/src/types/public.ts",
83
+ "owomark/packages/owomark-core/src/virtual/editor-virtual-list.ts",
84
+ "owomark/packages/owomark-core/tsconfig.json",
85
+ "owomark/packages/owomark-core/tsup.config.ts",
86
+ "package-lock.json"
87
+ ],
88
+ "artifactFiles": [
89
+ "dist/browser.d.ts",
90
+ "dist/browser.js",
91
+ "dist/chunk-3KTK7CSS.js",
92
+ "dist/chunk-5JNL3LHV.js",
93
+ "dist/chunk-ASRCHEFF.js",
94
+ "dist/chunk-BGXCXQZP.js",
95
+ "dist/chunk-BKJCBEI7.js",
96
+ "dist/chunk-CJSBFWKS.js",
97
+ "dist/chunk-MPIWZLI3.js",
98
+ "dist/chunk-OOH46GIF.js",
99
+ "dist/chunk-ROJILHRQ.js",
100
+ "dist/chunk-SHVM2TPY.js",
101
+ "dist/chunk-WFPUIPWU.js",
102
+ "dist/chunk-WXVKSKP3.js",
103
+ "dist/chunk-YZYJIXGO.js",
104
+ "dist/editor-core-DtPL3N3I.d.ts",
105
+ "dist/index.d.ts",
106
+ "dist/index.js",
107
+ "dist/internal/clipboard/paste.d.ts",
108
+ "dist/internal/clipboard/paste.js",
109
+ "dist/internal/commands/word-boundary.d.ts",
110
+ "dist/internal/commands/word-boundary.js",
111
+ "dist/internal/dom-adapter.d.ts",
112
+ "dist/internal/dom-adapter.js",
113
+ "dist/public-Dbsa3XRM.d.ts",
114
+ "dist/registry-D4LeNPxw.d.ts",
115
+ "dist/semantic/components/index.d.ts",
116
+ "dist/semantic/components/index.js",
117
+ "dist/semantic/editor/index.d.ts",
118
+ "dist/semantic/editor/index.js",
119
+ "dist/semantic/index.d.ts",
120
+ "dist/semantic/index.js",
121
+ "dist/semantic/runtime/index.d.ts",
122
+ "dist/semantic/runtime/index.js",
123
+ "dist/semantic/shared/index.d.ts",
124
+ "dist/semantic/shared/index.js",
125
+ "dist/semantic/syntax/index.d.ts",
126
+ "dist/semantic/syntax/index.js",
127
+ "dist/types-UF6oV7Xu.d.ts"
128
+ ],
129
+ "localDependencyFingerprints": {}
130
+ }
@@ -0,0 +1,66 @@
1
+ import { O as OwoMarkSelection, a as OwoMarkDocument, D as DirtyRange, I as InlineTokenType, e as InlineToken, f as PreviewBlock } from './public-zMo7BR9l.js';
2
+ import { O as OwoMarkScrollProjectionSnapshot, a as OwoMarkSurfaceGeometrySnapshot, b as OwoMarkScrollController } from './types-DMqYF6Zn.js';
3
+
4
+ declare function invalidateBlockCache(root: HTMLElement): void;
5
+
6
+ declare function domRangeToOffset(root: HTMLElement, range: {
7
+ startContainer: Node;
8
+ startOffset: number;
9
+ endContainer: Node;
10
+ endOffset: number;
11
+ }): OwoMarkSelection | null;
12
+ declare function offsetToDomRange(root: HTMLElement, selection: OwoMarkSelection): Range | null;
13
+
14
+ declare function restoreSelection(root: HTMLElement, selection: OwoMarkSelection): void;
15
+ declare function readSelection(root: HTMLElement): OwoMarkSelection | null;
16
+
17
+ /**
18
+ * Incremental DOM patch: only re-render dirty blocks.
19
+ */
20
+
21
+ /**
22
+ * Full render: build entire editor DOM from document model.
23
+ */
24
+ declare function fullRender(root: HTMLElement, doc: OwoMarkDocument): void;
25
+ /**
26
+ * Incremental patch: only re-render blocks in dirty range.
27
+ */
28
+ declare function patchDirtyBlocks(root: HTMLElement, doc: OwoMarkDocument, dirty: DirtyRange): void;
29
+ /**
30
+ * Detect changed blocks between old and new document, then render minimally.
31
+ * Falls back to full render when block count changes or no dirty blocks found
32
+ * despite text difference (structural shift).
33
+ */
34
+ declare function detectAndRenderDirty(root: HTMLElement, oldDoc: OwoMarkDocument, newDoc: OwoMarkDocument, fullFallback?: boolean): void;
35
+
36
+ /**
37
+ * Render a block's inline tokens into DOM nodes within a block container.
38
+ * Each block is a <div data-owo-block="index"> containing text/span nodes.
39
+ */
40
+
41
+ declare const TOKEN_CLASS_EXEMPTIONS: Set<"text" | "strong-marker" | "strong" | "emphasis-marker" | "emphasis" | "custom-inline-marker" | "custom-inline" | "code-marker" | "code" | "link-bracket" | "link-text" | "link-url" | "reference-bracket" | "reference-label" | "image-marker" | "image-alt" | "image-url" | "heading-marker" | "list-marker" | "task-marker" | "task-marker-checked" | "table-separator" | "blockquote-marker" | "fence-marker" | "fence-lang" | "code-block-text" | "strikethrough-marker" | "strikethrough" | "math-marker" | "math-text" | "hr" | "html">;
42
+ declare const TOKEN_TO_CLASS: Record<InlineTokenType, string>;
43
+ declare const BLOCK_TYPE_TO_CLASS: Record<string, string>;
44
+ declare function createBlockElement(doc: Document, tokens: InlineToken[], blockIndex: number, blockType: string, headingLevel?: number, depth?: number): HTMLDivElement;
45
+ /**
46
+ * Update an existing block element's content with new tokens.
47
+ * Returns true if the block was modified.
48
+ */
49
+ declare function updateBlockElement(doc: Document, existing: HTMLDivElement, tokens: InlineToken[], blockType: string, headingLevel?: number, depth?: number): boolean;
50
+
51
+ declare const BQ_STEP: number;
52
+ /**
53
+ * Build the CSS box-shadow value for nested blockquote bars.
54
+ * Returns null when depth <= 1 (no extra bars needed — the first bar is
55
+ * rendered by the block's own border/class styling).
56
+ */
57
+ declare function buildBlockquoteBarsBoxShadow(depth: number): string | null;
58
+
59
+ declare function measureEditorSurfaceGeometry(projection: OwoMarkScrollProjectionSnapshot, editorRoot: HTMLElement, _body?: string): OwoMarkSurfaceGeometrySnapshot;
60
+ declare function measurePreviewSurfaceGeometry(projection: OwoMarkScrollProjectionSnapshot, previewRoot: HTMLElement): OwoMarkSurfaceGeometrySnapshot;
61
+
62
+ declare function createScrollController(): OwoMarkScrollController & {
63
+ updateProjection(previewBlocks: readonly PreviewBlock[], documentRevision: number): void;
64
+ };
65
+
66
+ export { BLOCK_TYPE_TO_CLASS, BQ_STEP, TOKEN_CLASS_EXEMPTIONS, TOKEN_TO_CLASS, buildBlockquoteBarsBoxShadow, createBlockElement, createScrollController, detectAndRenderDirty, domRangeToOffset, fullRender, invalidateBlockCache, measureEditorSurfaceGeometry, measurePreviewSurfaceGeometry, offsetToDomRange, patchDirtyBlocks, readSelection, restoreSelection, updateBlockElement };
@@ -0,0 +1,396 @@
1
+ import {
2
+ DEFAULT_VIEWPORT_ANCHOR_RATIO,
3
+ buildScrollProjectionSnapshot,
4
+ resolveScrollPosition,
5
+ resolveScrollTopFromPosition
6
+ } from "./chunk-5JNL3LHV.js";
7
+ import {
8
+ BLOCK_TYPE_TO_CLASS,
9
+ BQ_STEP,
10
+ TOKEN_CLASS_EXEMPTIONS,
11
+ TOKEN_TO_CLASS,
12
+ buildBlockquoteBarsBoxShadow,
13
+ createBlockElement,
14
+ detectAndRenderDirty,
15
+ domRangeToOffset,
16
+ fullRender,
17
+ invalidateBlockCache,
18
+ offsetToDomRange,
19
+ patchDirtyBlocks,
20
+ readSelection,
21
+ restoreSelection,
22
+ updateBlockElement
23
+ } from "./chunk-CJSBFWKS.js";
24
+ import {
25
+ deriveSourceKey
26
+ } from "./chunk-WFPUIPWU.js";
27
+
28
+ // src/scroll/surface-geometry.ts
29
+ var geometryRevisionCounter = 0;
30
+ function getOffsetInContainer(el, container) {
31
+ const elRect = el.getBoundingClientRect();
32
+ const containerRect = container.getBoundingClientRect();
33
+ return elRect.top - containerRect.top + container.scrollTop;
34
+ }
35
+ function collectMeasuredSegments(root, selector) {
36
+ const elements = root.querySelectorAll(selector);
37
+ const measured = /* @__PURE__ */ new Map();
38
+ for (const el of elements) {
39
+ const dataset = el.dataset ?? {};
40
+ const sourceStart = Number(dataset.sourceLineStart);
41
+ const sourceEnd = Number(dataset.sourceLineEnd) || sourceStart;
42
+ const sourceKey = dataset.sourceKey ?? (Number.isFinite(sourceStart) ? deriveSourceKey(sourceStart, sourceEnd) : null);
43
+ if (!sourceKey) {
44
+ continue;
45
+ }
46
+ const top = getOffsetInContainer(el, root);
47
+ const bottom = top + el.offsetHeight;
48
+ const existing = measured.get(sourceKey);
49
+ if (existing) {
50
+ existing.top = Math.min(existing.top, top);
51
+ existing.bottom = Math.max(existing.bottom, bottom);
52
+ continue;
53
+ }
54
+ measured.set(sourceKey, { sourceKey, top, bottom });
55
+ }
56
+ return measured;
57
+ }
58
+ function measureEditorSurfaceGeometry(projection, editorRoot, _body) {
59
+ const measured = collectMeasuredSegments(editorRoot, "[data-source-key], [data-source-line-start]");
60
+ if (measured.size === 0) {
61
+ const legacyBlocks = editorRoot.querySelectorAll("[data-owo-block]");
62
+ legacyBlocks.forEach((el, index) => {
63
+ const projectionSegment = projection.segments[index];
64
+ if (!projectionSegment) {
65
+ return;
66
+ }
67
+ const top = getOffsetInContainer(el, editorRoot);
68
+ measured.set(projectionSegment.sourceKey, {
69
+ sourceKey: projectionSegment.sourceKey,
70
+ top,
71
+ bottom: top + el.offsetHeight
72
+ });
73
+ });
74
+ }
75
+ const segments = [];
76
+ let unmeasuredCount = 0;
77
+ for (const seg of projection.segments) {
78
+ const geo = measured.get(seg.sourceKey);
79
+ if (geo) {
80
+ segments.push({
81
+ sourceKey: seg.sourceKey,
82
+ top: geo.top,
83
+ bottom: geo.bottom,
84
+ height: geo.bottom - geo.top,
85
+ measured: true
86
+ });
87
+ } else {
88
+ unmeasuredCount++;
89
+ segments.push({
90
+ sourceKey: seg.sourceKey,
91
+ top: 0,
92
+ bottom: 0,
93
+ height: 0,
94
+ measured: false,
95
+ degradedReason: "segment-not-mounted-yet"
96
+ });
97
+ }
98
+ }
99
+ const status = unmeasuredCount === 0 ? "ready" : unmeasuredCount === segments.length ? "pending" : "partial";
100
+ geometryRevisionCounter += 1;
101
+ return {
102
+ documentRevision: projection.documentRevision,
103
+ projectionRevision: projection.projectionRevision,
104
+ geometryRevision: geometryRevisionCounter,
105
+ surface: "editor",
106
+ status,
107
+ segments
108
+ };
109
+ }
110
+ function measurePreviewSurfaceGeometry(projection, previewRoot) {
111
+ const measured = collectMeasuredSegments(previewRoot, "[data-source-line-start], [data-source-key]");
112
+ const segments = [];
113
+ let unmeasuredCount = 0;
114
+ for (const seg of projection.segments) {
115
+ const geo = measured.get(seg.sourceKey);
116
+ if (geo) {
117
+ segments.push({
118
+ sourceKey: seg.sourceKey,
119
+ top: geo.top,
120
+ bottom: geo.bottom,
121
+ height: geo.bottom - geo.top,
122
+ measured: true
123
+ });
124
+ } else {
125
+ unmeasuredCount++;
126
+ segments.push({
127
+ sourceKey: seg.sourceKey,
128
+ top: 0,
129
+ bottom: 0,
130
+ height: 0,
131
+ measured: false,
132
+ degradedReason: "segment-not-mounted-yet"
133
+ });
134
+ }
135
+ }
136
+ const status = unmeasuredCount === 0 ? "ready" : unmeasuredCount === segments.length ? "pending" : "partial";
137
+ geometryRevisionCounter += 1;
138
+ return {
139
+ documentRevision: projection.documentRevision,
140
+ projectionRevision: projection.projectionRevision,
141
+ geometryRevision: geometryRevisionCounter,
142
+ surface: "preview",
143
+ status,
144
+ segments
145
+ };
146
+ }
147
+
148
+ // src/scroll/scroll-controller.ts
149
+ function getMeasuredSegment(snapshot, sourceKey) {
150
+ return snapshot.segments.find((segment) => segment.sourceKey === sourceKey && segment.measured) ?? null;
151
+ }
152
+ function applyScrollTop(root, scrollTop, behavior) {
153
+ if (typeof root.scrollTo === "function") {
154
+ root.scrollTo({ top: scrollTop, behavior });
155
+ return;
156
+ }
157
+ root.scrollTop = scrollTop;
158
+ }
159
+ function createScrollController() {
160
+ let projectionSnapshot = {
161
+ documentRevision: 0,
162
+ projectionRevision: 0,
163
+ segments: []
164
+ };
165
+ let lastResolvedPosition = null;
166
+ const geometrySnapshots = {
167
+ editor: null,
168
+ preview: null
169
+ };
170
+ const boundRoots = {
171
+ editor: null,
172
+ preview: null
173
+ };
174
+ const projectionListeners = /* @__PURE__ */ new Set();
175
+ const geometryListeners = {
176
+ editor: /* @__PURE__ */ new Set(),
177
+ preview: /* @__PURE__ */ new Set()
178
+ };
179
+ function notifyProjectionListeners() {
180
+ for (const listener of projectionListeners) {
181
+ listener(projectionSnapshot);
182
+ }
183
+ }
184
+ function notifyGeometryListeners(surface) {
185
+ const snapshot = geometrySnapshots[surface];
186
+ if (!snapshot) return;
187
+ for (const listener of geometryListeners[surface]) {
188
+ listener(snapshot);
189
+ }
190
+ }
191
+ function getViewport(root) {
192
+ return {
193
+ scrollTop: root.scrollTop,
194
+ clientHeight: root.clientHeight,
195
+ scrollHeight: root.scrollHeight
196
+ };
197
+ }
198
+ function remeasureSurface(surface) {
199
+ const root = boundRoots[surface];
200
+ if (!root || projectionSnapshot.segments.length === 0) {
201
+ return null;
202
+ }
203
+ const snapshot = surface === "editor" ? measureEditorSurfaceGeometry(projectionSnapshot, root) : measurePreviewSurfaceGeometry(projectionSnapshot, root);
204
+ geometrySnapshots[surface] = snapshot;
205
+ notifyGeometryListeners(surface);
206
+ return snapshot;
207
+ }
208
+ function resolveTarget(position, surface, viewportHeight, scrollHeight, viewportAnchorRatio = DEFAULT_VIEWPORT_ANCHOR_RATIO) {
209
+ const geometry = geometrySnapshots[surface];
210
+ if (!geometry) {
211
+ return { ok: false, reason: "surface-not-bound" };
212
+ }
213
+ if (position.documentRevision !== projectionSnapshot.documentRevision) {
214
+ return { ok: false, reason: "document-revision-mismatch" };
215
+ }
216
+ if (position.projectionRevision !== projectionSnapshot.projectionRevision) {
217
+ return { ok: false, reason: "projection-revision-mismatch" };
218
+ }
219
+ if (!projectionSnapshot.segments.some((segment) => segment.sourceKey === position.sourceKey)) {
220
+ return { ok: false, reason: "source-key-not-found" };
221
+ }
222
+ if (geometry.status === "pending" || geometry.status === "stale") {
223
+ return { ok: false, reason: "target-geometry-pending" };
224
+ }
225
+ const scrollTop = resolveScrollTopFromPosition(
226
+ position,
227
+ projectionSnapshot,
228
+ geometry,
229
+ viewportHeight,
230
+ scrollHeight,
231
+ viewportAnchorRatio
232
+ );
233
+ if (scrollTop == null) {
234
+ return { ok: false, reason: "target-geometry-pending" };
235
+ }
236
+ return {
237
+ ok: true,
238
+ scrollTop,
239
+ precision: geometry.status === "ready" ? "exact" : "provisional"
240
+ };
241
+ }
242
+ function maybeCompensate(surface) {
243
+ const root = boundRoots[surface];
244
+ if (!root || !lastResolvedPosition) {
245
+ return;
246
+ }
247
+ const result = resolveTarget(
248
+ lastResolvedPosition,
249
+ surface,
250
+ root.clientHeight,
251
+ root.scrollHeight
252
+ );
253
+ if (!result.ok) {
254
+ return;
255
+ }
256
+ if (Math.abs(root.scrollTop - result.scrollTop) < 1) {
257
+ return;
258
+ }
259
+ applyScrollTop(root, result.scrollTop, "auto");
260
+ }
261
+ return {
262
+ getScrollProjectionSnapshot() {
263
+ return projectionSnapshot;
264
+ },
265
+ subscribeScrollProjectionSnapshot(listener) {
266
+ projectionListeners.add(listener);
267
+ return () => {
268
+ projectionListeners.delete(listener);
269
+ };
270
+ },
271
+ updateProjection(previewBlocks, documentRevision) {
272
+ projectionSnapshot = buildScrollProjectionSnapshot(previewBlocks, documentRevision);
273
+ geometrySnapshots.editor = null;
274
+ geometrySnapshots.preview = null;
275
+ notifyProjectionListeners();
276
+ if (boundRoots.editor) remeasureSurface("editor");
277
+ if (boundRoots.preview) remeasureSurface("preview");
278
+ },
279
+ getSurfaceGeometrySnapshot(surface) {
280
+ return geometrySnapshots[surface];
281
+ },
282
+ subscribeSurfaceGeometrySnapshot(surface, listener) {
283
+ geometryListeners[surface].add(listener);
284
+ return () => {
285
+ geometryListeners[surface].delete(listener);
286
+ };
287
+ },
288
+ bindSurface(surface, root) {
289
+ boundRoots[surface] = root;
290
+ remeasureSurface(surface);
291
+ },
292
+ unbindSurface(surface, root) {
293
+ if (root && boundRoots[surface] && boundRoots[surface] !== root) {
294
+ return;
295
+ }
296
+ boundRoots[surface] = null;
297
+ geometrySnapshots[surface] = null;
298
+ },
299
+ measureSurface(surface) {
300
+ const snapshot = remeasureSurface(surface);
301
+ maybeCompensate(surface);
302
+ return snapshot;
303
+ },
304
+ invalidateGeometry(surface) {
305
+ const current = geometrySnapshots[surface];
306
+ if (!current) {
307
+ return;
308
+ }
309
+ geometrySnapshots[surface] = {
310
+ ...current,
311
+ status: "stale"
312
+ };
313
+ notifyGeometryListeners(surface);
314
+ },
315
+ resolveScrollPosition(surface, viewport, viewportAnchorRatio = DEFAULT_VIEWPORT_ANCHOR_RATIO) {
316
+ const geometry = geometrySnapshots[surface];
317
+ if (!geometry || geometry.projectionRevision !== projectionSnapshot.projectionRevision) {
318
+ return null;
319
+ }
320
+ const position = resolveScrollPosition(
321
+ projectionSnapshot,
322
+ geometry,
323
+ viewport,
324
+ viewportAnchorRatio
325
+ );
326
+ if (position) {
327
+ lastResolvedPosition = position;
328
+ }
329
+ return position;
330
+ },
331
+ resolveScrollTopFromPosition(position, surface, viewportHeight, scrollHeight, viewportAnchorRatio = DEFAULT_VIEWPORT_ANCHOR_RATIO) {
332
+ return resolveTarget(position, surface, viewportHeight, scrollHeight, viewportAnchorRatio);
333
+ },
334
+ scrollToScrollPosition(surface, position, options) {
335
+ const root = boundRoots[surface];
336
+ if (!root) {
337
+ return { ok: false, reason: "surface-not-bound" };
338
+ }
339
+ const baseResult = resolveTarget(
340
+ position,
341
+ surface,
342
+ root.clientHeight,
343
+ root.scrollHeight
344
+ );
345
+ if (!baseResult.ok) {
346
+ return baseResult;
347
+ }
348
+ let targetScrollTop = baseResult.scrollTop;
349
+ const measuredSeg = geometrySnapshots[surface] ? getMeasuredSegment(geometrySnapshots[surface], position.sourceKey) : null;
350
+ if (measuredSeg) {
351
+ const offsetTop = options?.offsetTop ?? 0;
352
+ switch (options?.blockAlign) {
353
+ case "start":
354
+ targetScrollTop = measuredSeg.top - offsetTop;
355
+ break;
356
+ case "center":
357
+ targetScrollTop = measuredSeg.top - (root.clientHeight - measuredSeg.height) / 2 - offsetTop;
358
+ break;
359
+ case "end":
360
+ targetScrollTop = measuredSeg.bottom - root.clientHeight - offsetTop;
361
+ break;
362
+ default:
363
+ break;
364
+ }
365
+ }
366
+ const maxScroll = Math.max(0, root.scrollHeight - root.clientHeight);
367
+ const clamped = Math.max(0, Math.min(maxScroll, targetScrollTop));
368
+ lastResolvedPosition = position;
369
+ applyScrollTop(root, clamped, options?.behavior ?? "auto");
370
+ return {
371
+ ...baseResult,
372
+ scrollTop: clamped
373
+ };
374
+ }
375
+ };
376
+ }
377
+ export {
378
+ BLOCK_TYPE_TO_CLASS,
379
+ BQ_STEP,
380
+ TOKEN_CLASS_EXEMPTIONS,
381
+ TOKEN_TO_CLASS,
382
+ buildBlockquoteBarsBoxShadow,
383
+ createBlockElement,
384
+ createScrollController,
385
+ detectAndRenderDirty,
386
+ domRangeToOffset,
387
+ fullRender,
388
+ invalidateBlockCache,
389
+ measureEditorSurfaceGeometry,
390
+ measurePreviewSurfaceGeometry,
391
+ offsetToDomRange,
392
+ patchDirtyBlocks,
393
+ readSelection,
394
+ restoreSelection,
395
+ updateBlockElement
396
+ };