@reiwuzen/blocky 1.0.1 → 1.1.0

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 CHANGED
@@ -202,6 +202,45 @@ undo(h).match(
202
202
  );
203
203
  ```
204
204
 
205
+ ### Cursor — `engine/cursor`
206
+
207
+ Bridges the browser's flat cursor offset to the engine's `nodeIndex + offset` model.
208
+ The browser gives a single number from `window.getSelection()` — these functions convert it.
209
+
210
+ ```ts
211
+ // Convert a flat UI cursor offset → { nodeIndex, offset }
212
+ flatToPosition(block, flatOffset): Result<CursorPosition>
213
+
214
+ // Convert a flat UI selection → NodeSelection (ready for format/delete functions)
215
+ flatToSelection(block, start, end): Result<NodeSelection>
216
+
217
+ // Inverse — convert { nodeIndex, offset } back to flat offset (restore cursor in DOM)
218
+ positionToFlat(block, nodeIndex, offset): Result<number>
219
+ ```
220
+
221
+ ```ts
222
+ // Typical usage on every keypress / selection
223
+ const sel = window.getSelection();
224
+ flatToSelection(block, sel.anchorOffset, sel.focusOffset)
225
+ .andThen((nodeSel) => toggleBold(block.content, nodeSel))
226
+ .match(
227
+ (content) => updateBlock({ ...block, content }),
228
+ (err) => console.error(err)
229
+ );
230
+ ```
231
+
232
+ ### Block Ops — `engine/block-ops`
233
+
234
+ Wrappers around content functions that return `Result<AnyBlock>` instead of `Result<BlockContent>` —
235
+ useful when you want the full updated block back directly.
236
+
237
+ ```ts
238
+ blockInsertAt(block, nodeIndex, offset, incoming): Result<AnyBlock>
239
+ blockDeleteLastChar(block): Result<AnyBlock>
240
+ blockDeleteRange(block, startNodeIndex, startOffset, endNodeIndex, endOffset): Result<AnyBlock>
241
+ blockReplaceRange(block, startNodeIndex, startOffset, endNodeIndex, endOffset, incoming): Result<AnyBlock>
242
+ ```
243
+
205
244
  ### Utils — `utils/block`
206
245
 
207
246
  ```ts
@@ -224,6 +263,8 @@ src/
224
263
  │ └── block.ts
225
264
  └── engine/
226
265
  ├── content.ts ← insertAt, deleteLastChar, deleteRange, replaceRange, splitBlock, mergeBlocks
266
+ ├── block-ops.ts ← blockInsertAt, blockDeleteLastChar, blockDeleteRange, blockReplaceRange
267
+ ├── cursor.ts ← flatToPosition, flatToSelection, positionToFlat
227
268
  ├── format.ts ← toggleBold, toggleItalic, toggleColor, setLink, ...
228
269
  ├── transform.ts ← applyMarkdownTransform, changeBlockType, toggleTodo, indent/outdent
229
270
  ├── serializer.ts ← serialize, deserialize, toMarkdown, toPlainText, ...
