@reiwuzen/blocky 1.0.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 +250 -0
- package/dist/engine/content.d.ts +46 -0
- package/dist/engine/content.d.ts.map +1 -0
- package/dist/engine/content.js +317 -0
- package/dist/engine/content.js.map +1 -0
- package/dist/engine/format.d.ts +26 -0
- package/dist/engine/format.d.ts.map +1 -0
- package/dist/engine/format.js +116 -0
- package/dist/engine/format.js.map +1 -0
- package/dist/engine/history.d.ts +35 -0
- package/dist/engine/history.d.ts.map +1 -0
- package/dist/engine/history.js +62 -0
- package/dist/engine/history.js.map +1 -0
- package/dist/engine/serializer.d.ts +46 -0
- package/dist/engine/serializer.d.ts.map +1 -0
- package/dist/engine/serializer.js +205 -0
- package/dist/engine/serializer.js.map +1 -0
- package/dist/engine/transform.d.ts +47 -0
- package/dist/engine/transform.d.ts.map +1 -0
- package/dist/engine/transform.js +195 -0
- package/dist/engine/transform.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/types/block.d.ts +44 -0
- package/dist/types/block.d.ts.map +1 -0
- package/dist/types/block.js +2 -0
- package/dist/types/block.js.map +1 -0
- package/dist/types/editor.d.ts +1 -0
- package/dist/types/editor.d.ts.map +1 -0
- package/dist/types/editor.js +2 -0
- package/dist/types/editor.js.map +1 -0
- package/dist/utils/block.d.ts +32 -0
- package/dist/utils/block.d.ts.map +1 -0
- package/dist/utils/block.js +97 -0
- package/dist/utils/block.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# @reiwuzen/blocky
|
|
2
|
+
|
|
3
|
+
Pure TypeScript block editor engine. No UI, no framework, no opinions on rendering.
|
|
4
|
+
|
|
5
|
+
Handles everything an editor needs at the data layer — content mutation, formatting, markdown shortcuts, serialization, and history. Bring your own renderer.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @reiwuzen/blocky
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Concepts
|
|
18
|
+
|
|
19
|
+
### Block
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
type Block<T extends BlockType> = {
|
|
23
|
+
id: string;
|
|
24
|
+
type: T;
|
|
25
|
+
meta: BlockMeta<T>;
|
|
26
|
+
content: BlockContent<T>;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Block Types
|
|
31
|
+
|
|
32
|
+
| Type | Content | Meta |
|
|
33
|
+
|------|---------|------|
|
|
34
|
+
| `paragraph` | `TextNode[]` | — |
|
|
35
|
+
| `heading1` / `heading2` / `heading3` | `TextNode[]` | — |
|
|
36
|
+
| `bullet` | `TextNode[]` | `{ depth: number }` |
|
|
37
|
+
| `number` | `TextNode[]` | `{ depth: number }` |
|
|
38
|
+
| `todo` | `TextNode[]` | `{ depth: number, checked?: true }` |
|
|
39
|
+
| `code` | `[CodeNode]` | `{ language?: string }` |
|
|
40
|
+
| `equation` | `[EquationNode]` | — |
|
|
41
|
+
|
|
42
|
+
### Node
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
type Node =
|
|
46
|
+
| {
|
|
47
|
+
type: "text";
|
|
48
|
+
text: string;
|
|
49
|
+
bold?: true;
|
|
50
|
+
italic?: true;
|
|
51
|
+
underline?: true;
|
|
52
|
+
strikethrough?: true;
|
|
53
|
+
highlighted?: "yellow" | "green";
|
|
54
|
+
color?: "red" | "blue" | "green";
|
|
55
|
+
link?: string;
|
|
56
|
+
}
|
|
57
|
+
| { type: "code"; text: string }
|
|
58
|
+
| { type: "equation"; latex: string }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Rich blocks hold `TextNode[]` and can contain inline `code` and `equation` nodes.
|
|
62
|
+
Leaf blocks (`code`, `equation`) always hold a single-element tuple `[Node]`.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Result
|
|
67
|
+
|
|
68
|
+
All engine functions return `Result<T>` from [`@reiwuzen/result`](https://npmjs.com/package/@reiwuzen/result) — no silent failures, no thrown exceptions.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
result.match(
|
|
72
|
+
(value) => { /* success */ },
|
|
73
|
+
(error) => { /* failure, error is a string */ }
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
// chaining
|
|
77
|
+
deleteRange(block, 0, 0, 0, 5)
|
|
78
|
+
.andThen((content) => insertAt({ ...block, content }, 0, 0, incoming))
|
|
79
|
+
.match(...)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## API
|
|
85
|
+
|
|
86
|
+
### Content — `engine/content`
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
// Insert a node at any position — end, start, or mid-node
|
|
90
|
+
insertAt(block, nodeIndex, offset, incoming): Result<BlockContent<T>>
|
|
91
|
+
|
|
92
|
+
// Delete the last character from the last node
|
|
93
|
+
deleteLastChar(block): Result<BlockContent>
|
|
94
|
+
|
|
95
|
+
// Delete a selected range
|
|
96
|
+
deleteRange(block, startNodeIndex, startOffset, endNodeIndex, endOffset): Result<BlockContent<T>>
|
|
97
|
+
|
|
98
|
+
// Replace a selected range with a node — atomic deleteRange + insertAt
|
|
99
|
+
replaceRange(block, startNodeIndex, startOffset, endNodeIndex, endOffset, incoming): Result<BlockContent<T>>
|
|
100
|
+
|
|
101
|
+
// Split a block at cursor position → [original, newParagraph]
|
|
102
|
+
splitBlock(block, nodeIndex, offset): Result<[AnyBlock, AnyBlock]>
|
|
103
|
+
|
|
104
|
+
// Merge blockB content into blockA
|
|
105
|
+
mergeBlocks(blockA, blockB): Result<AnyBlock>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Format — `engine/format`
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
type NodeSelection = {
|
|
112
|
+
startIndex: number;
|
|
113
|
+
startOffset: number;
|
|
114
|
+
endIndex: number;
|
|
115
|
+
endOffset: number; // exclusive
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
toggleBold(nodes, selection): Result<Node[]>
|
|
119
|
+
toggleItalic(nodes, selection): Result<Node[]>
|
|
120
|
+
toggleUnderline(nodes, selection): Result<Node[]>
|
|
121
|
+
toggleStrikethrough(nodes, selection): Result<Node[]>
|
|
122
|
+
toggleHighlight(nodes, selection, "yellow" | "green"): Result<Node[]>
|
|
123
|
+
toggleColor(nodes, selection, "red" | "blue" | "green"): Result<Node[]>
|
|
124
|
+
setLink(nodes, selection, href): Result<Node[]>
|
|
125
|
+
removeLink(nodes, selection): Result<Node[]>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Auto-detects toggle — if all selected nodes already have the format, it removes it.
|
|
129
|
+
|
|
130
|
+
### Transform — `engine/transform`
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// Call on every space keypress — converts paragraph to another type
|
|
134
|
+
// if content starts with a markdown shortcut at position 0
|
|
135
|
+
applyMarkdownTransform(block, cursorOffset): Result<{ block, converted: boolean }>
|
|
136
|
+
|
|
137
|
+
// Convert a block to a new type, preserving content where possible
|
|
138
|
+
changeBlockType(block, targetType): Result<Block<T>>
|
|
139
|
+
|
|
140
|
+
// Toggle checked state on a todo block
|
|
141
|
+
toggleTodo(block): Result<Block<"todo">>
|
|
142
|
+
|
|
143
|
+
// Increase / decrease depth for bullet, number, todo (max depth: 6)
|
|
144
|
+
indentBlock(block): Result<IndentableBlock>
|
|
145
|
+
outdentBlock(block): Result<IndentableBlock>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Markdown shortcuts:**
|
|
149
|
+
|
|
150
|
+
| Typed | Result |
|
|
151
|
+
|-------|--------|
|
|
152
|
+
| `- ` | `bullet` |
|
|
153
|
+
| `1. ` | `number` |
|
|
154
|
+
| `[] ` | `todo` |
|
|
155
|
+
| `# ` | `heading1` |
|
|
156
|
+
| `## ` | `heading2` |
|
|
157
|
+
| `### ` | `heading3` |
|
|
158
|
+
|
|
159
|
+
### Serializer — `engine/serializer`
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
// Blocks ↔ JSON
|
|
163
|
+
serialize(blocks): Result<string>
|
|
164
|
+
deserialize(json): Result<AnyBlock[]>
|
|
165
|
+
|
|
166
|
+
// Nodes ↔ JSON (clipboard)
|
|
167
|
+
serializeNodes(nodes): Result<string>
|
|
168
|
+
deserializeNodes(json): Result<Node[]>
|
|
169
|
+
|
|
170
|
+
// Plain text extraction
|
|
171
|
+
toPlainText(nodes): string
|
|
172
|
+
|
|
173
|
+
// Blocks → markdown string
|
|
174
|
+
toMarkdown(blocks): string
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### History — `engine/history`
|
|
178
|
+
|
|
179
|
+
Pure functions — no classes, no mutation.
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
createHistory(initialBlocks): History
|
|
183
|
+
push(history, blocks, maxSize?): History // default maxSize: 100
|
|
184
|
+
undo(history): Result<History>
|
|
185
|
+
redo(history): Result<History>
|
|
186
|
+
canUndo(history): boolean
|
|
187
|
+
canRedo(history): boolean
|
|
188
|
+
currentBlocks(history): AnyBlock[]
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
// Typical usage
|
|
193
|
+
let h = createHistory(initialBlocks);
|
|
194
|
+
|
|
195
|
+
// after every engine operation
|
|
196
|
+
h = push(h, newBlocks);
|
|
197
|
+
|
|
198
|
+
// undo / redo
|
|
199
|
+
undo(h).match(
|
|
200
|
+
(h2) => { h = h2; render(currentBlocks(h)); },
|
|
201
|
+
(e) => console.error(e)
|
|
202
|
+
);
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Utils — `utils/block`
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
generateId(fn?): string
|
|
209
|
+
createBlock(type, idFn?): Result<Block<T>>
|
|
210
|
+
insertBlockAfter(blocks, afterId, type, idFn?): Result<{ blocks, newId }>
|
|
211
|
+
deleteBlock(blocks, id): { blocks, prevId }
|
|
212
|
+
duplicateBlock(block, newId): AnyBlock
|
|
213
|
+
moveBlock(blocks, id, "up" | "down"): Result<AnyBlock[]>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Package Structure
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
src/
|
|
222
|
+
├── index.ts
|
|
223
|
+
├── types/
|
|
224
|
+
│ └── block.ts
|
|
225
|
+
└── engine/
|
|
226
|
+
├── content.ts ← insertAt, deleteLastChar, deleteRange, replaceRange, splitBlock, mergeBlocks
|
|
227
|
+
├── format.ts ← toggleBold, toggleItalic, toggleColor, setLink, ...
|
|
228
|
+
├── transform.ts ← applyMarkdownTransform, changeBlockType, toggleTodo, indent/outdent
|
|
229
|
+
├── serializer.ts ← serialize, deserialize, toMarkdown, toPlainText, ...
|
|
230
|
+
└── history.ts ← createHistory, push, undo, redo
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Local Development
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
# in /@reiwuzen/blocky
|
|
239
|
+
npm install
|
|
240
|
+
npm run dev
|
|
241
|
+
|
|
242
|
+
# in your project
|
|
243
|
+
"dependencies": { "@reiwuzen/blocky": "../@reiwuzen/blocky" }
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
MIT
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Node, AnyBlock, BlockContent, BlockType } from '../types/block';
|
|
2
|
+
import { Result } from '@reiwuzen/result';
|
|
3
|
+
/**
|
|
4
|
+
* Insert a Node at a specific position within block content.
|
|
5
|
+
*
|
|
6
|
+
* - nodeIndex: which node to insert into
|
|
7
|
+
* - offset: char position within that node
|
|
8
|
+
*
|
|
9
|
+
* End-of-node (offset === length) naturally becomes an append —
|
|
10
|
+
* sliceNode produces no right half, incoming merges with left or gets pushed.
|
|
11
|
+
* No separate append function needed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function insertAt<T extends BlockType>(block: AnyBlock, nodeIndex: number, offset: number, incoming: Node): Result<BlockContent<T>>;
|
|
14
|
+
export declare function deleteLastChar(block: AnyBlock): Result<BlockContent<BlockType>>;
|
|
15
|
+
/**
|
|
16
|
+
* Delete content across a selection.
|
|
17
|
+
* After deletion, left and right boundaries are merged if formats match.
|
|
18
|
+
*
|
|
19
|
+
* For code/equation blocks: deletes within the single tuple node's text/latex.
|
|
20
|
+
*/
|
|
21
|
+
export declare function deleteRange<T extends BlockType>(block: AnyBlock, startNodeIndex: number, startOffset: number, endNodeIndex: number, endOffset: number): Result<BlockContent<T>>;
|
|
22
|
+
/**
|
|
23
|
+
* Split a block at a given position into two blocks.
|
|
24
|
+
* The original block keeps content before the cursor.
|
|
25
|
+
* A new block gets content after the cursor — always of type "paragraph".
|
|
26
|
+
*
|
|
27
|
+
* Not supported for code/equation blocks — returns Err.
|
|
28
|
+
*/
|
|
29
|
+
export declare function splitBlock(block: AnyBlock, nodeIndex: number, offset: number): Result<[AnyBlock, AnyBlock]>;
|
|
30
|
+
/**
|
|
31
|
+
* Merge blockB into blockA — blockB's content is appended to blockA's content.
|
|
32
|
+
* blockA's type and meta are preserved.
|
|
33
|
+
*
|
|
34
|
+
* Not supported if either block is code or equation — returns Err.
|
|
35
|
+
*/
|
|
36
|
+
export declare function mergeBlocks(blockA: AnyBlock, blockB: AnyBlock): Result<AnyBlock>;
|
|
37
|
+
/**
|
|
38
|
+
* Replace a selected range with an incoming Node — atomic deleteRange + insertAt.
|
|
39
|
+
* This is what fires when the user has a selection and types a character.
|
|
40
|
+
*
|
|
41
|
+
* Internally chains:
|
|
42
|
+
* 1. deleteRange — remove selected content
|
|
43
|
+
* 2. insertAt — insert incoming at the start of the deleted range
|
|
44
|
+
*/
|
|
45
|
+
export declare function replaceRange<T extends BlockType>(block: AnyBlock, startNodeIndex: number, startOffset: number, endNodeIndex: number, endOffset: number, incoming: Node): Result<BlockContent<T>>;
|
|
46
|
+
//# sourceMappingURL=content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/engine/content.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE9E,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAmD1C;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,SAAS,EAC1C,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,IAAI,GACb,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CA4EzB;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAgC/E;AAKD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,SAAS,EAC7C,KAAK,EAAE,QAAQ,EACf,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CA8DzB;AAID;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,MAAM,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAwC9B;AAID;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,QAAQ,GACf,MAAM,CAAC,QAAQ,CAAC,CAsBlB;AAKD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAC9C,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,YAAY,CAAC,CAAC,CAAC,CAAC,CAUzB"}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { generateId } from '../utils/block';
|
|
2
|
+
import { Result } from '@reiwuzen/result';
|
|
3
|
+
import { formatsMatch } from './format';
|
|
4
|
+
// ─── Helpers ───────────────────────────────────────────────────────────────────
|
|
5
|
+
function isClean(node) {
|
|
6
|
+
return (node.bold === undefined &&
|
|
7
|
+
node.italic === undefined &&
|
|
8
|
+
node.underline === undefined &&
|
|
9
|
+
node.strikethrough === undefined &&
|
|
10
|
+
node.highlighted === undefined &&
|
|
11
|
+
node.color === undefined &&
|
|
12
|
+
node.link === undefined);
|
|
13
|
+
}
|
|
14
|
+
function getTextLength(node) {
|
|
15
|
+
if (node.type === "text" || node.type === "code")
|
|
16
|
+
return node.text.length;
|
|
17
|
+
if (node.type === "equation")
|
|
18
|
+
return node.latex.length;
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
function tryMerge(a, b) {
|
|
22
|
+
if (a.type !== b.type)
|
|
23
|
+
return null;
|
|
24
|
+
if (a.type === "code" && b.type === "code")
|
|
25
|
+
return { ...a, text: a.text + b.text };
|
|
26
|
+
if (a.type === "equation" && b.type === "equation")
|
|
27
|
+
return { ...a, latex: a.latex + b.latex };
|
|
28
|
+
if (a.type === "text" && b.type === "text") {
|
|
29
|
+
if ((isClean(a) && isClean(b)) || formatsMatch(a, b))
|
|
30
|
+
return { ...a, text: a.text + b.text };
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
function sliceNode(node, start, end) {
|
|
35
|
+
if (start >= end)
|
|
36
|
+
return null;
|
|
37
|
+
if (node.type === "text") {
|
|
38
|
+
const text = node.text.slice(start, end);
|
|
39
|
+
return text.length ? { ...node, text } : null;
|
|
40
|
+
}
|
|
41
|
+
if (node.type === "code") {
|
|
42
|
+
const text = node.text.slice(start, end);
|
|
43
|
+
return text.length ? { ...node, text } : null;
|
|
44
|
+
}
|
|
45
|
+
if (node.type === "equation") {
|
|
46
|
+
const latex = node.latex.slice(start, end);
|
|
47
|
+
return latex.length ? { ...node, latex } : null;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
// ─── insertAt (sole public API) ────────────────────────────────────────────────
|
|
52
|
+
/**
|
|
53
|
+
* Insert a Node at a specific position within block content.
|
|
54
|
+
*
|
|
55
|
+
* - nodeIndex: which node to insert into
|
|
56
|
+
* - offset: char position within that node
|
|
57
|
+
*
|
|
58
|
+
* End-of-node (offset === length) naturally becomes an append —
|
|
59
|
+
* sliceNode produces no right half, incoming merges with left or gets pushed.
|
|
60
|
+
* No separate append function needed.
|
|
61
|
+
*/
|
|
62
|
+
export function insertAt(block, nodeIndex, offset, incoming) {
|
|
63
|
+
// ── code block ────────────────────────────────────────────────────────────
|
|
64
|
+
if (block.type === "code") {
|
|
65
|
+
if (incoming.type !== "code")
|
|
66
|
+
return Result.Err(`code block only accepts a code node, got "${incoming.type}"`);
|
|
67
|
+
const node = block.content[0];
|
|
68
|
+
if (offset < 0 || offset > node.text.length)
|
|
69
|
+
return Result.Err(`offset (${offset}) out of bounds for code node`);
|
|
70
|
+
const text = node.text.slice(0, offset) + incoming.text + node.text.slice(offset);
|
|
71
|
+
return Result.Ok([{ ...node, text }]);
|
|
72
|
+
}
|
|
73
|
+
// ── equation block ────────────────────────────────────────────────────────
|
|
74
|
+
if (block.type === "equation") {
|
|
75
|
+
if (incoming.type !== "equation")
|
|
76
|
+
return Result.Err(`equation block only accepts an equation node, got "${incoming.type}"`);
|
|
77
|
+
const node = block.content[0];
|
|
78
|
+
if (offset < 0 || offset > node.latex.length)
|
|
79
|
+
return Result.Err(`offset (${offset}) out of bounds for equation node`);
|
|
80
|
+
const latex = node.latex.slice(0, offset) + incoming.latex + node.latex.slice(offset);
|
|
81
|
+
return Result.Ok([{ ...node, latex }]);
|
|
82
|
+
}
|
|
83
|
+
// ── rich blocks ───────────────────────────────────────────────────────────
|
|
84
|
+
const content = block.content;
|
|
85
|
+
// Empty content — just push
|
|
86
|
+
if (content.length === 0) {
|
|
87
|
+
return Result.Ok([incoming]);
|
|
88
|
+
}
|
|
89
|
+
if (nodeIndex < 0 || nodeIndex >= content.length)
|
|
90
|
+
return Result.Err(`nodeIndex (${nodeIndex}) out of bounds (length=${content.length})`);
|
|
91
|
+
const target = content[nodeIndex];
|
|
92
|
+
const targetLen = getTextLength(target);
|
|
93
|
+
if (offset < 0 || offset > targetLen)
|
|
94
|
+
return Result.Err(`offset (${offset}) out of bounds for node at index ${nodeIndex}`);
|
|
95
|
+
const before = content.slice(0, nodeIndex);
|
|
96
|
+
const after = content.slice(nodeIndex + 1);
|
|
97
|
+
const middle = [];
|
|
98
|
+
// Left half of split — empty when offset=0
|
|
99
|
+
const left = sliceNode(target, 0, offset);
|
|
100
|
+
if (left)
|
|
101
|
+
middle.push(left);
|
|
102
|
+
// Incoming — try merge with left half
|
|
103
|
+
if (middle.length > 0) {
|
|
104
|
+
const merged = tryMerge(middle[middle.length - 1], incoming);
|
|
105
|
+
if (merged)
|
|
106
|
+
middle[middle.length - 1] = merged;
|
|
107
|
+
else
|
|
108
|
+
middle.push(incoming);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
middle.push(incoming);
|
|
112
|
+
}
|
|
113
|
+
// Right half — empty when offset=targetLen (end of node → natural append)
|
|
114
|
+
const right = sliceNode(target, offset, targetLen);
|
|
115
|
+
if (right) {
|
|
116
|
+
const merged = tryMerge(middle[middle.length - 1], right);
|
|
117
|
+
if (merged)
|
|
118
|
+
middle[middle.length - 1] = merged;
|
|
119
|
+
else
|
|
120
|
+
middle.push(right);
|
|
121
|
+
}
|
|
122
|
+
// Merge across all boundaries
|
|
123
|
+
const result = [];
|
|
124
|
+
for (const node of [...before, ...middle, ...after]) {
|
|
125
|
+
const prev = result[result.length - 1];
|
|
126
|
+
const merged = prev ? tryMerge(prev, node) : null;
|
|
127
|
+
if (merged)
|
|
128
|
+
result[result.length - 1] = merged;
|
|
129
|
+
else
|
|
130
|
+
result.push(node);
|
|
131
|
+
}
|
|
132
|
+
return Result.Ok(result);
|
|
133
|
+
}
|
|
134
|
+
// ─── deleteLastChar ────────────────────────────────────────────────────────────
|
|
135
|
+
export function deleteLastChar(block) {
|
|
136
|
+
if (block.type === "code") {
|
|
137
|
+
const node = block.content[0];
|
|
138
|
+
if (!node.text.length)
|
|
139
|
+
return Result.Err("Nothing to delete");
|
|
140
|
+
return Result.Ok([{ ...node, text: node.text.slice(0, -1) }]);
|
|
141
|
+
}
|
|
142
|
+
if (block.type === "equation") {
|
|
143
|
+
const node = block.content[0];
|
|
144
|
+
if (!node.latex.length)
|
|
145
|
+
return Result.Err("Nothing to delete");
|
|
146
|
+
return Result.Ok([{ ...node, latex: node.latex.slice(0, -1) }]);
|
|
147
|
+
}
|
|
148
|
+
const next = [...block.content];
|
|
149
|
+
for (let i = next.length - 1; i >= 0; i--) {
|
|
150
|
+
const node = next[i];
|
|
151
|
+
if (node.type === "text" || node.type === "code") {
|
|
152
|
+
const trimmed = node.text.slice(0, -1);
|
|
153
|
+
if (!trimmed.length)
|
|
154
|
+
next.splice(i, 1);
|
|
155
|
+
else
|
|
156
|
+
next[i] = { ...node, text: trimmed };
|
|
157
|
+
return Result.Ok(next);
|
|
158
|
+
}
|
|
159
|
+
if (node.type === "equation") {
|
|
160
|
+
const trimmed = node.latex.slice(0, -1);
|
|
161
|
+
if (!trimmed.length)
|
|
162
|
+
next.splice(i, 1);
|
|
163
|
+
else
|
|
164
|
+
next[i] = { ...node, latex: trimmed };
|
|
165
|
+
return Result.Ok(next);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return Result.Err("Nothing to delete");
|
|
169
|
+
}
|
|
170
|
+
// ─── deleteRange ───────────────────────────────────────────────────────────────
|
|
171
|
+
/**
|
|
172
|
+
* Delete content across a selection.
|
|
173
|
+
* After deletion, left and right boundaries are merged if formats match.
|
|
174
|
+
*
|
|
175
|
+
* For code/equation blocks: deletes within the single tuple node's text/latex.
|
|
176
|
+
*/
|
|
177
|
+
export function deleteRange(block, startNodeIndex, startOffset, endNodeIndex, endOffset) {
|
|
178
|
+
// ── code block ────────────────────────────────────────────────────────────
|
|
179
|
+
if (block.type === "code") {
|
|
180
|
+
const node = block.content[0];
|
|
181
|
+
if (startOffset < 0 || endOffset > node.text.length || startOffset > endOffset)
|
|
182
|
+
return Result.Err(`invalid range [${startOffset}, ${endOffset}] for code node`);
|
|
183
|
+
const text = node.text.slice(0, startOffset) + node.text.slice(endOffset);
|
|
184
|
+
return Result.Ok([{ ...node, text }]);
|
|
185
|
+
}
|
|
186
|
+
// ── equation block ────────────────────────────────────────────────────────
|
|
187
|
+
if (block.type === "equation") {
|
|
188
|
+
const node = block.content[0];
|
|
189
|
+
if (startOffset < 0 || endOffset > node.latex.length || startOffset > endOffset)
|
|
190
|
+
return Result.Err(`invalid range [${startOffset}, ${endOffset}] for equation node`);
|
|
191
|
+
const latex = node.latex.slice(0, startOffset) + node.latex.slice(endOffset);
|
|
192
|
+
return Result.Ok([{ ...node, latex }]);
|
|
193
|
+
}
|
|
194
|
+
// ── rich blocks ───────────────────────────────────────────────────────────
|
|
195
|
+
const nodes = block.content;
|
|
196
|
+
if (startNodeIndex < 0 || endNodeIndex >= nodes.length)
|
|
197
|
+
return Result.Err(`node indices [${startNodeIndex}, ${endNodeIndex}] out of bounds`);
|
|
198
|
+
if (startNodeIndex > endNodeIndex)
|
|
199
|
+
return Result.Err(`startNodeIndex (${startNodeIndex}) > endNodeIndex (${endNodeIndex})`);
|
|
200
|
+
const startNode = nodes[startNodeIndex];
|
|
201
|
+
const endNode = nodes[endNodeIndex];
|
|
202
|
+
if (startOffset < 0 || startOffset > getTextLength(startNode))
|
|
203
|
+
return Result.Err(`startOffset (${startOffset}) out of bounds`);
|
|
204
|
+
if (endOffset < 0 || endOffset > getTextLength(endNode))
|
|
205
|
+
return Result.Err(`endOffset (${endOffset}) out of bounds`);
|
|
206
|
+
const before = nodes.slice(0, startNodeIndex);
|
|
207
|
+
const after = nodes.slice(endNodeIndex + 1);
|
|
208
|
+
const middle = [];
|
|
209
|
+
// Keep left part of start node
|
|
210
|
+
const left = sliceNode(startNode, 0, startOffset);
|
|
211
|
+
if (left)
|
|
212
|
+
middle.push(left);
|
|
213
|
+
// Keep right part of end node
|
|
214
|
+
const right = sliceNode(endNode, endOffset, getTextLength(endNode));
|
|
215
|
+
if (right) {
|
|
216
|
+
const merged = middle.length > 0 ? tryMerge(middle[middle.length - 1], right) : null;
|
|
217
|
+
if (merged)
|
|
218
|
+
middle[middle.length - 1] = merged;
|
|
219
|
+
else
|
|
220
|
+
middle.push(right);
|
|
221
|
+
}
|
|
222
|
+
// Merge all boundaries
|
|
223
|
+
const result = [];
|
|
224
|
+
for (const node of [...before, ...middle, ...after]) {
|
|
225
|
+
const prev = result[result.length - 1];
|
|
226
|
+
const merged = prev ? tryMerge(prev, node) : null;
|
|
227
|
+
if (merged)
|
|
228
|
+
result[result.length - 1] = merged;
|
|
229
|
+
else
|
|
230
|
+
result.push(node);
|
|
231
|
+
}
|
|
232
|
+
return Result.Ok(result);
|
|
233
|
+
}
|
|
234
|
+
// ─── splitBlock ────────────────────────────────────────────────────────────────
|
|
235
|
+
/**
|
|
236
|
+
* Split a block at a given position into two blocks.
|
|
237
|
+
* The original block keeps content before the cursor.
|
|
238
|
+
* A new block gets content after the cursor — always of type "paragraph".
|
|
239
|
+
*
|
|
240
|
+
* Not supported for code/equation blocks — returns Err.
|
|
241
|
+
*/
|
|
242
|
+
export function splitBlock(block, nodeIndex, offset) {
|
|
243
|
+
var _a;
|
|
244
|
+
if (block.type === "code" || block.type === "equation")
|
|
245
|
+
return Result.Err(`splitBlock is not supported for "${block.type}" blocks`);
|
|
246
|
+
const nodes = block.content;
|
|
247
|
+
if (nodes.length > 0 && (nodeIndex < 0 || nodeIndex >= nodes.length))
|
|
248
|
+
return Result.Err(`nodeIndex (${nodeIndex}) out of bounds`);
|
|
249
|
+
const target = (_a = nodes[nodeIndex]) !== null && _a !== void 0 ? _a : null;
|
|
250
|
+
const targetLen = target ? getTextLength(target) : 0;
|
|
251
|
+
if (offset < 0 || offset > targetLen)
|
|
252
|
+
return Result.Err(`offset (${offset}) out of bounds`);
|
|
253
|
+
// Content before cursor stays in original block
|
|
254
|
+
const beforeNodes = [
|
|
255
|
+
...nodes.slice(0, nodeIndex),
|
|
256
|
+
...(target && offset > 0 ? [sliceNode(target, 0, offset)].filter(Boolean) : []),
|
|
257
|
+
];
|
|
258
|
+
// Content after cursor goes to new block
|
|
259
|
+
const afterNodes = [
|
|
260
|
+
...(target && offset < targetLen ? [sliceNode(target, offset, targetLen)].filter(Boolean) : []),
|
|
261
|
+
...nodes.slice(nodeIndex + 1),
|
|
262
|
+
];
|
|
263
|
+
const original = {
|
|
264
|
+
...block,
|
|
265
|
+
content: beforeNodes,
|
|
266
|
+
};
|
|
267
|
+
const newBlock = {
|
|
268
|
+
id: generateId(),
|
|
269
|
+
type: "paragraph",
|
|
270
|
+
content: afterNodes,
|
|
271
|
+
meta: {},
|
|
272
|
+
};
|
|
273
|
+
return Result.Ok([original, newBlock]);
|
|
274
|
+
}
|
|
275
|
+
// ─── mergeBlocks ───────────────────────────────────────────────────────────────
|
|
276
|
+
/**
|
|
277
|
+
* Merge blockB into blockA — blockB's content is appended to blockA's content.
|
|
278
|
+
* blockA's type and meta are preserved.
|
|
279
|
+
*
|
|
280
|
+
* Not supported if either block is code or equation — returns Err.
|
|
281
|
+
*/
|
|
282
|
+
export function mergeBlocks(blockA, blockB) {
|
|
283
|
+
if (blockA.type === "code" || blockA.type === "equation")
|
|
284
|
+
return Result.Err(`mergeBlocks: blockA cannot be of type "${blockA.type}"`);
|
|
285
|
+
if (blockB.type === "code" || blockB.type === "equation")
|
|
286
|
+
return Result.Err(`mergeBlocks: blockB cannot be of type "${blockB.type}"`);
|
|
287
|
+
const nodesA = blockA.content;
|
|
288
|
+
const nodesB = blockB.content;
|
|
289
|
+
// Merge boundaries between the two arrays
|
|
290
|
+
const result = [...nodesA];
|
|
291
|
+
for (const node of nodesB) {
|
|
292
|
+
const prev = result[result.length - 1];
|
|
293
|
+
const merged = prev ? tryMerge(prev, node) : null;
|
|
294
|
+
if (merged)
|
|
295
|
+
result[result.length - 1] = merged;
|
|
296
|
+
else
|
|
297
|
+
result.push(node);
|
|
298
|
+
}
|
|
299
|
+
return Result.Ok({
|
|
300
|
+
...blockA,
|
|
301
|
+
content: result,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
// ─── replaceRange ──────────────────────────────────────────────────────────────
|
|
305
|
+
/**
|
|
306
|
+
* Replace a selected range with an incoming Node — atomic deleteRange + insertAt.
|
|
307
|
+
* This is what fires when the user has a selection and types a character.
|
|
308
|
+
*
|
|
309
|
+
* Internally chains:
|
|
310
|
+
* 1. deleteRange — remove selected content
|
|
311
|
+
* 2. insertAt — insert incoming at the start of the deleted range
|
|
312
|
+
*/
|
|
313
|
+
export function replaceRange(block, startNodeIndex, startOffset, endNodeIndex, endOffset, incoming) {
|
|
314
|
+
return deleteRange(block, startNodeIndex, startOffset, endNodeIndex, endOffset)
|
|
315
|
+
.andThen((content) => insertAt({ ...block, content }, startNodeIndex, startOffset, incoming));
|
|
316
|
+
}
|
|
317
|
+
//# sourceMappingURL=content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/engine/content.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Node } from '../types/block';
|
|
2
|
+
import { Result } from '@reiwuzen/result';
|
|
3
|
+
type TextNode = Extract<Node, {
|
|
4
|
+
type: "text";
|
|
5
|
+
}>;
|
|
6
|
+
type TextFormat = Pick<TextNode, "bold" | "italic" | "underline" | "strikethrough" | "highlighted" | "color" | "link">;
|
|
7
|
+
export type NodeSelection = {
|
|
8
|
+
startIndex: number;
|
|
9
|
+
startOffset: number;
|
|
10
|
+
endIndex: number;
|
|
11
|
+
endOffset: number;
|
|
12
|
+
};
|
|
13
|
+
export declare function isTextNode(node: Node): node is TextNode;
|
|
14
|
+
export declare function formatNodes(nodes: Node[], sel: NodeSelection, format: keyof TextFormat, value?: unknown): Result<Node[]>;
|
|
15
|
+
export declare function mergeAdjacentNodes(nodes: Node[]): Node[];
|
|
16
|
+
export declare function formatsMatch(a: TextNode, b: TextNode): boolean;
|
|
17
|
+
export declare const toggleBold: (nodes: Node[], sel: NodeSelection) => Result<Node[], string>;
|
|
18
|
+
export declare const toggleItalic: (nodes: Node[], sel: NodeSelection) => Result<Node[], string>;
|
|
19
|
+
export declare const toggleUnderline: (nodes: Node[], sel: NodeSelection) => Result<Node[], string>;
|
|
20
|
+
export declare const toggleStrikethrough: (nodes: Node[], sel: NodeSelection) => Result<Node[], string>;
|
|
21
|
+
export declare const toggleHighlight: (nodes: Node[], sel: NodeSelection, color?: "yellow" | "green") => Result<Node[], string>;
|
|
22
|
+
export declare const toggleColor: (nodes: Node[], sel: NodeSelection, color: "red" | "blue" | "green") => Result<Node[], string>;
|
|
23
|
+
export declare const setLink: (nodes: Node[], sel: NodeSelection, href: string) => Result<Node[], string>;
|
|
24
|
+
export declare const removeLink: (nodes: Node[], sel: NodeSelection) => Result<Node[], string>;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/engine/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI1C,KAAK,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEhD,KAAK,UAAU,GAAG,IAAI,CACpB,QAAQ,EACR,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,eAAe,GAAG,aAAa,GAAG,OAAO,GAAG,MAAM,CACrF,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AA4CF,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,IAAI,QAAQ,CAEvD;AAiDD,wBAAgB,WAAW,CACzB,KAAK,EAAE,IAAI,EAAE,EACb,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,MAAM,UAAU,EACxB,KAAK,GAAE,OAAc,GACpB,MAAM,CAAC,IAAI,EAAE,CAAC,CAqChB;AAID,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAWxD;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAK9D;AAID,eAAO,MAAM,UAAU,GAAI,OAAO,IAAI,EAAE,EAAE,KAAK,aAAa,2BAC3B,CAAC;AAElC,eAAO,MAAM,YAAY,GAAI,OAAO,IAAI,EAAE,EAAE,KAAK,aAAa,2BAC3B,CAAC;AAEpC,eAAO,MAAM,eAAe,GAAI,OAAO,IAAI,EAAE,EAAE,KAAK,aAAa,2BAC3B,CAAC;AAEvC,eAAO,MAAM,mBAAmB,GAAI,OAAO,IAAI,EAAE,EAAE,KAAK,aAAa,2BAC3B,CAAC;AAE3C,eAAO,MAAM,eAAe,GAC1B,OAAO,IAAI,EAAE,EACb,KAAK,aAAa,EAClB,QAAO,QAAQ,GAAG,OAAkB,2BACY,CAAC;AAEnD,eAAO,MAAM,WAAW,GACtB,OAAO,IAAI,EAAE,EACb,KAAK,aAAa,EAClB,OAAO,KAAK,GAAG,MAAM,GAAG,OAAO,2BACW,CAAC;AAE7C,eAAO,MAAM,OAAO,GAAI,OAAO,IAAI,EAAE,EAAE,KAAK,aAAa,EAAE,MAAM,MAAM,2BAChC,CAAC;AAExC,eAAO,MAAM,UAAU,GAAI,OAAO,IAAI,EAAE,EAAE,KAAK,aAAa,2BAChB,CAAC"}
|