@haklex/rich-plugin-block-handle 0.0.101 → 0.0.103

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.
@@ -1 +1 @@
1
- {"version":3,"file":"BlockHandlePlugin.d.ts","sourceRoot":"","sources":["../src/BlockHandlePlugin.tsx"],"names":[],"mappings":"AAmDA,OAAO,KAAK,EAAgD,YAAY,EAAE,MAAM,OAAO,CAAC;AAgpBxF,wBAAgB,iBAAiB,IAAI,YAAY,CAGhD"}
1
+ {"version":3,"file":"BlockHandlePlugin.d.ts","sourceRoot":"","sources":["../src/BlockHandlePlugin.tsx"],"names":[],"mappings":"AAoDA,OAAO,KAAK,EAAgD,YAAY,EAAE,MAAM,OAAO,CAAC;AA+xBxF,wBAAgB,iBAAiB,IAAI,YAAY,CAGhD"}
@@ -0,0 +1,4 @@
1
+ import { LexicalEditor, LexicalNode } from 'lexical';
2
+ export declare function buildBlockClipboardData(editor: LexicalEditor, nodes: readonly LexicalNode[]): Record<string, string>;
3
+ export declare function removeTopLevelNodesAndRestoreSelection(nodes: readonly LexicalNode[]): void;
4
+ //# sourceMappingURL=blockSelectionUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blockSelectionUtils.d.ts","sourceRoot":"","sources":["../src/blockSelectionUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,aAAa,EAClB,KAAK,WAAW,EACjB,MAAM,SAAS,CAAC;AA+BjB,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,SAAS,WAAW,EAAE,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA2CxB;AAED,wBAAgB,sCAAsC,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,CAkC1F"}
package/dist/index.mjs CHANGED
@@ -7,17 +7,489 @@ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext
7
7
  import { INSERT_HORIZONTAL_RULE_COMMAND } from "@lexical/react/LexicalHorizontalRuleNode";
8
8
  import { $createQuoteNode, $createHeadingNode } from "@lexical/rich-text";
9
9
  import { $setBlocksType } from "@lexical/selection";
10
- import { $getNearestNodeFromDOMNode, $getNodeByKey, $createParagraphNode, $isElementNode, $getSelection, $isRangeSelection, DRAGOVER_COMMAND, COMMAND_PRIORITY_HIGH, DROP_COMMAND, DRAGSTART_COMMAND } from "lexical";
10
+ import { $getRoot, $createParagraphNode, createEditor, $parseSerializedNode, $isElementNode, $createNodeSelection, $setSelection, $getSelection, $isNodeSelection, KEY_ARROW_DOWN_COMMAND, COMMAND_PRIORITY_CRITICAL, KEY_ARROW_UP_COMMAND, SELECT_ALL_COMMAND, $isRangeSelection, COMMAND_PRIORITY_HIGH, KEY_ESCAPE_COMMAND, COPY_COMMAND, CUT_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, REMOVE_TEXT_COMMAND, DELETE_CHARACTER_COMMAND, $getNearestNodeFromDOMNode, $getNodeByKey, DRAGOVER_COMMAND, DROP_COMMAND, DRAGSTART_COMMAND } from "lexical";
11
11
  import { Plus, GripVertical, Type, Heading1, Heading2, Heading3, List, ListOrdered, ListChecks, TextQuote, Minus, Code2, Copy, ArrowUp, ArrowDown, Trash2 } from "lucide-react";
12
- import { useState, useRef, useCallback, useEffect } from "react";
12
+ import { useRef, useCallback, useEffect, useState } from "react";
13
13
  import { createPortal } from "react-dom";
14
+ import { $generateHtmlFromNodes } from "@lexical/html";
14
15
  var handleContainer = "iihqkc0";
15
16
  var handleContainerVisible = "iihqkc1";
16
17
  var handleBtn = "iihqkc2";
17
18
  var draggingBlock = "iihqkc3";
18
19
  var dragPreview = "iihqkc4";
19
20
  var dropIndicator = "iihqkc5";
