@owomark/core 0.1.1 → 0.1.3
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 +3 -3
- package/dist/{chunk-RINTEGPG.js → chunk-TRLKIMRD.js} +26 -0
- package/dist/{dom-adapter-BwEilZtY.d.ts → dom-adapter-CTSJe5Uo.d.ts} +6 -0
- package/dist/index.d.ts +17 -7
- package/dist/index.js +23 -12
- package/dist/internal/dom-adapter.d.ts +1 -1
- package/dist/internal/dom-adapter.js +1 -1
- package/package.json +1 -9
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Framework-agnostic Markdown editor engine. Provides document model, incremental rendering, input handling, commands, and selection management for a single-layer `contenteditable` editing experience.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Install the official package: `@owomark/core`.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -48,7 +48,7 @@ view.destroy();
|
|
|
48
48
|
core.destroy();
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
The standalone DOM editor API is also available through `@owomark/view`:
|
|
52
52
|
|
|
53
53
|
```ts
|
|
54
54
|
import { createOwoMarkVanillaEditor } from '@owomark/view';
|
|
@@ -193,7 +193,7 @@ const dirtyRange = computePreviewDirtyRange(previousBlocks, blocks);
|
|
|
193
193
|
```
|
|
194
194
|
|
|
195
195
|
- Adjacent same-type container blocks (lists, blockquotes) are grouped into a single `PreviewBlock`
|
|
196
|
-
- Each block has a
|
|
196
|
+
- Each block has a content-based `blockId` (`{renderKey}#{occurrenceIndex}`) for stable DOM reconciliation
|
|
197
197
|
- `renderKey` (djb2 hash of `kind:theme:raw`) drives cache invalidation
|
|
198
198
|
|
|
199
199
|
### Preview Types
|
|
@@ -194,6 +194,7 @@ function tokenizeInline(raw, baseOffset = 0) {
|
|
|
194
194
|
|
|
195
195
|
// src/parser/blocks.ts
|
|
196
196
|
var MATH_FENCE_RE = /^\$\$\s*$/;
|
|
197
|
+
var SINGLE_LINE_MATH_BLOCK_RE = /^ {0,3}\$\$(?!\s*$)([^\n]*?)(?<!\\)\$\$\s*$/;
|
|
197
198
|
function tokenizeBlock(raw, blockType, baseOffset = 0) {
|
|
198
199
|
if (blockType === "code-fence") {
|
|
199
200
|
return tokenizeCodeFence(raw, baseOffset);
|
|
@@ -207,6 +208,18 @@ function tokenizeBlock(raw, blockType, baseOffset = 0) {
|
|
|
207
208
|
});
|
|
208
209
|
}
|
|
209
210
|
if (blockType === "math-block") {
|
|
211
|
+
const singleLineMatch = raw.match(SINGLE_LINE_MATH_BLOCK_RE);
|
|
212
|
+
if (singleLineMatch) {
|
|
213
|
+
const leadingWhitespace = raw.match(/^ {0,3}/)?.[0] ?? "";
|
|
214
|
+
const trimmed = raw.slice(leadingWhitespace.length).replace(/\s+$/, "");
|
|
215
|
+
const inner = trimmed.slice(2, -2);
|
|
216
|
+
const start = baseOffset + leadingWhitespace.length;
|
|
217
|
+
return [
|
|
218
|
+
{ type: "fence-marker", text: "$$", start, end: start + 2 },
|
|
219
|
+
{ type: "math-text", text: inner, start: start + 2, end: start + 2 + inner.length },
|
|
220
|
+
{ type: "fence-marker", text: "$$", start: start + 2 + inner.length, end: start + 4 + inner.length }
|
|
221
|
+
];
|
|
222
|
+
}
|
|
210
223
|
return tokenizeMultiLineBlock(raw, baseOffset, (line, _i, isFirst, isLast) => {
|
|
211
224
|
if ((isFirst || isLast) && MATH_FENCE_RE.test(line)) {
|
|
212
225
|
return [{ type: "fence-marker", text: line, start: -1, end: -1 }];
|
|
@@ -409,6 +422,19 @@ function parseMarkdownToDocument(markdown, genId) {
|
|
|
409
422
|
continue;
|
|
410
423
|
}
|
|
411
424
|
}
|
|
425
|
+
if (SINGLE_LINE_MATH_BLOCK_RE.test(line)) {
|
|
426
|
+
const tokens2 = tokenizeBlock(line, "math-block", offset);
|
|
427
|
+
blocks.push({
|
|
428
|
+
type: "math-block",
|
|
429
|
+
raw: line,
|
|
430
|
+
id: genBlockId(),
|
|
431
|
+
depth: 0,
|
|
432
|
+
decorators: tokensToDecorators(tokens2, line, offset)
|
|
433
|
+
});
|
|
434
|
+
offset += line.length + 1;
|
|
435
|
+
i++;
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
412
438
|
if (MATH_FENCE_RE.test(line)) {
|
|
413
439
|
let closeIndex = -1;
|
|
414
440
|
for (let j = i + 1; j < lines.length; j++) {
|
|
@@ -130,6 +130,12 @@ type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
|
130
130
|
[key: string]: JsonValue;
|
|
131
131
|
};
|
|
132
132
|
type PreviewBlock = {
|
|
133
|
+
/**
|
|
134
|
+
* Content-based identity: `{renderKey}#{occurrenceIndex}`.
|
|
135
|
+
* Stable across blank line edits — only changes when block content changes.
|
|
136
|
+
* Used as the DOM wrapper lifecycle key by the virtual preview engine.
|
|
137
|
+
* For line-range positional mapping (scroll sync), use startLine/endLine.
|
|
138
|
+
*/
|
|
133
139
|
blockId: string;
|
|
134
140
|
kind: PreviewBlockKind;
|
|
135
141
|
raw: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as BlockNode, a as BlockContextType, O as OwoMarkSelection, C as CoreStateSnapshot, S as SlashState, b as CommandRegistry, c as OwoMarkDocument, D as DirtyRange, P as PreviewBlockKind, d as PreviewBlock, e as BlockTransform, f as OwoMarkSharedStateController, I as InlineToken, g as InlineTokenType } from './dom-adapter-
|
|
2
|
-
export { h as BeforeInputIntent, i as BlockContext, j as BlockContextListener, k as BlockInsertType, l as BlockType, m as CommandContext, n as CommandDefinition, o as CompositionState, p as CoreApplyAction, q as CoreApplyResult, r as CoreSelectionSnapshot, s as CreateOwoMarkCoreOptions, t as Decorator, u as DecoratorType, v as DocumentChangeCallback, w as DomAdapterInstance, H as HeadingLevel, x as HistoryEntry, y as IndentMode, z as IndentResult, J as JsonPrimitive, A as JsonValue, K as KeyDownIntent, L as Leaf, E as OwoMarkCommands, F as OwoMarkCore, G as OwoMarkEditorInstance, M as OwoMarkEditorLike, N as OwoMarkSharedState, Q as OwoMarkSharedStateStore, R as PasteIntent, T as PreviewDirtyReason, U as SlashStateListener, V as SlashTriggerInfo, W as VirtualPosition, X as VirtualSelection, Y as applyMarkdownIndent, Z as createOwoMarkCore, _ as getBlockIndexForPosition, $ as isVirtualSelectionCollapsed, a0 as linearToVirtual, a1 as linearToVirtualPosition, a2 as resolveIndentSize, a3 as virtualPositionToLinear, a4 as virtualPositionsEqual, a5 as virtualToLinear } from './dom-adapter-
|
|
1
|
+
import { B as BlockNode, a as BlockContextType, O as OwoMarkSelection, C as CoreStateSnapshot, S as SlashState, b as CommandRegistry, c as OwoMarkDocument, D as DirtyRange, P as PreviewBlockKind, d as PreviewBlock, e as BlockTransform, f as OwoMarkSharedStateController, I as InlineToken, g as InlineTokenType } from './dom-adapter-CTSJe5Uo.js';
|
|
2
|
+
export { h as BeforeInputIntent, i as BlockContext, j as BlockContextListener, k as BlockInsertType, l as BlockType, m as CommandContext, n as CommandDefinition, o as CompositionState, p as CoreApplyAction, q as CoreApplyResult, r as CoreSelectionSnapshot, s as CreateOwoMarkCoreOptions, t as Decorator, u as DecoratorType, v as DocumentChangeCallback, w as DomAdapterInstance, H as HeadingLevel, x as HistoryEntry, y as IndentMode, z as IndentResult, J as JsonPrimitive, A as JsonValue, K as KeyDownIntent, L as Leaf, E as OwoMarkCommands, F as OwoMarkCore, G as OwoMarkEditorInstance, M as OwoMarkEditorLike, N as OwoMarkSharedState, Q as OwoMarkSharedStateStore, R as PasteIntent, T as PreviewDirtyReason, U as SlashStateListener, V as SlashTriggerInfo, W as VirtualPosition, X as VirtualSelection, Y as applyMarkdownIndent, Z as createOwoMarkCore, _ as getBlockIndexForPosition, $ as isVirtualSelectionCollapsed, a0 as linearToVirtual, a1 as linearToVirtualPosition, a2 as resolveIndentSize, a3 as virtualPositionToLinear, a4 as virtualPositionsEqual, a5 as virtualToLinear } from './dom-adapter-CTSJe5Uo.js';
|
|
3
3
|
export { WordBoundaryResult, deleteToLineEnd, deleteToLineStart, deleteWordBackward, deleteWordForward } from './internal/commands/word-boundary.js';
|
|
4
4
|
export { normalizeMarkdownPaste } from './internal/clipboard/paste.js';
|
|
5
5
|
export { enableMapSet, produce } from 'immer';
|
|
@@ -95,12 +95,22 @@ declare function expandDirtyRange(current: DirtyRange | null, blockIndex: number
|
|
|
95
95
|
declare function expandWithContext(range: DirtyRange, totalBlocks: number, context?: number): DirtyRange | null;
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
98
|
+
* Block identity derivation.
|
|
99
99
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
100
|
+
* blockId is a content-based identity: stable across blank line edits.
|
|
101
|
+
* sourceKey is a line-range positional tag: used by scroll sync for
|
|
102
|
+
* editor↔preview coordinate mapping.
|
|
102
103
|
*/
|
|
103
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Derive a content-based block identity from its render key and
|
|
106
|
+
* occurrence index. Two blocks with identical content but different
|
|
107
|
+
* positions get different blockIds via their occurrence index.
|
|
108
|
+
*/
|
|
109
|
+
declare function deriveBlockId(renderKey: string, occurrenceIndex: number): string;
|
|
110
|
+
/**
|
|
111
|
+
* Derive a line-range positional key for scroll sync mapping.
|
|
112
|
+
*/
|
|
113
|
+
declare function deriveSourceKey(startLine: number, endLine: number): string;
|
|
104
114
|
|
|
105
115
|
/**
|
|
106
116
|
* Derive a stable render key for a preview block.
|
|
@@ -352,4 +362,4 @@ declare function buildVirtualRows(blocks: readonly BlockNode[], heightCache: Map
|
|
|
352
362
|
/** Compute the visible range given scroll state. */
|
|
353
363
|
declare function computeVisibleRange(rows: VirtualRow[], scrollTop: number, viewportHeight: number, overscan?: number): VisibleRange;
|
|
354
364
|
|
|
355
|
-
export { BLOCK_TYPE_TO_CLASS, BQ_STEP, BlockContextType, type BlockIdGenerator, BlockNode, BlockTransform, CommandRegistry, type CommandResult, type CoreEventHub, type CoreSlashStateListener, CoreStateSnapshot, type CreateSharedStateOptions, DirtyRange, type ImageSizeSyntax, InlineToken, InlineTokenType, NOT_HANDLED, OwoMarkDocument, OwoMarkSelection, OwoMarkSharedStateController, PreviewBlock, PreviewBlockKind, SlashState, TOKEN_TO_CLASS, type VirtualRow, type VisibleRange, buildBlockquoteBarsBoxShadow, buildVirtualRows, computePreviewDirtyRange, computeVisibleRange, createBlockElement, createBlockIdGenerator, createCommandRegistry, createImageSizeTransform, createSharedStateStore, deriveBlockId, deriveRenderKey, detectAndRenderDirty, domRangeToOffset, estimateEditorBlockHeight, expandDirtyRange, expandWithContext, fullRender, getBlockAtOffset, getBlockById, getBlockIndexById, getBlockStartOffset, handleCharInput, handleMarkdownEnter, handleSmartBackspace, handleSmartDelete, insertCodeFence, insertImage, insertLink, insertMathBlock, insertSideAnnotation, insertTable, invalidateBlockCache, offsetToDomRange, parseMarkdownToDocument, patchDirtyBlocks, projectToPreviewBlocks, readSelection, reconcileBlocks, resetBlockIdCounter, resolveBlockContextType, restoreSelection, serializeDocument, toggleBold, toggleItalic, tokenizeBlock, tokenizeInline, updateBlockElement };
|
|
365
|
+
export { BLOCK_TYPE_TO_CLASS, BQ_STEP, BlockContextType, type BlockIdGenerator, BlockNode, BlockTransform, CommandRegistry, type CommandResult, type CoreEventHub, type CoreSlashStateListener, CoreStateSnapshot, type CreateSharedStateOptions, DirtyRange, type ImageSizeSyntax, InlineToken, InlineTokenType, NOT_HANDLED, OwoMarkDocument, OwoMarkSelection, OwoMarkSharedStateController, PreviewBlock, PreviewBlockKind, SlashState, TOKEN_TO_CLASS, type VirtualRow, type VisibleRange, buildBlockquoteBarsBoxShadow, buildVirtualRows, computePreviewDirtyRange, computeVisibleRange, createBlockElement, createBlockIdGenerator, createCommandRegistry, createImageSizeTransform, createSharedStateStore, deriveBlockId, deriveRenderKey, deriveSourceKey, detectAndRenderDirty, domRangeToOffset, estimateEditorBlockHeight, expandDirtyRange, expandWithContext, fullRender, getBlockAtOffset, getBlockById, getBlockIndexById, getBlockStartOffset, handleCharInput, handleMarkdownEnter, handleSmartBackspace, handleSmartDelete, insertCodeFence, insertImage, insertLink, insertMathBlock, insertSideAnnotation, insertTable, invalidateBlockCache, offsetToDomRange, parseMarkdownToDocument, patchDirtyBlocks, projectToPreviewBlocks, readSelection, reconcileBlocks, resetBlockIdCounter, resolveBlockContextType, restoreSelection, serializeDocument, toggleBold, toggleItalic, tokenizeBlock, tokenizeInline, updateBlockElement };
|
package/dist/index.js
CHANGED
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
virtualPositionToLinear,
|
|
54
54
|
virtualPositionsEqual,
|
|
55
55
|
virtualToLinear
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-TRLKIMRD.js";
|
|
57
57
|
import {
|
|
58
58
|
normalizeMarkdownPaste
|
|
59
59
|
} from "./chunk-BGXCXQZP.js";
|
|
@@ -65,7 +65,10 @@ import {
|
|
|
65
65
|
} from "./chunk-MPIWZLI3.js";
|
|
66
66
|
|
|
67
67
|
// src/preview/block-id.ts
|
|
68
|
-
function deriveBlockId(
|
|
68
|
+
function deriveBlockId(renderKey, occurrenceIndex) {
|
|
69
|
+
return `${renderKey}#${occurrenceIndex}`;
|
|
70
|
+
}
|
|
71
|
+
function deriveSourceKey(startLine, endLine) {
|
|
69
72
|
return `L${startLine}-${endLine}`;
|
|
70
73
|
}
|
|
71
74
|
|
|
@@ -159,6 +162,7 @@ function collectSideAnnotationChain(blocks, startIndex) {
|
|
|
159
162
|
function projectToPreviewBlocks(doc, themeKey, transforms) {
|
|
160
163
|
const result = [];
|
|
161
164
|
const blocks = doc.blocks;
|
|
165
|
+
const occurrenceCounter = /* @__PURE__ */ new Map();
|
|
162
166
|
let currentLine = 1;
|
|
163
167
|
let i = 0;
|
|
164
168
|
while (i < blocks.length) {
|
|
@@ -168,14 +172,16 @@ function projectToPreviewBlocks(doc, themeKey, transforms) {
|
|
|
168
172
|
const sideChain = collectSideAnnotationChain(blocks, i);
|
|
169
173
|
if (sideChain) {
|
|
170
174
|
const endLine2 = startLine + sideChain.endLine;
|
|
171
|
-
const
|
|
175
|
+
const renderKey2 = deriveRenderKey(sideChain.raw, "paragraph", themeKey);
|
|
176
|
+
const occurrence2 = occurrenceCounter.get(renderKey2) ?? 0;
|
|
177
|
+
occurrenceCounter.set(renderKey2, occurrence2 + 1);
|
|
172
178
|
result.push({
|
|
173
|
-
blockId:
|
|
179
|
+
blockId: deriveBlockId(renderKey2, occurrence2),
|
|
174
180
|
kind: "paragraph",
|
|
175
181
|
raw: sideChain.raw,
|
|
176
182
|
startLine,
|
|
177
183
|
endLine: endLine2,
|
|
178
|
-
renderKey:
|
|
184
|
+
renderKey: renderKey2
|
|
179
185
|
});
|
|
180
186
|
currentLine = endLine2 + 1;
|
|
181
187
|
i = sideChain.endIndex + 1;
|
|
@@ -199,14 +205,16 @@ function projectToPreviewBlocks(doc, themeKey, transforms) {
|
|
|
199
205
|
}
|
|
200
206
|
const raw = groupRawParts.join("\n");
|
|
201
207
|
const kind2 = blockTypeToPreviewKind(block);
|
|
202
|
-
const
|
|
208
|
+
const renderKey2 = deriveRenderKey(raw, kind2, themeKey);
|
|
209
|
+
const occurrence2 = occurrenceCounter.get(renderKey2) ?? 0;
|
|
210
|
+
occurrenceCounter.set(renderKey2, occurrence2 + 1);
|
|
203
211
|
result.push({
|
|
204
|
-
blockId:
|
|
212
|
+
blockId: deriveBlockId(renderKey2, occurrence2),
|
|
205
213
|
kind: kind2,
|
|
206
214
|
raw,
|
|
207
215
|
startLine,
|
|
208
216
|
endLine: endLine2,
|
|
209
|
-
renderKey:
|
|
217
|
+
renderKey: renderKey2
|
|
210
218
|
});
|
|
211
219
|
currentLine = endLine2 + 1;
|
|
212
220
|
i = j;
|
|
@@ -214,14 +222,16 @@ function projectToPreviewBlocks(doc, themeKey, transforms) {
|
|
|
214
222
|
}
|
|
215
223
|
const endLine = startLine + blockLines - 1;
|
|
216
224
|
const kind = blockTypeToPreviewKind(block);
|
|
217
|
-
const
|
|
225
|
+
const renderKey = deriveRenderKey(block.raw, kind, themeKey);
|
|
226
|
+
const occurrence = occurrenceCounter.get(renderKey) ?? 0;
|
|
227
|
+
occurrenceCounter.set(renderKey, occurrence + 1);
|
|
218
228
|
const previewBlock = {
|
|
219
|
-
blockId,
|
|
229
|
+
blockId: deriveBlockId(renderKey, occurrence),
|
|
220
230
|
kind,
|
|
221
231
|
raw: block.raw,
|
|
222
232
|
startLine,
|
|
223
233
|
endLine,
|
|
224
|
-
renderKey
|
|
234
|
+
renderKey
|
|
225
235
|
};
|
|
226
236
|
if (block.type === "code-fence") {
|
|
227
237
|
previewBlock.language = block.language || null;
|
|
@@ -243,7 +253,7 @@ function projectToPreviewBlocks(doc, themeKey, transforms) {
|
|
|
243
253
|
});
|
|
244
254
|
}
|
|
245
255
|
function blocksMatch(a, b) {
|
|
246
|
-
return a.
|
|
256
|
+
return a.blockId === b.blockId;
|
|
247
257
|
}
|
|
248
258
|
function computePreviewDirtyRange(oldBlocks, newBlocks) {
|
|
249
259
|
const oldLen = oldBlocks.length;
|
|
@@ -599,6 +609,7 @@ export {
|
|
|
599
609
|
deleteWordForward,
|
|
600
610
|
deriveBlockId,
|
|
601
611
|
deriveRenderKey,
|
|
612
|
+
deriveSourceKey,
|
|
602
613
|
detectAndRenderDirty,
|
|
603
614
|
domRangeToOffset,
|
|
604
615
|
enableMapSet,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { v as DocumentChangeCallback, w as DomAdapterInstance, a6 as createDomAdapter } from '../dom-adapter-
|
|
1
|
+
export { v as DocumentChangeCallback, w as DomAdapterInstance, a6 as createDomAdapter } from '../dom-adapter-CTSJe5Uo.js';
|
package/package.json
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owomark/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Framework-agnostic core engine for the OwoMark editor.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"license": "MIT",
|
|
10
|
-
"repository": {
|
|
11
|
-
"type": "git",
|
|
12
|
-
"url": "https://github.com/kzhoa/qblog.git",
|
|
13
|
-
"directory": "owomark/packages/owomark-core"
|
|
14
|
-
},
|
|
15
|
-
"bugs": {
|
|
16
|
-
"url": "https://github.com/kzhoa/qblog/issues"
|
|
17
|
-
},
|
|
18
10
|
"keywords": [
|
|
19
11
|
"markdown",
|
|
20
12
|
"editor",
|