@@ -0,0 +1,13 @@
1
+ import type { AnyBlock, Node } from "../types/block";
2
+ import { Result } from "@reiwuzen/result";
3
+ import { splitBlock, mergeBlocks } from "./content";
4
+ /**─── Wrappers ──────────────────────────────────────────────────────────────────
5
+ * Each function runs the content engine operation and returns Result<AnyBlock>
6
+ instead of Result<BlockContent<T>> — caller gets the full updated block back.
7
+ */
8
+ export declare function blockInsertAt(block: AnyBlock, nodeIndex: number, offset: number, incoming: Node): Result<AnyBlock>;
9
+ export declare function blockDeleteLastChar(block: AnyBlock): Result<AnyBlock>;
10
+ export declare function blockDeleteRange(block: AnyBlock, startNodeIndex: number, startOffset: number, endNodeIndex: number, endOffset: number): Result<AnyBlock>;
11
+ export declare function blockReplaceRange(block: AnyBlock, startNodeIndex: number, startOffset: number, endNodeIndex: number, endOffset: number, incoming: Node): Result<AnyBlock>;
12
+ export { splitBlock, mergeBlocks };
13
+ //# sourceMappingURL=block.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block.d.ts","sourceRoot":"","sources":["../../src/engine/block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAoB,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAKL,UAAU,EACV,WAAW,EACZ,MAAM,WAAW,CAAC;AAEnB;;;EAGE;AAEF,wBAAgB,aAAa,CAC3B,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,IAAI,GACb,MAAM,CAAC,QAAQ,CAAC,CAQlB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAQrE;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,QAAQ,EACf,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,QAAQ,CAAC,CAQlB;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,QAAQ,EACf,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,IAAI,GACb,MAAM,CAAC,QAAQ,CAAC,CASlB;AAGD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { insertAt, deleteLastChar, deleteRange, replaceRange, splitBlock, mergeBlocks, } from "./content";
2
+ /**─── Wrappers ──────────────────────────────────────────────────────────────────
3
+ * Each function runs the content engine operation and returns Result<AnyBlock>
4
+ instead of Result<BlockContent<T>> — caller gets the full updated block back.
5
+ */
6
+ export function blockInsertAt(block, nodeIndex, offset, incoming) {
7
+ return insertAt(block, nodeIndex, offset, incoming).map((content) => ({
8
+ ...block,
9
+ content,
10
+ }));
11
+ }
12
+ export function blockDeleteLastChar(block) {
13
+ return deleteLastChar(block).map((content) => ({
14
+ ...block,
15
+ content,
16
+ }));
17
+ }
18
+ export function blockDeleteRange(block, startNodeIndex, startOffset, endNodeIndex, endOffset) {
19
+ return deleteRange(block, startNodeIndex, startOffset, endNodeIndex, endOffset).map((content) => ({ ...block, content }));
20
+ }
21
+ export function blockReplaceRange(block, startNodeIndex, startOffset, endNodeIndex, endOffset, incoming) {
22
+ return replaceRange(block, startNodeIndex, startOffset, endNodeIndex, endOffset, incoming).map((content) => ({ ...block, content }));
23
+ }
24
+ // splitBlock already returns Result<[AnyBlock, AnyBlock]> — re-exported as-is
25
+ export { splitBlock, mergeBlocks };
26
+ //# sourceMappingURL=block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block.js","sourceRoot":"","sources":["../../src/engine/block.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,QAAQ,EACR,cAAc,EACd,WAAW,EACX,YAAY,EACZ,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAC;AAEnB;;;EAGE;AAEF,MAAM,UAAU,aAAa,CAC3B,KAAe,EACf,SAAiB,EACjB,MAAc,EACd,QAAc;IAEd,OAAO,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,CACrD,CAAC,OAAO,EAAE,EAAE,CACV,CAAC;QACC,GAAG,KAAK;QACR,OAAO;KACR,CAAa,CACjB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAe;IACjD,OAAO,cAAc,CAAC,KAAK,CAAC,CAAC,GAAG,CAC9B,CAAC,OAAO,EAAE,EAAE,CACV,CAAC;QACC,GAAG,KAAK;QACR,OAAO;KACR,CAAa,CACjB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,KAAe,EACf,cAAsB,EACtB,WAAmB,EACnB,YAAoB,EACpB,SAAiB;IAEjB,OAAO,WAAW,CAChB,KAAK,EACL,cAAc,EACd,WAAW,EACX,YAAY,EACZ,SAAS,CACV,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,CAAa,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAAe,EACf,cAAsB,EACtB,WAAmB,EACnB,YAAoB,EACpB,SAAiB,EACjB,QAAc;IAEd,OAAO,YAAY,CACjB,KAAK,EACL,cAAc,EACd,WAAW,EACX,YAAY,EACZ,SAAS,EACT,QAAQ,CACT,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,CAAa,CAAC,CAAC;AAC1D,CAAC;AAED,8EAA8E;AAC9E,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { AnyBlock } from '../types/block';
2
+ import type { NodeSelection } from './format';
3
+ import { Result } from '@reiwuzen/result';
4
+ export type CursorPosition = {
5
+ nodeIndex: number;
6
+ offset: number;
7
+ };
8
+ /**
9
+ * Convert a flat UI cursor offset to { nodeIndex, offset }.
10
+ *
11
+ * The browser gives a single number representing position in the
12
+ * concatenated text of the block. This function walks the node array
13
+ * and finds which node that position falls in and where within it.
14
+ *
15
+ * e.g. nodes = ["Hello"(5), " World"(6)]
16
+ * flatOffset=0 → { nodeIndex: 0, offset: 0 }
17
+ * flatOffset=5 → { nodeIndex: 0, offset: 5 } (end of first node)
18
+ * flatOffset=6 → { nodeIndex: 1, offset: 1 }
19
+ * flatOffset=11 → { nodeIndex: 1, offset: 6 } (end of last node)
20
+ */
21
+ export declare function flatToPosition(block: AnyBlock, flatOffset: number): Result<CursorPosition, string>;
22
+ /**
23
+ * Convert a flat UI selection { start, end } to NodeSelection.
24
+ *
25
+ * The browser gives two flat offsets from window.getSelection().
26
+ * This calls flatToPosition twice and returns a NodeSelection
27
+ * ready to pass to toggleBold, formatNodes, deleteRange, etc.
28
+ *
29
+ * e.g. nodes = ["Hello"(bold), " World"]
30
+ * { start: 3, end: 8 } → { startIndex:0, startOffset:3, endIndex:1, endOffset:3 }
31
+ */
32
+ export declare function flatToSelection(block: AnyBlock, start: number, end: number): Result<NodeSelection, string>;
33
+ /**
34
+ * Inverse — convert { nodeIndex, offset } back to a flat offset.
35
+ * Useful after engine operations to restore cursor position in the DOM.
36
+ */
37
+ export declare function positionToFlat(block: AnyBlock, nodeIndex: number, offset: number): Result<number, string>;
38
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/engine/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI1C,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAYF;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,EACf,UAAU,EAAE,MAAM,GACjB,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAqChC;AAID;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACV,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAY/B;AAID;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAexB"}
@@ -0,0 +1,90 @@
1
+ import { Result } from '@reiwuzen/result';
2
+ // ─── Helpers ───────────────────────────────────────────────────────────────────
3
+ function getTextLength(node) {
4
+ if (node.type === "text" || node.type === "code")
5
+ return node.text.length;
6
+ if (node.type === "equation")
7
+ return node.latex.length;
8
+ return 0;
9
+ }
10
+ // ─── flatToPosition ────────────────────────────────────────────────────────────
11
+ /**
12
+ * Convert a flat UI cursor offset to { nodeIndex, offset }.
13
+ *
14
+ * The browser gives a single number representing position in the
15
+ * concatenated text of the block. This function walks the node array
16
+ * and finds which node that position falls in and where within it.
17
+ *
18
+ * e.g. nodes = ["Hello"(5), " World"(6)]
19
+ * flatOffset=0 → { nodeIndex: 0, offset: 0 }
20
+ * flatOffset=5 → { nodeIndex: 0, offset: 5 } (end of first node)
21
+ * flatOffset=6 → { nodeIndex: 1, offset: 1 }
22
+ * flatOffset=11 → { nodeIndex: 1, offset: 6 } (end of last node)
23
+ */
24
+ export function flatToPosition(block, flatOffset) {
25
+ // code/equation blocks are always a single node — offset maps directly
26
+ if (block.type === "code" || block.type === "equation") {
27
+ const node = block.content[0];
28
+ const len = getTextLength(node);
29
+ if (flatOffset < 0 || flatOffset > len)
30
+ return Result.Err(`flatOffset (${flatOffset}) out of bounds (length=${len})`);
31
+ return Result.Ok({ nodeIndex: 0, offset: flatOffset });
32
+ }
33
+ const nodes = block.content;
34
+ if (flatOffset < 0)
35
+ return Result.Err(`flatOffset (${flatOffset}) cannot be negative`);
36
+ let accumulated = 0;
37
+ for (let i = 0; i < nodes.length; i++) {
38
+ const len = getTextLength(nodes[i]);
39
+ // offset lands within this node OR at its end (but not the last node)
40
+ if (flatOffset <= accumulated + len) {
41
+ return Result.Ok({ nodeIndex: i, offset: flatOffset - accumulated });
42
+ }
43
+ accumulated += len;
44
+ }
45
+ // flatOffset is exactly at the end of the last node
46
+ if (flatOffset === accumulated) {
47
+ const last = nodes.length - 1;
48
+ return Result.Ok({ nodeIndex: Math.max(0, last), offset: getTextLength(nodes[last]) });
49
+ }
50
+ return Result.Err(`flatOffset (${flatOffset}) out of bounds (total length=${accumulated})`);
51
+ }
52
+ // ─── flatToSelection ───────────────────────────────────────────────────────────
53
+ /**
54
+ * Convert a flat UI selection { start, end } to NodeSelection.
55
+ *
56
+ * The browser gives two flat offsets from window.getSelection().
57
+ * This calls flatToPosition twice and returns a NodeSelection
58
+ * ready to pass to toggleBold, formatNodes, deleteRange, etc.
59
+ *
60
+ * e.g. nodes = ["Hello"(bold), " World"]
61
+ * { start: 3, end: 8 } → { startIndex:0, startOffset:3, endIndex:1, endOffset:3 }
62
+ */
63
+ export function flatToSelection(block, start, end) {
64
+ if (start > end)
65
+ return Result.Err(`start (${start}) cannot be greater than end (${end})`);
66
+ return flatToPosition(block, start).andThen((startPos) => flatToPosition(block, end).map((endPos) => ({
67
+ startIndex: startPos.nodeIndex,
68
+ startOffset: startPos.offset,
69
+ endIndex: endPos.nodeIndex,
70
+ endOffset: endPos.offset,
71
+ })));
72
+ }
73
+ // ─── positionToFlat ────────────────────────────────────────────────────────────
74
+ /**
75
+ * Inverse — convert { nodeIndex, offset } back to a flat offset.
76
+ * Useful after engine operations to restore cursor position in the DOM.
77
+ */
78
+ export function positionToFlat(block, nodeIndex, offset) {
79
+ if (block.type === "code" || block.type === "equation")
80
+ return Result.Ok(offset);
81
+ const nodes = block.content;
82
+ if (nodeIndex < 0 || nodeIndex >= nodes.length)
83
+ return Result.Err(`nodeIndex (${nodeIndex}) out of bounds`);
84
+ let flat = 0;
85
+ for (let i = 0; i < nodeIndex; i++) {
86
+ flat += getTextLength(nodes[i]);
87
+ }
88
+ return Result.Ok(flat + offset);
89
+ }
90
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/engine/cursor.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { insertAt, deleteLastChar, deleteRange, replaceRange, splitBlock, mergeB
2
2
  export { formatNodes, toggleBold, toggleItalic, toggleUnderline, toggleStrikethrough, toggleHighlight, toggleColor, setLink, removeLink, mergeAdjacentNodes } from "./engine/format";
3
3
  export { applyMarkdownTransform, changeBlockType, toggleTodo, indentBlock, outdentBlock } from "./engine/transform";
4
4
  export { serialize, deserialize, serializeNodes, deserializeNodes, toPlainText, toMarkdown } from "./engine/serializer";
5
+ export { flatToPosition, flatToSelection, positionToFlat, } from './engine/cursor';
5
6
  export { createHistory, push, undo, redo, canUndo, canRedo, currentBlocks } from "./engine/history";
6
7
  export type { History, HistoryEntry } from "./engine/history";
7
8
  export { generateId, createBlock, insertBlockAfter, deleteBlock, duplicateBlock, moveBlock } from "./utils/block";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGhH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGrL,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGpH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGxH,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACpG,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAG9D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlH,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGhH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGrL,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGpH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGxH,OAAO,EAAC,cAAc,EAAC,eAAe,EAAC,cAAc,GAAE,MAAM,iBAAiB,CAAA;AAG9E,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACpG,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAG9D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlH,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -6,6 +6,8 @@ export { formatNodes, toggleBold, toggleItalic, toggleUnderline, toggleStrikethr
6
6
  export { applyMarkdownTransform, changeBlockType, toggleTodo, indentBlock, outdentBlock } from "./engine/transform";
7
7
  // ─── Serializer ────────────────────────────────────────────────────────────────
8
8
  export { serialize, deserialize, serializeNodes, deserializeNodes, toPlainText, toMarkdown } from "./engine/serializer";
9
+ // ─── Cursor ───────────────────────────────────────────────────────────────────
10
+ export { flatToPosition, flatToSelection, positionToFlat, } from './engine/cursor';
9
11
  // ─── History ───────────────────────────────────────────────────────────────────
10
12
  export { createHistory, push, undo, redo, canUndo, canRedo, currentBlocks } from "./engine/history";
11
13
  // ─── Utils ─────────────────────────────────────────────────────────────────────
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEhH,kFAAkF;AAClF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErL,kFAAkF;AAClF,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEpH,kFAAkF;AAClF,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAExH,kFAAkF;AAClF,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGpG,kFAAkF;AAClF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEhH,kFAAkF;AAClF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErL,kFAAkF;AAClF,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEpH,kFAAkF;AAClF,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAExH,iFAAiF;AACjF,OAAO,EAAC,cAAc,EAAC,eAAe,EAAC,cAAc,GAAE,MAAM,iBAAiB,CAAA;AAE9E,kFAAkF;AAClF,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGpG,kFAAkF;AAClF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reiwuzen/blocky",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Pure TypeScript block editor engine. Headless, framework-agnostic — content mutation, formatting, transforms, serialization, and history.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",