20
- var menuItemDestructive = "iihqkc6";
21
+ var blockSelected = "iihqkc6";
22
+ var dragCountBadge = "iihqkc7";
23
+ var menuItemDestructive = "iihqkc8";
24
+ function serializeNodeWithChildren(node) {
25
+ const serialized = node.exportJSON();
26
+ if ($isElementNode(node)) {
27
+ serialized.children = node.getChildren().map(serializeNodeWithChildren);
28
+ }
29
+ return serialized;
30
+ }
31
+ function selectTopLevelNode(node, placement) {
32
+ if ($isElementNode(node)) {
33
+ if (placement === "end") {
34
+ node.selectEnd();
35
+ } else {
36
+ node.selectStart();
37
+ }
38
+ return;
39
+ }
40
+ const selection = $createNodeSelection();
41
+ selection.add(node.getKey());
42
+ $setSelection(selection);
43
+ }
44
+ function buildBlockClipboardData(editor, nodes) {
45
+ const lexicalEditor = editor;
46
+ const namespace = lexicalEditor._config.namespace;
47
+ const serializedNodes = nodes.map(serializeNodeWithChildren);
48
+ let html = "";
49
+ try {
50
+ const registeredNodeKlasses = [
51
+ ...new Set([...lexicalEditor._nodes.values()].map((entry) => entry.klass))
52
+ ];
53
+ const tempEditor = createEditor({
54
+ namespace: `${namespace}-block-selection-html`,
55
+ nodes: registeredNodeKlasses,
56
+ onError: (error) => {
57
+ throw error;
58
+ }
59
+ });
60
+ tempEditor.update(
61
+ () => {
62
+ const root = $getRoot();
63
+ for (const serializedNode of serializedNodes) {
64
+ root.append($parseSerializedNode(serializedNode));
65
+ }
66
+ html = $generateHtmlFromNodes(tempEditor, null);
67
+ },
68
+ { discrete: true }
69
+ );
70
+ } catch {
71
+ html = "";
72
+ }
73
+ return {
74
+ "application/x-lexical-editor": JSON.stringify({
75
+ namespace,
76
+ nodes: serializedNodes
77
+ }),
78
+ "text/html": html,
79
+ "text/plain": nodes.map((node) => node.getTextContent()).join("\n\n")
80
+ };
81
+ }
82
+ function removeTopLevelNodesAndRestoreSelection(nodes) {
83
+ if (nodes.length === 0) return;
84
+ const firstNode = nodes[0];
85
+ const lastNode = nodes.at(-1);
86
+ const previousSibling = firstNode.getPreviousSibling();
87
+ const nextSibling = lastNode.getNextSibling();
88
+ for (const node of nodes) {
89
+ node.remove();
90
+ }
91
+ const root = $getRoot();
92
+ if (root.getChildrenSize() === 0) {
93
+ const paragraph = $createParagraphNode();
94
+ root.append(paragraph);
95
+ paragraph.selectStart();
96
+ return;
97
+ }
98
+ if (nextSibling) {
99
+ selectTopLevelNode(nextSibling, "start");
100
+ return;
101
+ }
102
+ if (previousSibling) {
103
+ selectTopLevelNode(previousSibling, "end");
104
+ return;
105
+ }
106
+ const fallbackNode = root.getFirstChild();
107
+ if (fallbackNode) {
108
+ selectTopLevelNode(fallbackNode, "start");
109
+ }
110
+ }
111
+ function $getTopLevelKeys() {
112
+ return $getRoot().getChildren().map((c) => c.getKey());
113
+ }
114
+ function $selectBlockRange(anchorKey, focusKey) {
115
+ const children = $getRoot().getChildren();
116
+ const anchorIdx = children.findIndex((c) => c.getKey() === anchorKey);
117
+ const focusIdx = children.findIndex((c) => c.getKey() === focusKey);
118
+ if (anchorIdx === -1 || focusIdx === -1) return;
119
+ const start = Math.min(anchorIdx, focusIdx);
120
+ const end = Math.max(anchorIdx, focusIdx);
121
+ const sel = $createNodeSelection();
122
+ for (let i = start; i <= end; i++) {
123
+ sel.add(children[i].getKey());
124
+ }
125
+ $setSelection(sel);
126
+ }
127
+ function useBlockSelection(editor) {
128
+ const anchorKeyRef = useRef(null);
129
+ const focusKeyRef = useRef(null);
130
+ const blockSelectionKeysRef = useRef(/* @__PURE__ */ new Set());
131
+ const clearBlockSelectionState = useCallback(() => {
132
+ blockSelectionKeysRef.current = /* @__PURE__ */ new Set();
133
+ anchorKeyRef.current = null;
134
+ focusKeyRef.current = null;
135
+ }, []);
136
+ const getTopLevelNodesByKeys = useCallback((keys) => {
137
+ if (keys.size === 0) return [];
138
+ return $getRoot().getChildren().filter((node) => keys.has(node.getKey()));
139
+ }, []);
140
+ const getOwnedSelectionNodes = useCallback(() => {
141
+ const ownedKeys = blockSelectionKeysRef.current;
142
+ if (ownedKeys.size === 0) return [];
143
+ const selection = $getSelection();
144
+ if (!$isNodeSelection(selection)) return [];
145
+ const selectionKeys = new Set(selection.getNodes().map((node) => node.getKey()));
146
+ const isOwnedSelection = selectionKeys.size === ownedKeys.size && [...selectionKeys].every((key) => ownedKeys.has(key));
147
+ if (!isOwnedSelection) return [];
148
+ return getTopLevelNodesByKeys(ownedKeys);
149
+ }, [getTopLevelNodesByKeys]);
150
+ const deleteBlocksByKeys = useCallback(
151
+ (keys) => {
152
+ editor.update(
153
+ () => {
154
+ const nodes = getTopLevelNodesByKeys(new Set(keys));
155
+ if (nodes.length === 0) return;
156
+ removeTopLevelNodesAndRestoreSelection(nodes);
157
+ clearBlockSelectionState();
158
+ },
159
+ { discrete: true }
160
+ );
161
+ },
162
+ [clearBlockSelectionState, editor, getTopLevelNodesByKeys]
163
+ );
164
+ useEffect(() => {
165
+ let prevKeys = /* @__PURE__ */ new Set();
166
+ const unregister = editor.registerUpdateListener(({ editorState }) => {
167
+ const rootEl = editor.getRootElement();
168
+ if (!rootEl) return;
169
+ const nextKeys = /* @__PURE__ */ new Set();
170
+ let isNodeSel = false;
171
+ let topLevelKeys = [];
172
+ editorState.read(() => {
173
+ const sel = $getSelection();
174
+ if ($isNodeSelection(sel)) {
175
+ isNodeSel = true;
176
+ for (const node of sel.getNodes()) {
177
+ nextKeys.add(node.getKey());
178
+ }
179
+ }
180
+ topLevelKeys = $getTopLevelKeys();
181
+ });
182
+ if (blockSelectionKeysRef.current.size > 0) {
183
+ if (!isNodeSel) {
184
+ clearBlockSelectionState();
185
+ } else {
186
+ const owned = blockSelectionKeysRef.current;
187
+ const stillOwned = nextKeys.size === owned.size && [...nextKeys].every((k) => owned.has(k));
188
+ if (!stillOwned) {
189
+ const topLevelSet = new Set(topLevelKeys);
190
+ const restoredTopLevel = [...nextKeys].filter((k) => topLevelSet.has(k));
191
+ if (restoredTopLevel.length > 1) {
192
+ const indices = restoredTopLevel.map((k) => topLevelKeys.indexOf(k)).sort((a, b) => a - b);
193
+ anchorKeyRef.current = topLevelKeys[indices[0]];
194
+ focusKeyRef.current = topLevelKeys[indices.at(-1)];
195
+ blockSelectionKeysRef.current = new Set(restoredTopLevel);
196
+ } else {
197
+ clearBlockSelectionState();
198
+ }
199
+ }
200
+ }
201
+ }
202
+ const highlightKeys = blockSelectionKeysRef.current.size > 0 ? nextKeys : /* @__PURE__ */ new Set();
203
+ for (const key of prevKeys) {
204
+ if (!highlightKeys.has(key)) {
205
+ editor.getElementByKey(key)?.classList.remove(blockSelected);
206
+ }
207
+ }
208
+ for (const key of highlightKeys) {
209
+ if (!prevKeys.has(key)) {
210
+ editor.getElementByKey(key)?.classList.add(blockSelected);
211
+ }
212
+ }
213
+ prevKeys = highlightKeys;
214
+ });
215
+ return () => {
216
+ unregister();
217
+ for (const key of prevKeys) {
218
+ editor.getElementByKey(key)?.classList.remove(blockSelected);
219
+ }
220
+ };
221
+ }, [clearBlockSelectionState, editor]);
222
+ useEffect(() => {
223
+ const rootEl = editor.getRootElement();
224
+ if (!rootEl) return;
225
+ const onFocusIn = (e) => {
226
+ if (blockSelectionKeysRef.current.size === 0) return;
227
+ const target = e.target;
228
+ if (!target) return;
229
+ const nestedEditable = target.closest('[contenteditable="true"]');
230
+ if (nestedEditable && nestedEditable !== rootEl) {
231
+ clearBlockSelectionState();
232
+ editor.update(() => {
233
+ const sel = $getSelection();
234
+ if ($isNodeSelection(sel)) {
235
+ $setSelection(null);
236
+ }
237
+ });
238
+ }
239
+ };
240
+ rootEl.addEventListener("focusin", onFocusIn);
241
+ return () => rootEl.removeEventListener("focusin", onFocusIn);
242
+ }, [clearBlockSelectionState, editor]);
243
+ useEffect(() => {
244
+ const unregShiftDown = editor.registerCommand(
245
+ KEY_ARROW_DOWN_COMMAND,
246
+ (event) => {
247
+ if (!event?.shiftKey) return false;
248
+ if (blockSelectionKeysRef.current.size === 0 || !focusKeyRef.current) return false;
249
+ const sel = $getSelection();
250
+ if (!$isNodeSelection(sel)) return false;
251
+ const children = $getRoot().getChildren();
252
+ const focusIdx = children.findIndex((c) => c.getKey() === focusKeyRef.current);
253
+ if (focusIdx === -1 || focusIdx >= children.length - 1) return false;
254
+ event.preventDefault();
255
+ focusKeyRef.current = children[focusIdx + 1].getKey();
256
+ $selectBlockRange(anchorKeyRef.current, focusKeyRef.current);
257
+ const start = Math.min(
258
+ children.findIndex((c) => c.getKey() === anchorKeyRef.current),
259
+ focusIdx + 1
260
+ );
261
+ const end = Math.max(
262
+ children.findIndex((c) => c.getKey() === anchorKeyRef.current),
263
+ focusIdx + 1
264
+ );
265
+ blockSelectionKeysRef.current = new Set(
266
+ children.slice(start, end + 1).map((c) => c.getKey())
267
+ );
268
+ return true;
269
+ },
270
+ COMMAND_PRIORITY_CRITICAL
271
+ );
272
+ const unregShiftUp = editor.registerCommand(
273
+ KEY_ARROW_UP_COMMAND,
274
+ (event) => {
275
+ if (!event?.shiftKey) return false;
276
+ if (blockSelectionKeysRef.current.size === 0 || !focusKeyRef.current) return false;
277
+ const sel = $getSelection();
278
+ if (!$isNodeSelection(sel)) return false;
279
+ const children = $getRoot().getChildren();
280
+ const focusIdx = children.findIndex((c) => c.getKey() === focusKeyRef.current);
281
+ if (focusIdx === -1 || focusIdx <= 0) return false;
282
+ event.preventDefault();
283
+ focusKeyRef.current = children[focusIdx - 1].getKey();
284
+ $selectBlockRange(anchorKeyRef.current, focusKeyRef.current);
285
+ const anchorIdx = children.findIndex((c) => c.getKey() === anchorKeyRef.current);
286
+ const start = Math.min(anchorIdx, focusIdx - 1);
287
+ const end = Math.max(anchorIdx, focusIdx - 1);
288
+ blockSelectionKeysRef.current = new Set(
289
+ children.slice(start, end + 1).map((c) => c.getKey())
290
+ );
291
+ return true;
292
+ },
293
+ COMMAND_PRIORITY_CRITICAL
294
+ );
295
+ const unregSelectAll = editor.registerCommand(
296
+ SELECT_ALL_COMMAND,
297
+ () => {
298
+ if (blockSelectionKeysRef.current.size > 0) {
299
+ const children = $getRoot().getChildren();
300
+ const allKeys = children.map((c) => c.getKey());
301
+ if (blockSelectionKeysRef.current.size >= allKeys.length) {
302
+ return true;
303
+ }
304
+ anchorKeyRef.current = allKeys[0];
305
+ focusKeyRef.current = allKeys.at(-1);
306
+ blockSelectionKeysRef.current = new Set(allKeys);
307
+ const nodeSel = $createNodeSelection();
308
+ for (const key of allKeys) nodeSel.add(key);
309
+ $setSelection(nodeSel);
310
+ return true;
311
+ }
312
+ const sel = $getSelection();
313
+ let topLevelKey = null;
314
+ if ($isRangeSelection(sel)) {
315
+ let node = sel.anchor.getNode();
316
+ while (node.getParent() && node.getParent() !== $getRoot()) {
317
+ node = node.getParent();
318
+ }
319
+ if (node.getParent() === $getRoot()) {
320
+ topLevelKey = node.getKey();
321
+ }
322
+ } else if ($isNodeSelection(sel)) {
323
+ const nodes = sel.getNodes();
324
+ if (nodes.length > 0) {
325
+ let node = nodes[0];
326
+ while (node.getParent() && node.getParent() !== $getRoot()) {
327
+ node = node.getParent();
328
+ }
329
+ if (node.getParent() === $getRoot()) {
330
+ topLevelKey = node.getKey();
331
+ }
332
+ }
333
+ }
334
+ if (topLevelKey) {
335
+ anchorKeyRef.current = topLevelKey;
336
+ focusKeyRef.current = topLevelKey;
337
+ blockSelectionKeysRef.current = /* @__PURE__ */ new Set([topLevelKey]);
338
+ const nodeSel = $createNodeSelection();
339
+ nodeSel.add(topLevelKey);
340
+ $setSelection(nodeSel);
341
+ return true;
342
+ }
343
+ return false;
344
+ },
345
+ COMMAND_PRIORITY_HIGH
346
+ );
347
+ const unregEscape = editor.registerCommand(
348
+ KEY_ESCAPE_COMMAND,
349
+ () => {
350
+ if (blockSelectionKeysRef.current.size === 0) return false;
351
+ clearBlockSelectionState();
352
+ $setSelection(null);
353
+ return true;
354
+ },
355
+ COMMAND_PRIORITY_HIGH
356
+ );
357
+ const unregCopy = editor.registerCommand(
358
+ COPY_COMMAND,
359
+ (event) => {
360
+ if (!event?.clipboardData) return false;
361
+ let handled = false;
362
+ editor.getEditorState().read(() => {
363
+ const nodes = getOwnedSelectionNodes();
364
+ if (nodes.length === 0) return;
365
+ event.preventDefault();
366
+ const clipboardData = buildBlockClipboardData(editor, nodes);
367
+ for (const [mimeType, value] of Object.entries(clipboardData)) {
368
+ event.clipboardData?.setData(mimeType, value);
369
+ }
370
+ handled = true;
371
+ });
372
+ return handled;
373
+ },
374
+ COMMAND_PRIORITY_CRITICAL
375
+ );
376
+ const unregCut = editor.registerCommand(
377
+ CUT_COMMAND,
378
+ (event) => {
379
+ if (!event?.clipboardData) return false;
380
+ let keysToDelete = [];
381
+ editor.getEditorState().read(() => {
382
+ const nodes = getOwnedSelectionNodes();
383
+ if (nodes.length === 0) return;
384
+ event.preventDefault();
385
+ const clipboardData = buildBlockClipboardData(editor, nodes);
386
+ for (const [mimeType, value] of Object.entries(clipboardData)) {
387
+ event.clipboardData?.setData(mimeType, value);
388
+ }
389
+ keysToDelete = nodes.map((node) => node.getKey());
390
+ });
391
+ if (keysToDelete.length === 0) return false;
392
+ deleteBlocksByKeys(keysToDelete);
393
+ return true;
394
+ },
395
+ COMMAND_PRIORITY_CRITICAL
396
+ );
397
+ const handleDeleteBlocks = (payload) => {
398
+ let keysToDelete = [];
399
+ editor.getEditorState().read(() => {
400
+ keysToDelete = getOwnedSelectionNodes().map((node) => node.getKey());
401
+ });
402
+ if (keysToDelete.length === 0) return false;
403
+ if (payload && typeof payload !== "boolean" && "preventDefault" in payload && typeof payload.preventDefault === "function") {
404
+ payload.preventDefault();
405
+ }
406
+ deleteBlocksByKeys(keysToDelete);
407
+ return true;
408
+ };
409
+ const unregBackspace = editor.registerCommand(
410
+ KEY_BACKSPACE_COMMAND,
411
+ handleDeleteBlocks,
412
+ COMMAND_PRIORITY_CRITICAL
413
+ );
414
+ const unregDelete = editor.registerCommand(
415
+ KEY_DELETE_COMMAND,
416
+ handleDeleteBlocks,
417
+ COMMAND_PRIORITY_CRITICAL
418
+ );
419
+ const unregRemoveText = editor.registerCommand(
420
+ REMOVE_TEXT_COMMAND,
421
+ handleDeleteBlocks,
422
+ COMMAND_PRIORITY_CRITICAL
423
+ );
424
+ const unregDeleteCharacter = editor.registerCommand(
425
+ DELETE_CHARACTER_COMMAND,
426
+ handleDeleteBlocks,
427
+ COMMAND_PRIORITY_CRITICAL
428
+ );
429
+ return () => {
430
+ unregShiftDown();
431
+ unregShiftUp();
432
+ unregSelectAll();
433
+ unregEscape();
434
+ unregCopy();
435
+ unregCut();
436
+ unregBackspace();
437
+ unregDelete();
438
+ unregRemoveText();
439
+ unregDeleteCharacter();
440
+ };
441
+ }, [clearBlockSelectionState, deleteBlocksByKeys, editor, getOwnedSelectionNodes]);
442
+ const selectBlock = useCallback(
443
+ (nodeKey, shiftKey) => {
444
+ editor.update(() => {
445
+ if (shiftKey && anchorKeyRef.current) {
446
+ focusKeyRef.current = nodeKey;
447
+ $selectBlockRange(anchorKeyRef.current, nodeKey);
448
+ const children = $getRoot().getChildren();
449
+ const anchorIdx = children.findIndex((c) => c.getKey() === anchorKeyRef.current);
450
+ const focusIdx = children.findIndex((c) => c.getKey() === nodeKey);
451
+ if (anchorIdx !== -1 && focusIdx !== -1) {
452
+ const start = Math.min(anchorIdx, focusIdx);
453
+ const end = Math.max(anchorIdx, focusIdx);
454
+ blockSelectionKeysRef.current = new Set(
455
+ children.slice(start, end + 1).map((c) => c.getKey())
456
+ );
457
+ }
458
+ } else {
459
+ anchorKeyRef.current = nodeKey;
460
+ focusKeyRef.current = nodeKey;
461
+ blockSelectionKeysRef.current = /* @__PURE__ */ new Set([nodeKey]);
462
+ const sel = $createNodeSelection();
463
+ sel.add(nodeKey);
464
+ $setSelection(sel);
465
+ }
466
+ });
467
+ },
468
+ [editor]
469
+ );
470
+ const getSelectedKeys = useCallback(() => {
471
+ if (blockSelectionKeysRef.current.size === 0) return [];
472
+ let keys = [];
473
+ editor.getEditorState().read(() => {
474
+ keys = getTopLevelNodesByKeys(blockSelectionKeysRef.current).map((node) => node.getKey());
475
+ });
476
+ return keys;
477
+ }, [editor, getTopLevelNodesByKeys]);
478
+ const deleteSelectedBlocks = useCallback(
479
+ (fallbackNodeKey) => {
480
+ const selectedKeys = getSelectedKeys();
481
+ const keys = selectedKeys.length > 0 ? selectedKeys : fallbackNodeKey ? [fallbackNodeKey] : [];
482
+ if (keys.length === 0) return;
483
+ deleteBlocksByKeys(keys);
484
+ },
485
+ [deleteBlocksByKeys, getSelectedKeys]
486
+ );
487
+ const isBlockSelectionActive = useCallback(
488
+ () => blockSelectionKeysRef.current.size > 0,
489
+ []
490
+ );
491
+ return { selectBlock, getSelectedKeys, isBlockSelectionActive, deleteSelectedBlocks };
492
+ }
21
493
  const DRAG_DATA_KEY = "application/x-rich-editor-drag";
