@reiwuzen/blocky 1.3.4 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +33 -11
- package/dist/index.d.cts +77 -22
- package/dist/index.d.ts +77 -22
- package/dist/index.js +32 -11
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
applyMarkdownTransform: () => applyMarkdownTransform,
|
|
24
|
+
areBlocksSame: () => areBlocksSame,
|
|
24
25
|
canRedo: () => canRedo,
|
|
25
26
|
canUndo: () => canUndo,
|
|
26
27
|
changeBlockType: () => changeBlockType,
|
|
@@ -619,18 +620,15 @@ function buildMetaForTarget(targetType) {
|
|
|
619
620
|
case "number":
|
|
620
621
|
case "todo":
|
|
621
622
|
return { depth: 0 };
|
|
622
|
-
|
|
623
|
-
case "heading2":
|
|
624
|
-
case "heading3":
|
|
625
|
-
case "paragraph":
|
|
626
|
-
case "code":
|
|
627
|
-
case "equation":
|
|
623
|
+
default:
|
|
628
624
|
return {};
|
|
629
625
|
}
|
|
630
626
|
}
|
|
627
|
+
function isList(type) {
|
|
628
|
+
return type === "bullet" || type === "todo" || type === "number";
|
|
629
|
+
}
|
|
631
630
|
function deriveMeta(block, targetType) {
|
|
632
|
-
if (isList(block.type) && isList(targetType))
|
|
633
|
-
return block.meta;
|
|
631
|
+
if (isList(block.type) && isList(targetType)) return block.meta;
|
|
634
632
|
return buildMetaForTarget(targetType);
|
|
635
633
|
}
|
|
636
634
|
function toggleTodo(block) {
|
|
@@ -645,9 +643,6 @@ var MAX_DEPTH = 6;
|
|
|
645
643
|
function isIndentable(block) {
|
|
646
644
|
return block.type === "bullet" || block.type === "number" || block.type === "todo";
|
|
647
645
|
}
|
|
648
|
-
function isList(type) {
|
|
649
|
-
return type === "bullet" || type === "todo" || type === "number";
|
|
650
|
-
}
|
|
651
646
|
function indentBlock(block) {
|
|
652
647
|
if (!isIndentable(block))
|
|
653
648
|
return import_result4.Result.Err(
|
|
@@ -671,6 +666,31 @@ function outdentBlock(block) {
|
|
|
671
666
|
meta: { ...block.meta, depth: block.meta.depth - 1 }
|
|
672
667
|
});
|
|
673
668
|
}
|
|
669
|
+
function areBlocksSame(blocks, withBlocks) {
|
|
670
|
+
if (blocks.length !== withBlocks.length) return false;
|
|
671
|
+
return blocks.every((a, i) => {
|
|
672
|
+
const b = withBlocks[i];
|
|
673
|
+
if (a.type !== b.type) return false;
|
|
674
|
+
if (JSON.stringify(a.meta) !== JSON.stringify(b.meta)) return false;
|
|
675
|
+
if (a.content.length !== b.content.length) return false;
|
|
676
|
+
return a.content.every((an, j) => {
|
|
677
|
+
const bn = b.content[j];
|
|
678
|
+
if (an.type !== bn.type) return false;
|
|
679
|
+
switch (an.type) {
|
|
680
|
+
case "code":
|
|
681
|
+
return an.text === bn.text;
|
|
682
|
+
case "equation":
|
|
683
|
+
return an.latex === bn.latex;
|
|
684
|
+
case "text": {
|
|
685
|
+
const bt = bn;
|
|
686
|
+
return an.text === bt.text && an.bold === bt.bold && an.italic === bt.italic && an.underline === bt.underline && an.strikethrough === bt.strikethrough && an.highlighted === bt.highlighted && an.color === bt.color && an.link === bt.link;
|
|
687
|
+
}
|
|
688
|
+
default:
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
});
|
|
693
|
+
}
|
|
674
694
|
|
|
675
695
|
// src/engine/serializer.ts
|
|
676
696
|
var import_result5 = require("@reiwuzen/result");
|
|
@@ -769,6 +789,7 @@ function nodesToMarkdown(nodes) {
|
|
|
769
789
|
return nodes.map((n) => {
|
|
770
790
|
if (n.type === "code") return `\`${n.text}\``;
|
|
771
791
|
if (n.type === "equation") return `$${n.latex}$`;
|
|
792
|
+
if (n.type === "ref") return `[${n.label}](${n.href})`;
|
|
772
793
|
let text = n.text;
|
|
773
794
|
if (n.bold) text = `**${text}**`;
|
|
774
795
|
if (n.italic) text = `*${text}*`;
|
|
@@ -857,6 +878,7 @@ var currentBlocks = (history) => history.present.blocks;
|
|
|
857
878
|
// Annotate the CommonJS export names for ESM import in node:
|
|
858
879
|
0 && (module.exports = {
|
|
859
880
|
applyMarkdownTransform,
|
|
881
|
+
areBlocksSame,
|
|
860
882
|
canRedo,
|
|
861
883
|
canUndo,
|
|
862
884
|
changeBlockType,
|
package/dist/index.d.cts
CHANGED
|
@@ -5,12 +5,16 @@ type Node = {
|
|
|
5
5
|
type: "text";
|
|
6
6
|
text: string;
|
|
7
7
|
highlighted?: "yellow" | "green";
|
|
8
|
-
color?: "red" |
|
|
8
|
+
color?: "red" | "blue" | "green";
|
|
9
9
|
bold?: true;
|
|
10
10
|
italic?: true;
|
|
11
11
|
underline?: true;
|
|
12
12
|
strikethrough?: true;
|
|
13
13
|
link?: string;
|
|
14
|
+
} | {
|
|
15
|
+
type: "ref";
|
|
16
|
+
href: string;
|
|
17
|
+
label: string;
|
|
14
18
|
} | {
|
|
15
19
|
type: "code";
|
|
16
20
|
text: string;
|
|
@@ -111,47 +115,98 @@ declare const removeLink: (nodes: Node[], sel: NodeSelection) => Result<Node[],
|
|
|
111
115
|
|
|
112
116
|
type TransformResult = {
|
|
113
117
|
block: AnyBlock;
|
|
118
|
+
/** true if the block type was actually changed, false if no trigger matched */
|
|
114
119
|
converted: boolean;
|
|
115
120
|
};
|
|
116
121
|
/**
|
|
117
|
-
*
|
|
122
|
+
* Attempt to convert a paragraph block into another type based on a
|
|
123
|
+
* Markdown-style prefix typed by the user.
|
|
124
|
+
*
|
|
125
|
+
* Call this when a **space** is typed inside a paragraph block.
|
|
126
|
+
*
|
|
127
|
+
* Supported triggers (must appear at position 0, immediately before the space):
|
|
128
|
+
*
|
|
129
|
+
* | Typed | Converts to |
|
|
130
|
+
* |----------|-------------|
|
|
131
|
+
* | `-` | bullet |
|
|
132
|
+
* | `1.` | number |
|
|
133
|
+
* | `[]` | todo |
|
|
134
|
+
* | `#` | heading1 |
|
|
135
|
+
* | `##` | heading2 |
|
|
136
|
+
* | `###` | heading3 |
|
|
118
137
|
*
|
|
119
|
-
* Guards
|
|
120
|
-
* 1. block.type must be "paragraph"
|
|
121
|
-
* 2.
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
138
|
+
* Guards — returns `converted: false` without mutating if any fail:
|
|
139
|
+
* 1. `block.type` must be `"paragraph"`
|
|
140
|
+
* 2. The text must start with a known trigger prefix
|
|
141
|
+
* 3. `cursorOffset` must be ≥ `trigger.length + 1` — ensures the space
|
|
142
|
+
* was typed right after the trigger (blocks mid-text triggers like
|
|
143
|
+
* `"hello - "` from accidentally converting)
|
|
144
|
+
*
|
|
145
|
+
* On success the returned block has the trigger prefix stripped from its
|
|
146
|
+
* content and a freshly built `meta` for the target type.
|
|
125
147
|
*/
|
|
126
148
|
declare function applyMarkdownTransform(block: AnyBlock, cursorOffset: number): Result<TransformResult>;
|
|
127
149
|
/**
|
|
128
|
-
* Convert a block to a new type
|
|
150
|
+
* Convert a block to a new type, preserving content and meta where possible.
|
|
151
|
+
*
|
|
152
|
+
* Content conversion rules:
|
|
129
153
|
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* rich
|
|
133
|
-
* code
|
|
134
|
-
* equation
|
|
135
|
-
* code
|
|
136
|
-
* equation
|
|
154
|
+
* | From | To | Content result |
|
|
155
|
+
* |-------------|-------------|----------------------------------------|
|
|
156
|
+
* | rich | rich | kept as-is |
|
|
157
|
+
* | rich | code | all text concatenated → `[CodeNode]` |
|
|
158
|
+
* | rich | equation | all text concatenated → `[EquationNode]`|
|
|
159
|
+
* | code | rich | `code.text` → single `TextNode` |
|
|
160
|
+
* | equation | rich | `equation.latex` → single `TextNode` |
|
|
161
|
+
* | code | equation | `code.text` as `latex` |
|
|
162
|
+
* | equation | code | `equation.latex` as `text` |
|
|
163
|
+
*
|
|
164
|
+
* Meta conversion rules:
|
|
165
|
+
* - list → list : `meta` (including `depth`) is preserved
|
|
166
|
+
* - anything else: fresh default `meta` is built for the target type
|
|
137
167
|
*/
|
|
138
168
|
declare function changeBlockType<T extends BlockType>(block: AnyBlock, targetType: T): Result<Block<T>>;
|
|
139
169
|
/**
|
|
140
170
|
* Toggle the checked state of a todo block.
|
|
141
|
-
*
|
|
171
|
+
*
|
|
172
|
+
* Cycle: `undefined → true → undefined`
|
|
173
|
+
*
|
|
174
|
+
* Returns `Err` if the block is not of type `"todo"`.
|
|
142
175
|
*/
|
|
143
176
|
declare function toggleTodo(block: AnyBlock): Result<Block<"todo">>;
|
|
144
177
|
type IndentableBlock = Block<"bullet"> | Block<"number"> | Block<"todo">;
|
|
145
178
|
/**
|
|
146
|
-
* Increase the depth of a bullet
|
|
147
|
-
*
|
|
179
|
+
* Increase the indentation depth of a list block (`bullet`, `number`, `todo`) by 1.
|
|
180
|
+
*
|
|
181
|
+
* - Maximum depth is `6`.
|
|
182
|
+
* - Returns `Err` if the block type is not indentable or is already at max depth.
|
|
148
183
|
*/
|
|
149
184
|
declare function indentBlock(block: AnyBlock): Result<IndentableBlock>;
|
|
150
185
|
/**
|
|
151
|
-
* Decrease the depth of a bullet
|
|
152
|
-
*
|
|
186
|
+
* Decrease the indentation depth of a list block (`bullet`, `number`, `todo`) by 1.
|
|
187
|
+
*
|
|
188
|
+
* - Minimum depth is `0`.
|
|
189
|
+
* - Returns `Err` if the block type is not indentable or is already at depth 0.
|
|
153
190
|
*/
|
|
154
191
|
declare function outdentBlock(block: AnyBlock): Result<IndentableBlock>;
|
|
192
|
+
/**
|
|
193
|
+
* Deep-equality check between two block arrays.
|
|
194
|
+
*
|
|
195
|
+
* Two arrays are considered the same when:
|
|
196
|
+
* - They have the same length
|
|
197
|
+
* - Every block at the same index has the same `id`, `type`, `meta`, and `content`
|
|
198
|
+
*
|
|
199
|
+
* Content nodes are compared field-by-field. Unknown extra fields on nodes are
|
|
200
|
+
* ignored so the check stays stable across schema additions.
|
|
201
|
+
*
|
|
202
|
+
* Useful for preventing redundant saves or snapshot writes:
|
|
203
|
+
* ```ts
|
|
204
|
+
* if (!areBlocksSame(current, saved)) {
|
|
205
|
+
* await createSnapshot(current);
|
|
206
|
+
* }
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
declare function areBlocksSame(blocks: AnyBlock[], withBlocks: AnyBlock[]): boolean;
|
|
155
210
|
|
|
156
211
|
/**
|
|
157
212
|
* Serialize an array of blocks to a JSON string.
|
|
@@ -267,4 +322,4 @@ declare function duplicateBlockAfter(blocks: AnyBlock[], id: string, newId?: str
|
|
|
267
322
|
*/
|
|
268
323
|
declare function moveBlock(blocks: AnyBlock[], id: string, direction: "up" | "down"): Result<AnyBlock[], string>;
|
|
269
324
|
|
|
270
|
-
export { type AnyBlock, type Block, type BlockContent, type BlockMeta, type BlockType, type History, type HistoryEntry, type Node, type NodeSelection, applyMarkdownTransform, canRedo, canUndo, changeBlockType, createBlock, createBlockAfter, createHistory, currentBlocks, deleteBlock, deleteLastChar, deleteRange, deserialize, deserializeNodes, duplicateBlock, duplicateBlockAfter, formatNodes, generateId, indentBlock, insertAt, insertBlockAfter, mergeAdjacentNodes, mergeBlocks, moveBlock, outdentBlock, push, redo, removeLink, replaceRange, serialize, serializeNodes, setLink, splitBlock, toMarkdown, toPlainText, toggleBold, toggleColor, toggleHighlight, toggleItalic, toggleStrikethrough, toggleTodo, toggleUnderline, undo };
|
|
325
|
+
export { type AnyBlock, type Block, type BlockContent, type BlockMeta, type BlockType, type History, type HistoryEntry, type Node, type NodeSelection, applyMarkdownTransform, areBlocksSame, canRedo, canUndo, changeBlockType, createBlock, createBlockAfter, createHistory, currentBlocks, deleteBlock, deleteLastChar, deleteRange, deserialize, deserializeNodes, duplicateBlock, duplicateBlockAfter, formatNodes, generateId, indentBlock, insertAt, insertBlockAfter, mergeAdjacentNodes, mergeBlocks, moveBlock, outdentBlock, push, redo, removeLink, replaceRange, serialize, serializeNodes, setLink, splitBlock, toMarkdown, toPlainText, toggleBold, toggleColor, toggleHighlight, toggleItalic, toggleStrikethrough, toggleTodo, toggleUnderline, undo };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,12 +5,16 @@ type Node = {
|
|
|
5
5
|
type: "text";
|
|
6
6
|
text: string;
|
|
7
7
|
highlighted?: "yellow" | "green";
|
|
8
|
-
color?: "red" |
|
|
8
|
+
color?: "red" | "blue" | "green";
|
|
9
9
|
bold?: true;
|
|
10
10
|
italic?: true;
|
|
11
11
|
underline?: true;
|
|
12
12
|
strikethrough?: true;
|
|
13
13
|
link?: string;
|
|
14
|
+
} | {
|
|
15
|
+
type: "ref";
|
|
16
|
+
href: string;
|
|
17
|
+
label: string;
|
|
14
18
|
} | {
|
|
15
19
|
type: "code";
|
|
16
20
|
text: string;
|
|
@@ -111,47 +115,98 @@ declare const removeLink: (nodes: Node[], sel: NodeSelection) => Result<Node[],
|
|
|
111
115
|
|
|
112
116
|
type TransformResult = {
|
|
113
117
|
block: AnyBlock;
|
|
118
|
+
/** true if the block type was actually changed, false if no trigger matched */
|
|
114
119
|
converted: boolean;
|
|
115
120
|
};
|
|
116
121
|
/**
|
|
117
|
-
*
|
|
122
|
+
* Attempt to convert a paragraph block into another type based on a
|
|
123
|
+
* Markdown-style prefix typed by the user.
|
|
124
|
+
*
|
|
125
|
+
* Call this when a **space** is typed inside a paragraph block.
|
|
126
|
+
*
|
|
127
|
+
* Supported triggers (must appear at position 0, immediately before the space):
|
|
128
|
+
*
|
|
129
|
+
* | Typed | Converts to |
|
|
130
|
+
* |----------|-------------|
|
|
131
|
+
* | `-` | bullet |
|
|
132
|
+
* | `1.` | number |
|
|
133
|
+
* | `[]` | todo |
|
|
134
|
+
* | `#` | heading1 |
|
|
135
|
+
* | `##` | heading2 |
|
|
136
|
+
* | `###` | heading3 |
|
|
118
137
|
*
|
|
119
|
-
* Guards
|
|
120
|
-
* 1. block.type must be "paragraph"
|
|
121
|
-
* 2.
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
138
|
+
* Guards — returns `converted: false` without mutating if any fail:
|
|
139
|
+
* 1. `block.type` must be `"paragraph"`
|
|
140
|
+
* 2. The text must start with a known trigger prefix
|
|
141
|
+
* 3. `cursorOffset` must be ≥ `trigger.length + 1` — ensures the space
|
|
142
|
+
* was typed right after the trigger (blocks mid-text triggers like
|
|
143
|
+
* `"hello - "` from accidentally converting)
|
|
144
|
+
*
|
|
145
|
+
* On success the returned block has the trigger prefix stripped from its
|
|
146
|
+
* content and a freshly built `meta` for the target type.
|
|
125
147
|
*/
|
|
126
148
|
declare function applyMarkdownTransform(block: AnyBlock, cursorOffset: number): Result<TransformResult>;
|
|
127
149
|
/**
|
|
128
|
-
* Convert a block to a new type
|
|
150
|
+
* Convert a block to a new type, preserving content and meta where possible.
|
|
151
|
+
*
|
|
152
|
+
* Content conversion rules:
|
|
129
153
|
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* rich
|
|
133
|
-
* code
|
|
134
|
-
* equation
|
|
135
|
-
* code
|
|
136
|
-
* equation
|
|
154
|
+
* | From | To | Content result |
|
|
155
|
+
* |-------------|-------------|----------------------------------------|
|
|
156
|
+
* | rich | rich | kept as-is |
|
|
157
|
+
* | rich | code | all text concatenated → `[CodeNode]` |
|
|
158
|
+
* | rich | equation | all text concatenated → `[EquationNode]`|
|
|
159
|
+
* | code | rich | `code.text` → single `TextNode` |
|
|
160
|
+
* | equation | rich | `equation.latex` → single `TextNode` |
|
|
161
|
+
* | code | equation | `code.text` as `latex` |
|
|
162
|
+
* | equation | code | `equation.latex` as `text` |
|
|
163
|
+
*
|
|
164
|
+
* Meta conversion rules:
|
|
165
|
+
* - list → list : `meta` (including `depth`) is preserved
|
|
166
|
+
* - anything else: fresh default `meta` is built for the target type
|
|
137
167
|
*/
|
|
138
168
|
declare function changeBlockType<T extends BlockType>(block: AnyBlock, targetType: T): Result<Block<T>>;
|
|
139
169
|
/**
|
|
140
170
|
* Toggle the checked state of a todo block.
|
|
141
|
-
*
|
|
171
|
+
*
|
|
172
|
+
* Cycle: `undefined → true → undefined`
|
|
173
|
+
*
|
|
174
|
+
* Returns `Err` if the block is not of type `"todo"`.
|
|
142
175
|
*/
|
|
143
176
|
declare function toggleTodo(block: AnyBlock): Result<Block<"todo">>;
|
|
144
177
|
type IndentableBlock = Block<"bullet"> | Block<"number"> | Block<"todo">;
|
|
145
178
|
/**
|
|
146
|
-
* Increase the depth of a bullet
|
|
147
|
-
*
|
|
179
|
+
* Increase the indentation depth of a list block (`bullet`, `number`, `todo`) by 1.
|
|
180
|
+
*
|
|
181
|
+
* - Maximum depth is `6`.
|
|
182
|
+
* - Returns `Err` if the block type is not indentable or is already at max depth.
|
|
148
183
|
*/
|
|
149
184
|
declare function indentBlock(block: AnyBlock): Result<IndentableBlock>;
|
|
150
185
|
/**
|
|
151
|
-
* Decrease the depth of a bullet
|
|
152
|
-
*
|
|
186
|
+
* Decrease the indentation depth of a list block (`bullet`, `number`, `todo`) by 1.
|
|
187
|
+
*
|
|
188
|
+
* - Minimum depth is `0`.
|
|
189
|
+
* - Returns `Err` if the block type is not indentable or is already at depth 0.
|
|
153
190
|
*/
|
|
154
191
|
declare function outdentBlock(block: AnyBlock): Result<IndentableBlock>;
|
|
192
|
+
/**
|
|
193
|
+
* Deep-equality check between two block arrays.
|
|
194
|
+
*
|
|
195
|
+
* Two arrays are considered the same when:
|
|
196
|
+
* - They have the same length
|
|
197
|
+
* - Every block at the same index has the same `id`, `type`, `meta`, and `content`
|
|
198
|
+
*
|
|
199
|
+
* Content nodes are compared field-by-field. Unknown extra fields on nodes are
|
|
200
|
+
* ignored so the check stays stable across schema additions.
|
|
201
|
+
*
|
|
202
|
+
* Useful for preventing redundant saves or snapshot writes:
|
|
203
|
+
* ```ts
|
|
204
|
+
* if (!areBlocksSame(current, saved)) {
|
|
205
|
+
* await createSnapshot(current);
|
|
206
|
+
* }
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
declare function areBlocksSame(blocks: AnyBlock[], withBlocks: AnyBlock[]): boolean;
|
|
155
210
|
|
|
156
211
|
/**
|
|
157
212
|
* Serialize an array of blocks to a JSON string.
|
|
@@ -267,4 +322,4 @@ declare function duplicateBlockAfter(blocks: AnyBlock[], id: string, newId?: str
|
|
|
267
322
|
*/
|
|
268
323
|
declare function moveBlock(blocks: AnyBlock[], id: string, direction: "up" | "down"): Result<AnyBlock[], string>;
|
|
269
324
|
|
|
270
|
-
export { type AnyBlock, type Block, type BlockContent, type BlockMeta, type BlockType, type History, type HistoryEntry, type Node, type NodeSelection, applyMarkdownTransform, canRedo, canUndo, changeBlockType, createBlock, createBlockAfter, createHistory, currentBlocks, deleteBlock, deleteLastChar, deleteRange, deserialize, deserializeNodes, duplicateBlock, duplicateBlockAfter, formatNodes, generateId, indentBlock, insertAt, insertBlockAfter, mergeAdjacentNodes, mergeBlocks, moveBlock, outdentBlock, push, redo, removeLink, replaceRange, serialize, serializeNodes, setLink, splitBlock, toMarkdown, toPlainText, toggleBold, toggleColor, toggleHighlight, toggleItalic, toggleStrikethrough, toggleTodo, toggleUnderline, undo };
|
|
325
|
+
export { type AnyBlock, type Block, type BlockContent, type BlockMeta, type BlockType, type History, type HistoryEntry, type Node, type NodeSelection, applyMarkdownTransform, areBlocksSame, canRedo, canUndo, changeBlockType, createBlock, createBlockAfter, createHistory, currentBlocks, deleteBlock, deleteLastChar, deleteRange, deserialize, deserializeNodes, duplicateBlock, duplicateBlockAfter, formatNodes, generateId, indentBlock, insertAt, insertBlockAfter, mergeAdjacentNodes, mergeBlocks, moveBlock, outdentBlock, push, redo, removeLink, replaceRange, serialize, serializeNodes, setLink, splitBlock, toMarkdown, toPlainText, toggleBold, toggleColor, toggleHighlight, toggleItalic, toggleStrikethrough, toggleTodo, toggleUnderline, undo };
|
package/dist/index.js
CHANGED
|
@@ -552,18 +552,15 @@ function buildMetaForTarget(targetType) {
|
|
|
552
552
|
case "number":
|
|
553
553
|
case "todo":
|
|
554
554
|
return { depth: 0 };
|
|
555
|
-
|
|
556
|
-
case "heading2":
|
|
557
|
-
case "heading3":
|
|
558
|
-
case "paragraph":
|
|
559
|
-
case "code":
|
|
560
|
-
case "equation":
|
|
555
|
+
default:
|
|
561
556
|
return {};
|
|
562
557
|
}
|
|
563
558
|
}
|
|
559
|
+
function isList(type) {
|
|
560
|
+
return type === "bullet" || type === "todo" || type === "number";
|
|
561
|
+
}
|
|
564
562
|
function deriveMeta(block, targetType) {
|
|
565
|
-
if (isList(block.type) && isList(targetType))
|
|
566
|
-
return block.meta;
|
|
563
|
+
if (isList(block.type) && isList(targetType)) return block.meta;
|
|
567
564
|
return buildMetaForTarget(targetType);
|
|
568
565
|
}
|
|
569
566
|
function toggleTodo(block) {
|
|
@@ -578,9 +575,6 @@ var MAX_DEPTH = 6;
|
|
|
578
575
|
function isIndentable(block) {
|
|
579
576
|
return block.type === "bullet" || block.type === "number" || block.type === "todo";
|
|
580
577
|
}
|
|
581
|
-
function isList(type) {
|
|
582
|
-
return type === "bullet" || type === "todo" || type === "number";
|
|
583
|
-
}
|
|
584
578
|
function indentBlock(block) {
|
|
585
579
|
if (!isIndentable(block))
|
|
586
580
|
return Result4.Err(
|
|
@@ -604,6 +598,31 @@ function outdentBlock(block) {
|
|
|
604
598
|
meta: { ...block.meta, depth: block.meta.depth - 1 }
|
|
605
599
|
});
|
|
606
600
|
}
|
|
601
|
+
function areBlocksSame(blocks, withBlocks) {
|
|
602
|
+
if (blocks.length !== withBlocks.length) return false;
|
|
603
|
+
return blocks.every((a, i) => {
|
|
604
|
+
const b = withBlocks[i];
|
|
605
|
+
if (a.type !== b.type) return false;
|
|
606
|
+
if (JSON.stringify(a.meta) !== JSON.stringify(b.meta)) return false;
|
|
607
|
+
if (a.content.length !== b.content.length) return false;
|
|
608
|
+
return a.content.every((an, j) => {
|
|
609
|
+
const bn = b.content[j];
|
|
610
|
+
if (an.type !== bn.type) return false;
|
|
611
|
+
switch (an.type) {
|
|
612
|
+
case "code":
|
|
613
|
+
return an.text === bn.text;
|
|
614
|
+
case "equation":
|
|
615
|
+
return an.latex === bn.latex;
|
|
616
|
+
case "text": {
|
|
617
|
+
const bt = bn;
|
|
618
|
+
return an.text === bt.text && an.bold === bt.bold && an.italic === bt.italic && an.underline === bt.underline && an.strikethrough === bt.strikethrough && an.highlighted === bt.highlighted && an.color === bt.color && an.link === bt.link;
|
|
619
|
+
}
|
|
620
|
+
default:
|
|
621
|
+
return false;
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
});
|
|
625
|
+
}
|
|
607
626
|
|
|
608
627
|
// src/engine/serializer.ts
|
|
609
628
|
import { Result as Result5 } from "@reiwuzen/result";
|
|
@@ -702,6 +721,7 @@ function nodesToMarkdown(nodes) {
|
|
|
702
721
|
return nodes.map((n) => {
|
|
703
722
|
if (n.type === "code") return `\`${n.text}\``;
|
|
704
723
|
if (n.type === "equation") return `$${n.latex}$`;
|
|
724
|
+
if (n.type === "ref") return `[${n.label}](${n.href})`;
|
|
705
725
|
let text = n.text;
|
|
706
726
|
if (n.bold) text = `**${text}**`;
|
|
707
727
|
if (n.italic) text = `*${text}*`;
|
|
@@ -789,6 +809,7 @@ var canRedo = (history) => history.future.length > 0;
|
|
|
789
809
|
var currentBlocks = (history) => history.present.blocks;
|
|
790
810
|
export {
|
|
791
811
|
applyMarkdownTransform,
|
|
812
|
+
areBlocksSame,
|
|
792
813
|
canRedo,
|
|
793
814
|
canUndo,
|
|
794
815
|
changeBlockType,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reiwuzen/blocky",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Pure TypeScript block editor engine. Headless, framework-agnostic — content mutation, formatting, transforms, serialization, and history.",
|
|
5
5
|
"author": "Rei WuZen",
|
|
6
6
|
"license": "ISC",
|