22
494
  const HIDE_DELAY = 300;
23
495
  const HANDLE_OFFSET = 52;
@@ -123,6 +595,8 @@ function BlockHandleInner({ editor }) {
123
595
  const menuOpenCountRef = useRef(0);
124
596
  const dragPreviewRef = useRef(null);
125
597
  const draggingBlockRef = useRef(null);
598
+ const draggingBlockKeysRef = useRef(null);
599
+ const { selectBlock, getSelectedKeys, deleteSelectedBlocks } = useBlockSelection(editor);
126
600
  const clearHideTimer = useCallback(() => {
127
601
  if (hideTimerRef.current) {
128
602
  clearTimeout(hideTimerRef.current);
@@ -134,7 +608,7 @@ function BlockHandleInner({ editor }) {
134
608
  hideTimerRef.current = setTimeout(() => {
135
609
  if (!hoveringHandleRef.current && menuOpenCountRef.current === 0) {
136
610
  activeBlockRef.current = null;
137
- setHandle((s) => ({ ...s, visible: false, nodeKey: null }));
611
+ setHandle((state) => ({ ...state, visible: false, nodeKey: null }));
138
612
  }
139
613
  }, HIDE_DELAY);
140
614
  }, [clearHideTimer]);
@@ -152,25 +626,25 @@ function BlockHandleInner({ editor }) {
152
626
  if (!open) scheduleHide();
153
627
  else clearHideTimer();
154
628
  },
155
- [scheduleHide, clearHideTimer]
629
+ [clearHideTimer, scheduleHide]
156
630
  );
157
631
  const updatePositionFromBlock = useCallback(
158
632
  (block) => {
159
633
  if (block !== void 0) activeBlockRef.current = block;
160
- const el = activeBlockRef.current;
161
- if (!el || !el.isConnected) {
634
+ const element = activeBlockRef.current;
635
+ if (!element || !element.isConnected) {
162
636
  activeBlockRef.current = null;
163
- setHandle((s) => s.visible ? { ...s, visible: false, nodeKey: null } : s);
637
+ setHandle((state) => state.visible ? { ...state, visible: false, nodeKey: null } : state);
164
638
  return;
165
639
  }
166
640
  const rootElement = editor.getRootElement();
167
641
  if (!rootElement) return;
168
- const blockRect = el.getBoundingClientRect();
642
+ const blockRect = element.getBoundingClientRect();
169
643
  const rootRect = rootElement.getBoundingClientRect();
170
644
  const page = toPagePosition(blockRect);
171
645
  let nodeKey = null;
172
646
  editor.read(() => {
173
- const node = $getNearestNodeFromDOMNode(el);
647
+ const node = $getNearestNodeFromDOMNode(element);
174
648
  if (node) nodeKey = node.getKey();
175
649
  });
176
650
  setHandle({
@@ -186,11 +660,11 @@ function BlockHandleInner({ editor }) {
186
660
  const rootElement = editor.getRootElement();
187
661
  if (!rootElement) return;
188
662
  let rafId = null;
189
- const onMouseMove = (e) => {
663
+ const onMouseMove = (event) => {
190
664
  if (rafId !== null) return;
191
665
  rafId = requestAnimationFrame(() => {
192
666
  rafId = null;
193
- const target = e.target;
667
+ const target = event.target;
194
668
  const block = getBlockElement(editor, target);
195
669
  if (block) {
196
670
  clearHideTimer();
@@ -211,7 +685,7 @@ function BlockHandleInner({ editor }) {
211
685
  rootElement.removeEventListener("mouseleave", onMouseLeave);
212
686
  clearHideTimer();
213
687
  };
214
- }, [editor, clearHideTimer, scheduleHide, updatePositionFromBlock]);
688
+ }, [clearHideTimer, editor, scheduleHide, updatePositionFromBlock]);
215
689
  useEffect(() => {
216
690
  const update = () => updatePositionFromBlock();
217
691
  window.addEventListener("scroll", update, true);
@@ -230,9 +704,9 @@ function BlockHandleInner({ editor }) {
230
704
  editor.update(() => {
231
705
  const node = $getNodeByKey(handle.nodeKey);
232
706
  if (!node) return;
233
- const p = $createParagraphNode();
234
- node.insertAfter(p);
235
- p.selectStart();
707
+ const paragraph = $createParagraphNode();
708
+ node.insertAfter(paragraph);
709
+ paragraph.selectStart();
236
710
  });
237
711
  }, [editor, handle.nodeKey]);
238
712
  const handleTurnInto = useCallback(
@@ -258,8 +732,8 @@ function BlockHandleInner({ editor }) {
258
732
  const node = $getNodeByKey(nodeKey);
259
733
  if (!node || !$isElementNode(node)) return;
260
734
  node.selectStart();
261
- const sel = $getSelection();
262
- if (!$isRangeSelection(sel)) return;
735
+ const selection = $getSelection();
736
+ if (!$isRangeSelection(selection)) return;
263
737
  const creators = {
264
738
  paragraph: () => $createParagraphNode(),
265
739
  h1: () => $createHeadingNode("h1"),
@@ -269,52 +743,69 @@ function BlockHandleInner({ editor }) {
269
743
  code: () => $createCodeNode()
270
744
  };
271
745
  const create = creators[type];
272
- if (create) $setBlocksType(sel, create);
746
+ if (create) $setBlocksType(selection, create);
273
747
  });
274
748
  },
275
- [editor, handle.nodeKey]
749
+ [editor, handle]
276
750
  );
277
751
  const handleDelete = useCallback(() => {
278
- if (!handle.nodeKey) return;
279
- editor.update(() => {
280
- const node = $getNodeByKey(handle.nodeKey);
281
- node?.remove();
282
- });
283
- setHandle((s) => ({ ...s, visible: false, nodeKey: null }));
284
- }, [editor, handle.nodeKey]);
752
+ deleteSelectedBlocks(handle.nodeKey);
753
+ setHandle((state) => ({ ...state, visible: false, nodeKey: null }));
754
+ }, [deleteSelectedBlocks, handle.nodeKey]);
285
755
  const handleDuplicate = useCallback(() => {
286
- if (!handle.nodeKey) return;
756
+ const selectedKeys = getSelectedKeys();
757
+ const keys = selectedKeys.length > 0 ? selectedKeys : handle.nodeKey ? [handle.nodeKey] : [];
758
+ if (!keys.length) return;
287
759
  editor.update(() => {
288
- const node = $getNodeByKey(handle.nodeKey);
289
- if (!node) return;
290
- const clone = $cloneNode(node);
291
- node.insertAfter(clone);
760
+ const root = $getRoot();
761
+ const children = root.getChildren();
762
+ const keySet = new Set(keys);
763
+ const nodesToDuplicate = children.filter((child) => keySet.has(child.getKey()));
764
+ if (!nodesToDuplicate.length) return;
765
+ let insertAfter = nodesToDuplicate.at(-1);
766
+ for (const node of nodesToDuplicate) {
767
+ const clone = $cloneNode(node);
768
+ insertAfter.insertAfter(clone);
769
+ insertAfter = clone;
770
+ }
292
771
  });
293
- }, [editor, handle.nodeKey]);
772
+ }, [editor, getSelectedKeys, handle.nodeKey]);
294
773
  const handleMoveUp = useCallback(() => {
295
- if (!handle.nodeKey) return;
774
+ const selectedKeys = getSelectedKeys();
775
+ const keys = selectedKeys.length > 0 ? selectedKeys : handle.nodeKey ? [handle.nodeKey] : [];
776
+ if (!keys.length) return;
296
777
  editor.update(() => {
297
- const node = $getNodeByKey(handle.nodeKey);
298
- if (!node) return;
299
- const prev = node.getPreviousSibling();
300
- if (prev) {
301
- node.remove();
302
- prev.insertBefore(node);
303
- }
778
+ const root = $getRoot();
779
+ const children = root.getChildren();
780
+ const keySet = new Set(keys);
781
+ const selectedNodes = children.filter((child) => keySet.has(child.getKey()));
782
+ if (!selectedNodes.length) return;
783
+ const firstSelected = selectedNodes[0];
784
+ const previousSibling = firstSelected.getPreviousSibling();
785
+ if (!previousSibling || keySet.has(previousSibling.getKey())) return;
786
+ const lastSelected = selectedNodes.at(-1);
787
+ previousSibling.remove();
788
+ lastSelected.insertAfter(previousSibling);
304
789
  });
305
- }, [editor, handle.nodeKey]);
790
+ }, [editor, getSelectedKeys, handle.nodeKey]);
306
791
  const handleMoveDown = useCallback(() => {
307
- if (!handle.nodeKey) return;
792
+ const selectedKeys = getSelectedKeys();
793
+ const keys = selectedKeys.length > 0 ? selectedKeys : handle.nodeKey ? [handle.nodeKey] : [];
794
+ if (!keys.length) return;
308
795
  editor.update(() => {
309
- const node = $getNodeByKey(handle.nodeKey);
310
- if (!node) return;
311
- const next = node.getNextSibling();
312
- if (next) {
313
- node.remove();
314
- next.insertAfter(node);
315
- }
796
+ const root = $getRoot();
797
+ const children = root.getChildren();
798
+ const keySet = new Set(keys);
799
+ const selectedNodes = children.filter((child) => keySet.has(child.getKey()));
800
+ if (!selectedNodes.length) return;
801
+ const lastSelected = selectedNodes.at(-1);
802
+ const nextSibling = lastSelected.getNextSibling();
803
+ if (!nextSibling || keySet.has(nextSibling.getKey())) return;
804
+ const firstSelected = selectedNodes[0];
805
+ nextSibling.remove();
806
+ firstSelected.insertBefore(nextSibling);
316
807
  });
317
- }, [editor, handle.nodeKey]);
808
+ }, [editor, getSelectedKeys, handle.nodeKey]);
318
809
  const [gripMenuOpen, setGripMenuOpen] = useState(false);
319
810
  const dragStartedRef = useRef(false);
320
811
  const clearDragVisualState = useCallback(() => {
@@ -328,13 +819,22 @@ function BlockHandleInner({ editor }) {
328
819
  draggingBlock$1.classList.remove(draggingBlock);
329
820
  draggingBlockRef.current = null;
330
821
  }
331
- }, []);
822
+ const draggingKeys = draggingBlockKeysRef.current;
823
+ if (draggingKeys) {
824
+ for (const key of draggingKeys) {
825
+ editor.getElementByKey(key)?.classList.remove(draggingBlock);
826
+ }
827
+ draggingBlockKeysRef.current = null;
828
+ }
829
+ }, [editor]);
332
830
  const onGripDragStart = useCallback(
333
- (e) => {
831
+ (event) => {
334
832
  dragStartedRef.current = true;
335
- if (!e.dataTransfer || !handle.nodeKey) return;
336
- e.dataTransfer.setData(DRAG_DATA_KEY, handle.nodeKey);
337
- e.dataTransfer.effectAllowed = "move";
833
+ if (!event.dataTransfer || !handle.nodeKey) return;
834
+ const selectedKeys = getSelectedKeys();
835
+ const dragKeys = selectedKeys.length > 0 && selectedKeys.includes(handle.nodeKey) ? selectedKeys : [handle.nodeKey];
836
+ event.dataTransfer.setData(DRAG_DATA_KEY, JSON.stringify(dragKeys));
837
+ event.dataTransfer.effectAllowed = "move";
338
838
  const block = activeBlockRef.current;
339
839
  if (!block) return;
340
840
  clearDragVisualState();
@@ -342,6 +842,13 @@ function BlockHandleInner({ editor }) {
342
842
  const preview = block.cloneNode(true);
343
843
  preview.classList.add(dragPreview);
344
844
  preview.style.width = `${rect.width}px`;
845
+ preview.style.position = "relative";
846
+ if (dragKeys.length > 1) {
847
+ const badge = document.createElement("div");
848
+ badge.className = dragCountBadge;
849
+ badge.textContent = String(dragKeys.length);
850
+ preview.appendChild(badge);
851
+ }
345
852
  if (portalClassName) {
346
853
  const wrapper = document.createElement("div");
347
854
  wrapper.className = portalClassName;
@@ -354,40 +861,61 @@ function BlockHandleInner({ editor }) {
354
861
  document.body.append(preview);
355
862
  dragPreviewRef.current = preview;
356
863
  }
357
- draggingBlockRef.current = block;
358
- block.classList.add(draggingBlock);
359
- const offsetX = Math.max(12, Math.min(rect.width - 12, e.clientX - rect.left));
360
- const offsetY = Math.max(8, Math.min(rect.height - 8, e.clientY - rect.top));
361
- e.dataTransfer.setDragImage(preview, offsetX, offsetY);
864
+ if (dragKeys.length > 1) {
865
+ for (const key of dragKeys) {
866
+ editor.getElementByKey(key)?.classList.add(draggingBlock);
867
+ }
868
+ draggingBlockKeysRef.current = dragKeys;
869
+ } else {
870
+ draggingBlockRef.current = block;
871
+ block.classList.add(draggingBlock);
872
+ }
873
+ const offsetX = Math.max(12, Math.min(rect.width - 12, event.clientX - rect.left));
874
+ const offsetY = Math.max(8, Math.min(rect.height - 8, event.clientY - rect.top));
875
+ event.dataTransfer.setDragImage(preview, offsetX, offsetY);
362
876
  },
363
- [clearDragVisualState, handle.nodeKey, portalClassName, theme]
877
+ [clearDragVisualState, editor, getSelectedKeys, handle.nodeKey, portalClassName, theme]
364
878
  );
365
879
  const onGripOpenChange = useCallback(
366
880
  (open) => {
367
- setGripMenuOpen((prev) => {
368
- if (prev === open) return prev;
881
+ setGripMenuOpen((previous) => {
882
+ if (previous === open) return previous;
369
883
  onMenuOpenChange(open);
370
884
  return open;
371
885
  });
372
886
  },
373
887
  [onMenuOpenChange]
374
888
  );
375
- const onGripMouseDownCapture = useCallback((e) => {
889
+ const onGripMouseDownCapture = useCallback((event) => {
376
890
  dragStartedRef.current = false;
377
- if (e.button === 0) e.stopPropagation();
891
+ if (event.button === 0) event.stopPropagation();
378
892
  }, []);
379
893
  const onGripClick = useCallback(
380
- (e) => {
381
- if (e.detail === 0) return;
382
- e.preventDefault();
383
- e.stopPropagation();
894
+ (event) => {
895
+ if (event.detail === 0) return;
896
+ event.preventDefault();
897
+ event.stopPropagation();
384
898
  if (dragStartedRef.current) {
385
899
  dragStartedRef.current = false;
386
900
  return;
387
901
  }
388
- onGripOpenChange(!gripMenuOpen);
902
+ if (!handle.nodeKey) return;
903
+ selectBlock(handle.nodeKey, event.shiftKey);
389
904
  },
390
- [gripMenuOpen, onGripOpenChange]
905
+ [handle.nodeKey, selectBlock]
906
+ );
907
+ const onGripContextMenu = useCallback(
908
+ (event) => {
909
+ event.preventDefault();
910
+ if (handle.nodeKey) {
911
+ const currentKeys = getSelectedKeys();
912
+ if (!currentKeys.includes(handle.nodeKey)) {
913
+ selectBlock(handle.nodeKey, false);
914
+ }
915
+ }
916
+ onGripOpenChange(true);
917
+ },
918
+ [getSelectedKeys, handle.nodeKey, onGripOpenChange, selectBlock]
391
919
  );
392
920
  useEffect(() => {
393
921
  const rootElement = editor.getRootElement();
@@ -400,7 +928,7 @@ function BlockHandleInner({ editor }) {
400
928
  event.dataTransfer.dropEffect = "move";
401
929
  const block = getDropTargetBlock(editor, rootElement, event);
402
930
  if (!block) {
403
- setDropLine((s) => s.visible ? { ...s, visible: false } : s);
931
+ setDropLine((state) => state.visible ? { ...state, visible: false } : state);
404
932
  return true;
405
933
  }
406
934
  const rect = block.getBoundingClientRect();
@@ -420,24 +948,46 @@ function BlockHandleInner({ editor }) {
420
948
  const unregDrop = editor.registerCommand(
421
949
  DROP_COMMAND,
422
950
  (event) => {
423
- const draggedKey = event.dataTransfer?.getData(DRAG_DATA_KEY);
424
- if (!draggedKey) return false;
951
+ const raw = event.dataTransfer?.getData(DRAG_DATA_KEY);
952
+ if (!raw) return false;
425
953
  event.preventDefault();
426
- setDropLine((s) => ({ ...s, visible: false }));
954
+ setDropLine((state) => ({ ...state, visible: false }));
427
955
  clearDragVisualState();
956
+ let draggedKeys;
957
+ try {
958
+ const parsed = JSON.parse(raw);
959
+ draggedKeys = Array.isArray(parsed) && parsed.every((key) => typeof key === "string") ? parsed : [raw];
960
+ } catch {
961
+ draggedKeys = [raw];
962
+ }
963
+ if (!draggedKeys.length) return false;
428
964
  const block = getDropTargetBlock(editor, rootElement, event);
429
965
  if (!block) return false;
430
966
  editor.update(() => {
431
- const draggedNode = $getNodeByKey(draggedKey);
432
967
  const targetNode = $getNearestNodeFromDOMNode(block);
433
- if (!draggedNode || !targetNode || draggedNode === targetNode) return;
968
+ if (!targetNode) return;
969
+ if (draggedKeys.includes(targetNode.getKey())) return;
434
970
  const rect = block.getBoundingClientRect();
435
- const midY = rect.top + rect.height / 2;
436
- draggedNode.remove();
437
- if (event.clientY < midY) {
438
- targetNode.insertBefore(draggedNode);
971
+ const insertBefore = event.clientY < rect.top + rect.height / 2;
972
+ const root = $getRoot();
973
+ const children = root.getChildren();
974
+ const keySet = new Set(draggedKeys);
975
+ const draggedNodes = children.filter((child) => keySet.has(child.getKey()));
976
+ for (const node of draggedNodes) {
977
+ node.remove();
978
+ }
979
+ const freshTarget = $getNodeByKey(targetNode.getKey());
980
+ if (!freshTarget) return;
981
+ if (insertBefore) {
982
+ for (let i = draggedNodes.length - 1; i >= 0; i--) {
983
+ freshTarget.insertBefore(draggedNodes[i]);
984
+ }
439
985
  } else {
440
- targetNode.insertAfter(draggedNode);
986
+ let cursor = freshTarget;
987
+ for (const node of draggedNodes) {
988
+ cursor.insertAfter(node);
989
+ cursor = node;
990
+ }
441
991
  }
442
992
  });
443
993
  return true;
@@ -453,14 +1003,14 @@ function BlockHandleInner({ editor }) {
453
1003
  COMMAND_PRIORITY_HIGH
454
1004
  );
455
1005
  const clearDropLine = () => {
456
- setDropLine((s) => s.visible ? { ...s, visible: false } : s);
1006
+ setDropLine((state) => state.visible ? { ...state, visible: false } : state);
457
1007
  };
458
1008
  const clearDragState = () => {
459
1009
  clearDropLine();
460
1010
  clearDragVisualState();
461
1011
  };
462
- const onDragLeave = (e) => {
463
- if (e.relatedTarget === null || !rootElement.contains(e.relatedTarget)) {
1012
+ const onDragLeave = (event) => {
1013
+ if (event.relatedTarget === null || !rootElement.contains(event.relatedTarget)) {
464
1014
  clearDropLine();
465
1015
  }
466
1016
  };
@@ -477,6 +1027,54 @@ function BlockHandleInner({ editor }) {
477
1027
  clearDragState();
478
1028
  };
479
1029
  }, [clearDragVisualState, editor]);
1030
+ useEffect(() => {
1031
+ const rootElement = editor.getRootElement();
1032
+ if (!rootElement) return;
1033
+ const outerContainer = rootElement.closest(".rich-editor");
1034
+ if (!outerContainer) return;
1035
+ let isDragging = false;
1036
+ let lastKey = null;
1037
+ const getBlockKeyAtY = (clientY) => {
1038
+ const block = getNearestBlockByY(rootElement, clientY);
1039
+ if (!block) return null;
1040
+ let nodeKey = null;
1041
+ editor.read(() => {
1042
+ nodeKey = $getNearestNodeFromDOMNode(block)?.getKey() ?? null;
1043
+ });
1044
+ return nodeKey;
1045
+ };
1046
+ const onMouseDown = (event) => {
1047
+ if (event.button !== 0) return;
1048
+ const target = event.target;
1049
+ if (rootElement.contains(target)) return;
1050
+ const nodeKey = getBlockKeyAtY(event.clientY);
1051
+ if (!nodeKey) return;
1052
+ event.preventDefault();
1053
+ selectBlock(nodeKey, event.shiftKey);
1054
+ editor.focus();
1055
+ isDragging = true;
1056
+ lastKey = nodeKey;
1057
+ };
1058
+ const onMouseMove = (event) => {
1059
+ if (!isDragging) return;
1060
+ const nodeKey = getBlockKeyAtY(event.clientY);
1061
+ if (!nodeKey || nodeKey === lastKey) return;
1062
+ selectBlock(nodeKey, true);
1063
+ lastKey = nodeKey;
1064
+ };
1065
+ const onMouseUp = () => {
1066
+ isDragging = false;
1067
+ lastKey = null;
1068
+ };
1069
+ outerContainer.addEventListener("mousedown", onMouseDown);
1070
+ window.addEventListener("mousemove", onMouseMove);
1071
+ window.addEventListener("mouseup", onMouseUp);
1072
+ return () => {
1073
+ outerContainer.removeEventListener("mousedown", onMouseDown);
1074
+ window.removeEventListener("mousemove", onMouseMove);
1075
+ window.removeEventListener("mouseup", onMouseUp);
1076
+ };
1077
+ }, [editor, selectBlock]);
480
1078
  const themeWrapperProps = portalClassName ? {
481
1079
  "className": portalClassName,
482
1080
  "data-theme": theme,
@@ -500,6 +1098,7 @@ function BlockHandleInner({ editor }) {
500
1098
  "aria-label": "Block actions",
501
1099
  className: handleBtn,
502
1100
  onClick: onGripClick,
1101
+ onContextMenu: onGripContextMenu,
503
1102
  onDragStart: onGripDragStart,
504
1103
  onMouseDownCapture: onGripMouseDownCapture,
505
1104
  children: /* @__PURE__ */ jsx(GripVertical, { size: 14 })
@@ -1 +1 @@
1
- :root{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #2563eb;--rc-quote-bg: #f5f5f5;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}:root.dark{--rc-text: #fafafa;--rc-text-secondary: #a3a3a3;--rc-text-tertiary: #737373;--rc-text-quaternary: #525252;--rc-bg: #0a0a0a;--rc-bg-secondary: #171717;--rc-bg-tertiary: #262626;--rc-fill: #2a2a2a;--rc-fill-secondary: #222222;--rc-fill-tertiary: #1a1a1a;--rc-fill-quaternary: #141414;--rc-border: #262626;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #d4d4d4;--rc-code-bg: #262626;--rc-hr-border: #262626;--rc-quote-border: #60a5fa;--rc-quote-bg: #262626;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}[data-theme=dark]{--rc-text: #fafafa;--rc-text-secondary: #a3a3a3;--rc-text-tertiary: #737373;--rc-text-quaternary: #525252;--rc-bg: #0a0a0a;--rc-bg-secondary: #171717;--rc-bg-tertiary: #262626;--rc-fill: #2a2a2a;--rc-fill-secondary: #222222;--rc-fill-tertiary: #1a1a1a;--rc-fill-quaternary: #141414;--rc-border: #262626;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #d4d4d4;--rc-code-bg: #262626;--rc-hr-border: #262626;--rc-quote-border: #60a5fa;--rc-quote-bg: #262626;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1bfi5uc0{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #2563eb;--rc-quote-bg: #f5f5f5;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1bfi5uc1{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #2563eb;--rc-quote-bg: #f5f5f5;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.8;--rc-line-height-tight: 1.4;--rc-font-family: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1bfi5uc2{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #a3a3a3;--rc-quote-bg: #fafafa;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: none;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 2px;--rc-space-sm: 4px;--rc-space-md: 10px;--rc-space-lg: 16px;--rc-space-xl: 20px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 14px;--rc-font-size-small: 12px;--rc-line-height: 1.5;--rc-line-height-tight: 1.3;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 3px;--rc-radius-md: 6px;--rc-radius-lg: 12px}.dark ._1bfi5uc0,[data-theme=dark] ._1bfi5uc0,.dark._1bfi5uc0,[data-theme=dark]._1bfi5uc0,.dark ._1bfi5uc1,[data-theme=dark] ._1bfi5uc1,.dark._1bfi5uc1,[data-theme=dark]._1bfi5uc1,.dark ._1bfi5uc2,[data-theme=dark] ._1bfi5uc2,.dark._1bfi5uc2,[data-theme=dark]._1bfi5uc2{--rc-text: #fafafa;--rc-text-secondary: #a3a3a3;--rc-text-tertiary: #737373;--rc-text-quaternary: #525252;--rc-bg: #0a0a0a;--rc-bg-secondary: #171717;--rc-bg-tertiary: #262626;--rc-fill: #2a2a2a;--rc-fill-secondary: #222222;--rc-fill-tertiary: #1a1a1a;--rc-fill-quaternary: #141414;--rc-border: #262626;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #d4d4d4;--rc-code-bg: #262626;--rc-hr-border: #262626;--rc-quote-border: #60a5fa;--rc-quote-bg: #262626;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4)}.iihqkc0{display:flex;align-items:center;gap:1px;position:absolute;z-index:30;opacity:0;pointer-events:none;transition:opacity .15s}.iihqkc1{opacity:1;pointer-events:auto}.iihqkc2{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:4px;border:none;background:transparent;color:color-mix(in srgb,var(--rc-text) 35%,transparent);cursor:pointer;padding:0;transition:background-color .15s,color .15s}.iihqkc2:hover{background:color-mix(in srgb,var(--rc-text) 8%,transparent);color:color-mix(in srgb,var(--rc-text) 70%,transparent)}.iihqkc3{opacity:.35}.iihqkc4{position:fixed;top:-10000px;left:-10000px;z-index:40;pointer-events:none;margin:0;box-sizing:border-box;opacity:.9;background-color:var(--rc-bg);border:1px solid var(--rc-border);border-radius:var(--rc-radius-sm);box-shadow:var(--rc-shadow-top-bar)}.iihqkc5{position:absolute;height:2px;background:var(--rc-accent);border-radius:1px;z-index:30;pointer-events:none}.iihqkc6{color:var(--rc-alert-caution)}.iihqkc6[data-highlighted]{color:var(--rc-alert-caution);background-color:color-mix(in srgb,var(--rc-alert-caution) 8%,transparent)}.iihqkc6 svg{color:var(--rc-alert-caution)}.iihqkc6[data-highlighted] svg{color:var(--rc-alert-caution)}
1
+ :root{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #2563eb;--rc-quote-bg: #f5f5f5;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}:root.dark{--rc-text: #fafafa;--rc-text-secondary: #a3a3a3;--rc-text-tertiary: #737373;--rc-text-quaternary: #525252;--rc-bg: #0a0a0a;--rc-bg-secondary: #171717;--rc-bg-tertiary: #262626;--rc-fill: #2a2a2a;--rc-fill-secondary: #222222;--rc-fill-tertiary: #1a1a1a;--rc-fill-quaternary: #141414;--rc-border: #262626;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #d4d4d4;--rc-code-bg: #262626;--rc-hr-border: #262626;--rc-quote-border: #60a5fa;--rc-quote-bg: #262626;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}[data-theme=dark]{--rc-text: #fafafa;--rc-text-secondary: #a3a3a3;--rc-text-tertiary: #737373;--rc-text-quaternary: #525252;--rc-bg: #0a0a0a;--rc-bg-secondary: #171717;--rc-bg-tertiary: #262626;--rc-fill: #2a2a2a;--rc-fill-secondary: #222222;--rc-fill-tertiary: #1a1a1a;--rc-fill-quaternary: #141414;--rc-border: #262626;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #d4d4d4;--rc-code-bg: #262626;--rc-hr-border: #262626;--rc-quote-border: #60a5fa;--rc-quote-bg: #262626;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1bfi5uc0{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #2563eb;--rc-quote-bg: #f5f5f5;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1bfi5uc1{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #2563eb;--rc-quote-bg: #f5f5f5;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.8;--rc-line-height-tight: 1.4;--rc-font-family: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1bfi5uc2{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #a3a3a3;--rc-quote-bg: #fafafa;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: none;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 2px;--rc-space-sm: 4px;--rc-space-md: 10px;--rc-space-lg: 16px;--rc-space-xl: 20px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 14px;--rc-font-size-small: 12px;--rc-line-height: 1.5;--rc-line-height-tight: 1.3;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 3px;--rc-radius-md: 6px;--rc-radius-lg: 12px}.dark ._1bfi5uc0,[data-theme=dark] ._1bfi5uc0,.dark._1bfi5uc0,[data-theme=dark]._1bfi5uc0,.dark ._1bfi5uc1,[data-theme=dark] ._1bfi5uc1,.dark._1bfi5uc1,[data-theme=dark]._1bfi5uc1,.dark ._1bfi5uc2,[data-theme=dark] ._1bfi5uc2,.dark._1bfi5uc2,[data-theme=dark]._1bfi5uc2{--rc-text: #fafafa;--rc-text-secondary: #a3a3a3;--rc-text-tertiary: #737373;--rc-text-quaternary: #525252;--rc-bg: #0a0a0a;--rc-bg-secondary: #171717;--rc-bg-tertiary: #262626;--rc-fill: #2a2a2a;--rc-fill-secondary: #222222;--rc-fill-tertiary: #1a1a1a;--rc-fill-quaternary: #141414;--rc-border: #262626;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #d4d4d4;--rc-code-bg: #262626;--rc-hr-border: #262626;--rc-quote-border: #60a5fa;--rc-quote-bg: #262626;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4)}.iihqkc0{display:flex;align-items:center;gap:1px;position:absolute;z-index:30;opacity:0;pointer-events:none;transition:opacity .15s}.iihqkc1{opacity:1;pointer-events:auto}.iihqkc2{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:4px;border:none;background:transparent;color:color-mix(in srgb,var(--rc-text) 35%,transparent);cursor:pointer;padding:0;transition:background-color .15s,color .15s}.iihqkc2:hover{background:color-mix(in srgb,var(--rc-text) 8%,transparent);color:color-mix(in srgb,var(--rc-text) 70%,transparent)}.iihqkc3{opacity:.35}.iihqkc4{position:fixed;top:-10000px;left:-10000px;z-index:40;pointer-events:none;margin:0;box-sizing:border-box;opacity:.9;background-color:var(--rc-bg);border:1px solid var(--rc-border);border-radius:var(--rc-radius-sm);box-shadow:var(--rc-shadow-top-bar)}.iihqkc5{position:absolute;height:2px;background:var(--rc-accent);border-radius:1px;z-index:30;pointer-events:none}.iihqkc6{box-shadow:0 0 0 2px color-mix(in srgb,var(--rc-accent) 30%,transparent);background-color:color-mix(in srgb,var(--rc-accent) 6%,transparent);border-radius:var(--rc-radius-sm);transition:box-shadow .15s ease,background-color .15s ease}.iihqkc7{position:absolute;top:-8px;right:-8px;min-width:20px;height:20px;border-radius:10px;background-color:var(--rc-accent);color:#fff;font-size:12px;font-weight:600;display:flex;align-items:center;justify-content:center;padding:0 6px;box-shadow:0 2px 4px #00000026}.iihqkc8{color:var(--rc-alert-caution)}.iihqkc8[data-highlighted]{color:var(--rc-alert-caution);background-color:color-mix(in srgb,var(--rc-alert-caution) 8%,transparent)}.iihqkc8 svg{color:var(--rc-alert-caution)}.iihqkc8[data-highlighted] svg{color:var(--rc-alert-caution)}
@@ -4,5 +4,7 @@ export declare const handleBtn: string;
4
4
  export declare const draggingBlock: string;
5
5
  export declare const dragPreview: string;
6
6
  export declare const dropIndicator: string;
7
+ export declare const blockSelected: string;
8
+ export declare const dragCountBadge: string;
7
9
  export declare const menuItemDestructive: string;
8
10
  //# sourceMappingURL=styles.css.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"styles.css.d.ts","sourceRoot":"","sources":["../src/styles.css.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,QAS1B,CAAA;AAEF,eAAO,MAAM,sBAAsB,QAGjC,CAAA;AAGF,eAAO,MAAM,SAAS,QAiBpB,CAAA;AAGF,eAAO,MAAM,aAAa,QAExB,CAAA;AAEF,eAAO,MAAM,WAAW,QAatB,CAAA;AAGF,eAAO,MAAM,aAAa,QAOxB,CAAA;AAGF,eAAO,MAAM,mBAAmB,QAQ9B,CAAA"}
1
+ {"version":3,"file":"styles.css.d.ts","sourceRoot":"","sources":["../src/styles.css.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,QAS1B,CAAC;AAEH,eAAO,MAAM,sBAAsB,QAGjC,CAAC;AAGH,eAAO,MAAM,SAAS,QAiBpB,CAAC;AAGH,eAAO,MAAM,aAAa,QAExB,CAAC;AAEH,eAAO,MAAM,WAAW,QAatB,CAAC;AAGH,eAAO,MAAM,aAAa,QAOxB,CAAC;AAGH,eAAO,MAAM,aAAa,QAKxB,CAAC;AAEH,eAAO,MAAM,cAAc,QAgBzB,CAAC;AAGH,eAAO,MAAM,mBAAmB,QAQ9B,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { LexicalEditor } from 'lexical';
2
+ export declare function useBlockSelection(editor: LexicalEditor): {
3
+ selectBlock: (nodeKey: string, shiftKey: boolean) => void;
4
+ getSelectedKeys: () => string[];
5
+ isBlockSelectionActive: () => boolean;
6
+ deleteSelectedBlocks: (fallbackNodeKey?: string | null) => void;
7
+ };
8
+ //# sourceMappingURL=useBlockSelection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBlockSelection.d.ts","sourceRoot":"","sources":["../src/useBlockSelection.ts"],"names":[],"mappings":"AAAA,OAAO,EAiBL,KAAK,aAAa,EAInB,MAAM,SAAS,CAAC;AA8BjB,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa;2BA4ZzC,MAAM,YAAY,OAAO;2BA6BG,MAAM,EAAE;kCAwB1C,OAAO;6CAZQ,MAAM,GAAG,IAAI;EAiBnC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haklex/rich-plugin-block-handle",
3
- "version": "0.0.101",
3
+ "version": "0.0.103",
4
4
  "description": "Block handle plugin with add button and context menu",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,10 +22,11 @@
22
22
  ],
23
23
  "dependencies": {
24
24
  "@lexical/code-core": "^0.42.0",
25
- "@haklex/rich-editor-ui": "0.0.101",
26
- "@haklex/rich-style-token": "0.0.101"
25
+ "@haklex/rich-style-token": "0.0.103",
26
+ "@haklex/rich-editor-ui": "0.0.103"
27
27
  },
28
28
  "devDependencies": {
29
+ "@lexical/html": "^0.42.0",
29
30
  "@lexical/list": "^0.42.0",
30
31
  "@lexical/react": "^0.42.0",
31
32
  "@lexical/rich-text": "^0.42.0",
@@ -43,6 +44,7 @@
43
44
  "vite-plugin-dts": "^4.5.4"
44
45
  },
45
46
  "peerDependencies": {
47
+ "@lexical/html": "^0.42.0",
46
48
  "@lexical/list": "^0.42.0",
47
49
  "@lexical/react": "^0.42.0",
48
50
  "@lexical/rich-text": "^0.42.0",