@lobehub/editor 4.16.0 → 4.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { IDocumentOptions, IEditor, IPlugin } from "../types/kernel.js";
2
- import { LITEXML_MODIFY_COMMAND } from "../plugins/litexml/command/index.js";
2
+ import { LITEXML_MODIFY_COMMAND } from "../plugins/litexml/command/symbols.js";
3
3
  import { FileListItem, ImageListItem, MediaLists, extractMediaFromEditorState } from "./extract-media-from-editor-state.js";
4
4
  import { CommandPayloadType, SerializedEditorState, SerializedLexicalNode } from "lexical";
5
5
 
@@ -1,6 +1,6 @@
1
1
  import { moment } from "../editor-kernel/utils.js";
2
2
  import Editor from "../editor-kernel/index.js";
3
- import { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "../plugins/litexml/command/index.js";
3
+ import { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "../plugins/litexml/command/symbols.js";
4
4
  import { MarkdownPlugin } from "../plugins/markdown/plugin/index.js";
5
5
  import { LitexmlPlugin } from "../plugins/litexml/plugin/index.js";
6
6
  import { CommonPlugin } from "../plugins/common/plugin/index.js";
package/es/headless.d.ts CHANGED
@@ -1,5 +1,5 @@
1
+ import { o as LITEXML_MODIFY_COMMAND } from "./symbols-DEEvsKq4.js";
1
2
  import { HistoryState, HistoryStateEntry } from "@lexical/history";
2
- import * as _$lexical from "lexical";
3
3
  import { CommandListener, CommandListenerPriority, CommandPayloadType, EditorState, ElementNode, LexicalCommand, LexicalEditor, LexicalNode, LexicalNodeConfig, SerializedEditorState, SerializedLexicalNode } from "lexical";
4
4
 
5
5
  //#region src/editor-kernel/data-source.d.ts
@@ -473,23 +473,6 @@ interface IEditorPluginConstructor<IConfig> {
473
473
  }
474
474
  type IPlugin<T = any> = IEditorPluginConstructor<T> | [IEditorPluginConstructor<T>, T?];
475
475
  //#endregion
476
- //#region src/plugins/litexml/command/index.d.ts
477
- declare const LITEXML_MODIFY_COMMAND: _$lexical.LexicalCommand<({
478
- action: "insert";
479
- beforeId: string;
480
- litexml: string;
481
- } | {
482
- action: "insert";
483
- afterId: string;
484
- litexml: string;
485
- } | {
486
- action: "remove";
487
- id: string;
488
- } | {
489
- action: "modify";
490
- litexml: string | string[];
491
- })[]>;
492
- //#endregion
493
476
  //#region src/headless/extract-media-from-editor-state.d.ts
494
477
  interface ImageListItem {
495
478
  alt: string;
package/es/headless.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "./plugins/litexml/command/symbols.js";
1
2
  import { createHeadlessEditor as createHeadlessEditor$1 } from "@lexical/headless";
2
3
  import { createEmptyHistoryState, registerHistory } from "@lexical/history";
3
4
  import { $computeTableMapSkipCellCheck, $createTableNodeWithDimensions, $createTableSelection, $deleteTableColumnAtSelection, $deleteTableRowAtSelection, $findTableNode, $insertTableColumnAtSelection, $insertTableRowAtSelection, $isSimpleTable, $isTableCellNode, $isTableNode, $isTableRowNode, $isTableSelection, TableCellNode, TableNode, TableNode as TableNode$1, TableRowNode, registerTableCellUnmergeTransform, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive } from "@lexical/table";
@@ -1847,10 +1848,6 @@ function wrapBlockModify(oldBlock, editor, changeFn) {
1847
1848
  diffNode.append($cloneNode(newBlock, editor));
1848
1849
  newBlock.replace(diffNode, false);
1849
1850
  }
1850
- const LITEXML_MODIFY_COMMAND = createCommand("LITEXML_MODIFY_COMMAND");
1851
- const LITEXML_APPLY_COMMAND = createCommand("LITEXML_APPLY_COMMAND");
1852
- const LITEXML_REMOVE_COMMAND = createCommand("LITEXML_REMOVE_COMMAND");
1853
- const LITEXML_INSERT_COMMAND = createCommand("LITEXML_INSERT_COMMAND");
1854
1851
  function registerLiteXMLCommand(editor, dataSource) {
1855
1852
  return mergeRegister(editor.registerCommand(LITEXML_MODIFY_COMMAND, (payload) => {
1856
1853
  const resultPayload = payload.reduce((acc, cur) => {
@@ -2077,8 +2074,6 @@ function handleInsert(editor, payload, dataSource) {
2077
2074
  }
2078
2075
  //#endregion
2079
2076
  //#region src/plugins/litexml/command/diffCommand.ts
2080
- const LITEXML_DIFFNODE_COMMAND = createCommand("LITEXML_DIFFNODE_COMMAND");
2081
- const LITEXML_DIFFNODE_ALL_COMMAND = createCommand("LITEXML_DIFFNODE_ALL_COMMAND");
2082
2077
  function doAction(node, action) {
2083
2078
  if (node.diffType === "modify") {
2084
2079
  const children = node.getChildren();
package/es/index.d.ts CHANGED
@@ -12,7 +12,7 @@ import { ReactEditor } from "./editor-kernel/react/react-editor.js";
12
12
  import { useLexicalEditor } from "./editor-kernel/react/useLexicalEditor.js";
13
13
  import { ILitexmlService, LitexmlService, XMLReaderFunc, XMLReaderRecord, XMLWriterFunc, XMLWriterRecord } from "./plugins/litexml/service/litexml-service.js";
14
14
  import { LitexmlDataSource } from "./plugins/litexml/data-source/litexml-data-source.js";
15
- import { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "./plugins/litexml/command/index.js";
15
+ import { DiffAction, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "./plugins/litexml/command/symbols.js";
16
16
  import { FileListItem, ImageListItem, MediaLists, extractMediaFromEditorState } from "./headless/extract-media-from-editor-state.js";
17
17
  import { DEFAULT_HEADLESS_EDITOR_PLUGINS, HeadlessDocumentType, HeadlessEditor, HeadlessEditorExport, HeadlessEditorExportOptions, HeadlessEditorHydrationInput, HeadlessEditorOptions, HeadlessLiteXMLBatchOperation, HeadlessLiteXMLInsertOperation, HeadlessLiteXMLOperation, HeadlessLiteXMLRemoveOperation, HeadlessLiteXMLReplaceOperation, createHeadlessEditor } from "./headless/index.js";
18
18
  import { AutoCompletePlugin } from "./plugins/auto-complete/plugin/index.js";
@@ -22,6 +22,7 @@ import { BlockPlugin, BlockPluginOptions } from "./plugins/block/plugin/index.js
22
22
  import { BlockDragTarget } from "./plugins/block/react/core/types.js";
23
23
  import { BlockMenuService, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService, IBlockSelectHandler } from "./plugins/block/service/i-block-menu-service.js";
24
24
  import { ReactBlockPlugin, ReactBlockPluginProps } from "./plugins/block/react/ReactBlockPlugin.js";
25
+ import { ANCHOR_PADDING_CSS_VAR, DEFAULT_BLOCK_ANCHOR_PADDING } from "./plugins/block/react/style.js";
25
26
  import { INSERT_CODEINLINE_COMMAND } from "./plugins/code/command/index.js";
26
27
  import { CodePlugin } from "./plugins/code/plugin/index.js";
27
28
  import { ReactCodePlugin } from "./plugins/code/react/CodeReactPlugin.js";
@@ -73,7 +74,6 @@ import { INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, REMOVE_LIST
73
74
  import { ListPlugin, ListPluginOptions } from "./plugins/list/plugin/index.js";
74
75
  import { ReactListPluginProps } from "./plugins/list/react/type.js";
75
76
  import { ReactListPlugin } from "./plugins/list/react/ReactListPlugin.js";
76
- import { DiffAction, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND } from "./plugins/litexml/command/diffCommand.js";
77
77
  import { LitexmlPlugin, LitexmlPluginOptions } from "./plugins/litexml/plugin/index.js";
78
78
  import { ReactLiteXmlPlugin } from "./plugins/litexml/react/index.js";
79
79
  import { useHasDiffNode } from "./plugins/litexml/react/hooks/useHasDiffNode.js";
@@ -122,4 +122,4 @@ declare function enableHotReload(): void;
122
122
  */
123
123
  declare function disableHotReload(): void;
124
124
  //#endregion
125
- export { $closest, $closestNodeType, $createCursorNode, $getNearestNodeFromDOMNode, $getNodeFromDOMNode, $isCardLikeElementNode, $isCursorNode, AutoCompletePlugin, BlockDragTarget, BlockMenuService, BlockMovePayload, BlockPlugin, BlockPluginOptions, CONTENT_BLOCKS_DATA_TYPE, CardLikeElementNode, CodePlugin, CodeblockPlugin, CodeblockPluginOptions, CodemirrorPlugin, CodemirrorPluginOptions, CommonPlugin, CommonPluginOptions, ContentBlock, ContentBlocksDataSource, ContentBlocksPlugin, ContentBlocksPluginOptions, DEFAULT_HEADLESS_EDITOR_PLUGINS, DOM_DOCUMENT_FRAGMENT_TYPE, DOM_DOCUMENT_TYPE, DOM_ELEMENT_TYPE, DOM_TEXT_TYPE, DataSource, DiffAction, EDITOR_THEME_KEY, ExtractContentBlocksOptions, FileContentBlock, FileListItem, FilePlugin, FilePluginOptions, GET_MARKDOWN_SELECTION_COMMAND, HIDE_TOOLBAR_COMMAND, HOVER_COMMAND, HRPlugin, HRPluginOptions, HeadlessDocumentType, HeadlessEditor, HeadlessEditorExport, HeadlessEditorExportOptions, HeadlessEditorHydrationInput, HeadlessEditorOptions, HeadlessLiteXMLBatchOperation, HeadlessLiteXMLInsertOperation, HeadlessLiteXMLOperation, HeadlessLiteXMLRemoveOperation, HeadlessLiteXMLReplaceOperation, HotkeyEnum, HotkeyId, HotkeyItem, HotkeyScopeEnum, HotkeyScopeId, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService, IBlockSelectHandler, type IEditor, ILinkService, ILitexmlService, IMarkdownShortCutService, INSERT_CHECK_LIST_COMMAND, INSERT_CODEINLINE_COMMAND, INSERT_CODEMIRROR_COMMAND, INSERT_FILE_COMMAND, INSERT_HEADING_COMMAND, INSERT_HORIZONTAL_RULE_COMMAND, INSERT_IMAGE_COMMAND, INSERT_LINK_COMMAND, INSERT_LINK_HIGHLIGHT_COMMAND, INSERT_MARKDOWN_COMMAND, INSERT_MATH_COMMAND, INSERT_MENTION_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_QUOTE_COMMAND, INSERT_TABLE_COLUMN_COMMAND, INSERT_TABLE_COMMAND, INSERT_TABLE_ROW_COMMAND, INSERT_UNORDERED_LIST_COMMAND, INodePlugin, INodePluginOptions, INodeService, ISlashMenuOption, ISlashOption, ISlashService, ITableControllerMenuActionItem, ITableControllerMenuItem, ITableControllerMenuRenderContext, ITableControllerMenuSeparatorItem, ITableControllerMenuService, ITriggerContext, IUploadService, ImageContentBlock, ImageListItem, ImagePlugin, ImagePluginOptions, Kernel, KeyEnum, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, LexicalErrorBoundary, LexicalPortalContainer, LinkHighlightPlugin, LinkHighlightPluginOptions, LinkPlugin, LinkPluginOptions, ListPlugin, ListPluginOptions, LitexmlDataSource, LitexmlPlugin, LitexmlPluginOptions, LitexmlService, MARKDOWN_READER_LEVEL, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, MOVE_BLOCK_COMMAND, MarkdownPlugin, MathPlugin, MediaLists, MentionPlugin, MentionPluginOptions, MenuRenderProps, ModifierCombination, REMOVE_LIST_COMMAND, ReactAutoCompletePlugin, ReactBlockPlugin, ReactBlockPluginProps, ReactCodePlugin, ReactCodeblockPlugin, ReactCodeblockPluginProps, ReactCodemirrorPlugin, ReactEditor, ReactEditorContent, ReactEditorContentProps, ReactFilePlugin, ReactFilePluginProps, ReactHRPlugin, ReactHRPluginProps, ReactImagePlugin, ReactImagePluginProps, ReactLinkHighlightPlugin, ReactLinkPlugin, ReactLinkPluginProps, ReactListPlugin, ReactListPluginProps, ReactLiteXmlPlugin, ReactMarkdownPlugin, ReactMathPlugin, ReactMentionPlugin, ReactMentionPluginProps, ReactNodePlugin, ReactPlainText, ReactPlainTextProps, ReactSlashOption, ReactSlashOptionProps, ReactSlashPlugin, ReactSlashPluginProps, ReactTablePlugin, ReactToolbarPlugin, ReactVirtualBlockPlugin, SELECT_AFTER_CODEMIRROR_COMMAND, SELECT_BEFORE_CODEMIRROR_COMMAND, SELECT_TABLE_COMMAND, SHOW_TOOLBAR_COMMAND, SerializedMentionNode, SlashMenu, SlashMenuProps, SlashOptions, SlashPlugin, SlashPluginOptions, TableControllerMenuAxis, TableControllerMenuService, TablePlugin, TablePluginOptions, TextContentBlock, ToolbarCommandOptions, UPDATE_CODEBLOCK_LANG, UPDATE_LIST_START_COMMAND, UPLOAD_PRIORITY_HIGH, UPLOAD_PRIORITY_LOW, UPLOAD_PRIORITY_MEDIUM, UploadPlugin, UploadPluginOptions, VirtualBlockPlugin, VirtualBlockPluginOptions, XMLReaderFunc, XMLReaderRecord, XMLWriterFunc, XMLWriterRecord, assert, browserDebug, bundledLanguagesInfo, compareNodeOrder, createDebugLogger, createEmptyEditorState, createHeadlessEditor, cursorNodeSerialized, debugLogger, debugLoggers, detectCodeLanguage, detectLanguage, devConsole, disableHotReload, enableHotReload, extractContentBlocks, extractMediaFromEditorState, extractMediaLists, genServiceId, generateEditorId, getHotkeyById, getKernelFromEditor, getKernelFromEditorConfig, getNodeKeyFromDOMNode, getParentElement, isDOMNode, isDocumentFragment, isPunctuationChar, isPureUrl, isValidUrl, moment, noop, prodSafeLogger, reconcileDecorator, registerBlockMoveCommand, registerEditorKernel, registerLinkHighlightCommand, registerToolbarCommand, resetRandomKey, scrollIntoView, unregisterEditorKernel, useHasDiffNode, useLexicalComposerContext, useLexicalEditor };
125
+ export { $closest, $closestNodeType, $createCursorNode, $getNearestNodeFromDOMNode, $getNodeFromDOMNode, $isCardLikeElementNode, $isCursorNode, ANCHOR_PADDING_CSS_VAR, AutoCompletePlugin, BlockDragTarget, BlockMenuService, BlockMovePayload, BlockPlugin, BlockPluginOptions, CONTENT_BLOCKS_DATA_TYPE, CardLikeElementNode, CodePlugin, CodeblockPlugin, CodeblockPluginOptions, CodemirrorPlugin, CodemirrorPluginOptions, CommonPlugin, CommonPluginOptions, ContentBlock, ContentBlocksDataSource, ContentBlocksPlugin, ContentBlocksPluginOptions, DEFAULT_BLOCK_ANCHOR_PADDING, DEFAULT_HEADLESS_EDITOR_PLUGINS, DOM_DOCUMENT_FRAGMENT_TYPE, DOM_DOCUMENT_TYPE, DOM_ELEMENT_TYPE, DOM_TEXT_TYPE, DataSource, DiffAction, EDITOR_THEME_KEY, ExtractContentBlocksOptions, FileContentBlock, FileListItem, FilePlugin, FilePluginOptions, GET_MARKDOWN_SELECTION_COMMAND, HIDE_TOOLBAR_COMMAND, HOVER_COMMAND, HRPlugin, HRPluginOptions, HeadlessDocumentType, HeadlessEditor, HeadlessEditorExport, HeadlessEditorExportOptions, HeadlessEditorHydrationInput, HeadlessEditorOptions, HeadlessLiteXMLBatchOperation, HeadlessLiteXMLInsertOperation, HeadlessLiteXMLOperation, HeadlessLiteXMLRemoveOperation, HeadlessLiteXMLReplaceOperation, HotkeyEnum, HotkeyId, HotkeyItem, HotkeyScopeEnum, HotkeyScopeId, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService, IBlockSelectHandler, type IEditor, ILinkService, ILitexmlService, IMarkdownShortCutService, INSERT_CHECK_LIST_COMMAND, INSERT_CODEINLINE_COMMAND, INSERT_CODEMIRROR_COMMAND, INSERT_FILE_COMMAND, INSERT_HEADING_COMMAND, INSERT_HORIZONTAL_RULE_COMMAND, INSERT_IMAGE_COMMAND, INSERT_LINK_COMMAND, INSERT_LINK_HIGHLIGHT_COMMAND, INSERT_MARKDOWN_COMMAND, INSERT_MATH_COMMAND, INSERT_MENTION_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_QUOTE_COMMAND, INSERT_TABLE_COLUMN_COMMAND, INSERT_TABLE_COMMAND, INSERT_TABLE_ROW_COMMAND, INSERT_UNORDERED_LIST_COMMAND, INodePlugin, INodePluginOptions, INodeService, ISlashMenuOption, ISlashOption, ISlashService, ITableControllerMenuActionItem, ITableControllerMenuItem, ITableControllerMenuRenderContext, ITableControllerMenuSeparatorItem, ITableControllerMenuService, ITriggerContext, IUploadService, ImageContentBlock, ImageListItem, ImagePlugin, ImagePluginOptions, Kernel, KeyEnum, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, LexicalErrorBoundary, LexicalPortalContainer, LinkHighlightPlugin, LinkHighlightPluginOptions, LinkPlugin, LinkPluginOptions, ListPlugin, ListPluginOptions, LitexmlDataSource, LitexmlPlugin, LitexmlPluginOptions, LitexmlService, MARKDOWN_READER_LEVEL, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, MOVE_BLOCK_COMMAND, MarkdownPlugin, MathPlugin, MediaLists, MentionPlugin, MentionPluginOptions, MenuRenderProps, ModifierCombination, REMOVE_LIST_COMMAND, ReactAutoCompletePlugin, ReactBlockPlugin, ReactBlockPluginProps, ReactCodePlugin, ReactCodeblockPlugin, ReactCodeblockPluginProps, ReactCodemirrorPlugin, ReactEditor, ReactEditorContent, ReactEditorContentProps, ReactFilePlugin, ReactFilePluginProps, ReactHRPlugin, ReactHRPluginProps, ReactImagePlugin, ReactImagePluginProps, ReactLinkHighlightPlugin, ReactLinkPlugin, ReactLinkPluginProps, ReactListPlugin, ReactListPluginProps, ReactLiteXmlPlugin, ReactMarkdownPlugin, ReactMathPlugin, ReactMentionPlugin, ReactMentionPluginProps, ReactNodePlugin, ReactPlainText, ReactPlainTextProps, ReactSlashOption, ReactSlashOptionProps, ReactSlashPlugin, ReactSlashPluginProps, ReactTablePlugin, ReactToolbarPlugin, ReactVirtualBlockPlugin, SELECT_AFTER_CODEMIRROR_COMMAND, SELECT_BEFORE_CODEMIRROR_COMMAND, SELECT_TABLE_COMMAND, SHOW_TOOLBAR_COMMAND, SerializedMentionNode, SlashMenu, SlashMenuProps, SlashOptions, SlashPlugin, SlashPluginOptions, TableControllerMenuAxis, TableControllerMenuService, TablePlugin, TablePluginOptions, TextContentBlock, ToolbarCommandOptions, UPDATE_CODEBLOCK_LANG, UPDATE_LIST_START_COMMAND, UPLOAD_PRIORITY_HIGH, UPLOAD_PRIORITY_LOW, UPLOAD_PRIORITY_MEDIUM, UploadPlugin, UploadPluginOptions, VirtualBlockPlugin, VirtualBlockPluginOptions, XMLReaderFunc, XMLReaderRecord, XMLWriterFunc, XMLWriterRecord, assert, browserDebug, bundledLanguagesInfo, compareNodeOrder, createDebugLogger, createEmptyEditorState, createHeadlessEditor, cursorNodeSerialized, debugLogger, debugLoggers, detectCodeLanguage, detectLanguage, devConsole, disableHotReload, enableHotReload, extractContentBlocks, extractMediaFromEditorState, extractMediaLists, genServiceId, generateEditorId, getHotkeyById, getKernelFromEditor, getKernelFromEditorConfig, getNodeKeyFromDOMNode, getParentElement, isDOMNode, isDocumentFragment, isPunctuationChar, isPureUrl, isValidUrl, moment, noop, prodSafeLogger, reconcileDecorator, registerBlockMoveCommand, registerEditorKernel, registerLinkHighlightCommand, registerToolbarCommand, resetRandomKey, scrollIntoView, unregisterEditorKernel, useHasDiffNode, useLexicalComposerContext, useLexicalEditor };
package/es/index.js CHANGED
@@ -14,8 +14,7 @@ import { ReactEditor } from "./editor-kernel/react/react-editor.js";
14
14
  import { useLexicalEditor } from "./editor-kernel/react/useLexicalEditor.js";
15
15
  import { INSERT_HEADING_COMMAND, INSERT_QUOTE_COMMAND } from "./plugins/common/command/index.js";
16
16
  import { $createCursorNode, $isCardLikeElementNode, $isCursorNode, CardLikeElementNode, cursorNodeSerialized } from "./plugins/common/node/cursor.js";
17
- import { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "./plugins/litexml/command/index.js";
18
- import { DiffAction, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND } from "./plugins/litexml/command/diffCommand.js";
17
+ import { DiffAction, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "./plugins/litexml/command/symbols.js";
19
18
  import { INodeService } from "./plugins/inode/service/index.js";
20
19
  import { INodePlugin } from "./plugins/inode/plugin/index.js";
21
20
  import ReactNodePlugin from "./plugins/inode/react/index.js";
@@ -53,6 +52,7 @@ import { AutoCompletePlugin } from "./plugins/auto-complete/plugin/index.js";
53
52
  import ReactAutoCompletePlugin from "./plugins/auto-complete/react/ReactAutoCompletePlugin.js";
54
53
  import { MOVE_BLOCK_COMMAND, registerBlockMoveCommand } from "./plugins/block/command/index.js";
55
54
  import { BlockPlugin } from "./plugins/block/plugin/index.js";
55
+ import { ANCHOR_PADDING_CSS_VAR, DEFAULT_BLOCK_ANCHOR_PADDING } from "./plugins/block/react/style.js";
56
56
  import ReactBlockPlugin from "./plugins/block/react/ReactBlockPlugin.js";
57
57
  import ReactCodePlugin from "./plugins/code/react/CodeReactPlugin.js";
58
58
  import { UPDATE_CODEBLOCK_LANG } from "./plugins/codeblock/command/index.js";
@@ -127,4 +127,4 @@ function disableHotReload() {
127
127
  }
128
128
  }
129
129
  //#endregion
130
- export { $closest, $closestNodeType, $createCursorNode, $getNearestNodeFromDOMNode, $getNodeFromDOMNode, $isCardLikeElementNode, $isCursorNode, AutoCompletePlugin, BlockMenuService, BlockPlugin, CONTENT_BLOCKS_DATA_TYPE, CardLikeElementNode, CodePlugin, CodeblockPlugin, CodemirrorPlugin, CommonPlugin, ContentBlocksDataSource, ContentBlocksPlugin, DEFAULT_HEADLESS_EDITOR_PLUGINS, DOM_DOCUMENT_FRAGMENT_TYPE, DOM_DOCUMENT_TYPE, DOM_ELEMENT_TYPE, DOM_TEXT_TYPE, DataSource, DiffAction, EDITOR_THEME_KEY, FilePlugin, GET_MARKDOWN_SELECTION_COMMAND, HIDE_TOOLBAR_COMMAND, HOVER_COMMAND, HRPlugin, HeadlessEditor, HotkeyEnum, HotkeyScopeEnum, IBlockMenuService, ILinkService, ILitexmlService, IMarkdownShortCutService, INSERT_CHECK_LIST_COMMAND, INSERT_CODEINLINE_COMMAND, INSERT_CODEMIRROR_COMMAND, INSERT_FILE_COMMAND, INSERT_HEADING_COMMAND, INSERT_HORIZONTAL_RULE_COMMAND, INSERT_IMAGE_COMMAND, INSERT_LINK_COMMAND, INSERT_LINK_HIGHLIGHT_COMMAND, INSERT_MARKDOWN_COMMAND, INSERT_MATH_COMMAND, INSERT_MENTION_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_QUOTE_COMMAND, INSERT_TABLE_COLUMN_COMMAND, INSERT_TABLE_COMMAND, INSERT_TABLE_ROW_COMMAND, INSERT_UNORDERED_LIST_COMMAND, INodePlugin, INodeService, ITableControllerMenuService, IUploadService, ImagePlugin, Kernel, KeyEnum, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, LexicalErrorBoundary, LexicalPortalContainer, LinkHighlightPlugin, LinkPlugin, ListPlugin, LitexmlDataSource, LitexmlPlugin, LitexmlService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, MOVE_BLOCK_COMMAND, MarkdownPlugin, MathPlugin, MentionPlugin, REMOVE_LIST_COMMAND, ReactAutoCompletePlugin, ReactBlockPlugin, ReactCodePlugin, ReactCodeblockPlugin, ReactCodemirrorPlugin, ReactEditor, ReactEditorContent, ReactFilePlugin, ReactHRPlugin, ReactImagePlugin, ReactLinkHighlightPlugin, ReactLinkPlugin, ReactListPlugin, ReactLiteXmlPlugin, ReactMarkdownPlugin, ReactMathPlugin, ReactMentionPlugin, ReactNodePlugin, ReactPlainText, ReactSlashOption, ReactSlashPlugin, ReactTablePlugin, ReactToolbarPlugin, ReactVirtualBlockPlugin, SELECT_AFTER_CODEMIRROR_COMMAND, SELECT_BEFORE_CODEMIRROR_COMMAND, SELECT_TABLE_COMMAND, SHOW_TOOLBAR_COMMAND, SlashMenu, SlashPlugin, TableControllerMenuService, TablePlugin, UPDATE_CODEBLOCK_LANG, UPDATE_LIST_START_COMMAND, UPLOAD_PRIORITY_HIGH, UPLOAD_PRIORITY_LOW, UPLOAD_PRIORITY_MEDIUM, UploadPlugin, VirtualBlockPlugin, assert, browserDebug, bundledLanguagesInfo, compareNodeOrder, createDebugLogger, createEmptyEditorState, createHeadlessEditor, cursorNodeSerialized, debugLogger, debugLoggers, detectCodeLanguage, detectLanguage, devConsole, disableHotReload, enableHotReload, extractContentBlocks, extractMediaFromEditorState, extractMediaLists, genServiceId, generateEditorId, getHotkeyById, getKernelFromEditor, getKernelFromEditorConfig, getNodeKeyFromDOMNode, getParentElement, isDOMNode, isDocumentFragment, isPunctuationChar, isPureUrl, isValidUrl, moment, noop, prodSafeLogger, reconcileDecorator, registerBlockMoveCommand, registerEditorKernel, registerLinkHighlightCommand, registerToolbarCommand, resetRandomKey, scrollIntoView, unregisterEditorKernel, useHasDiffNode, useLexicalComposerContext, useLexicalEditor };
130
+ export { $closest, $closestNodeType, $createCursorNode, $getNearestNodeFromDOMNode, $getNodeFromDOMNode, $isCardLikeElementNode, $isCursorNode, ANCHOR_PADDING_CSS_VAR, AutoCompletePlugin, BlockMenuService, BlockPlugin, CONTENT_BLOCKS_DATA_TYPE, CardLikeElementNode, CodePlugin, CodeblockPlugin, CodemirrorPlugin, CommonPlugin, ContentBlocksDataSource, ContentBlocksPlugin, DEFAULT_BLOCK_ANCHOR_PADDING, DEFAULT_HEADLESS_EDITOR_PLUGINS, DOM_DOCUMENT_FRAGMENT_TYPE, DOM_DOCUMENT_TYPE, DOM_ELEMENT_TYPE, DOM_TEXT_TYPE, DataSource, DiffAction, EDITOR_THEME_KEY, FilePlugin, GET_MARKDOWN_SELECTION_COMMAND, HIDE_TOOLBAR_COMMAND, HOVER_COMMAND, HRPlugin, HeadlessEditor, HotkeyEnum, HotkeyScopeEnum, IBlockMenuService, ILinkService, ILitexmlService, IMarkdownShortCutService, INSERT_CHECK_LIST_COMMAND, INSERT_CODEINLINE_COMMAND, INSERT_CODEMIRROR_COMMAND, INSERT_FILE_COMMAND, INSERT_HEADING_COMMAND, INSERT_HORIZONTAL_RULE_COMMAND, INSERT_IMAGE_COMMAND, INSERT_LINK_COMMAND, INSERT_LINK_HIGHLIGHT_COMMAND, INSERT_MARKDOWN_COMMAND, INSERT_MATH_COMMAND, INSERT_MENTION_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_QUOTE_COMMAND, INSERT_TABLE_COLUMN_COMMAND, INSERT_TABLE_COMMAND, INSERT_TABLE_ROW_COMMAND, INSERT_UNORDERED_LIST_COMMAND, INodePlugin, INodeService, ITableControllerMenuService, IUploadService, ImagePlugin, Kernel, KeyEnum, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, LexicalErrorBoundary, LexicalPortalContainer, LinkHighlightPlugin, LinkPlugin, ListPlugin, LitexmlDataSource, LitexmlPlugin, LitexmlService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, MOVE_BLOCK_COMMAND, MarkdownPlugin, MathPlugin, MentionPlugin, REMOVE_LIST_COMMAND, ReactAutoCompletePlugin, ReactBlockPlugin, ReactCodePlugin, ReactCodeblockPlugin, ReactCodemirrorPlugin, ReactEditor, ReactEditorContent, ReactFilePlugin, ReactHRPlugin, ReactImagePlugin, ReactLinkHighlightPlugin, ReactLinkPlugin, ReactListPlugin, ReactLiteXmlPlugin, ReactMarkdownPlugin, ReactMathPlugin, ReactMentionPlugin, ReactNodePlugin, ReactPlainText, ReactSlashOption, ReactSlashPlugin, ReactTablePlugin, ReactToolbarPlugin, ReactVirtualBlockPlugin, SELECT_AFTER_CODEMIRROR_COMMAND, SELECT_BEFORE_CODEMIRROR_COMMAND, SELECT_TABLE_COMMAND, SHOW_TOOLBAR_COMMAND, SlashMenu, SlashPlugin, TableControllerMenuService, TablePlugin, UPDATE_CODEBLOCK_LANG, UPDATE_LIST_START_COMMAND, UPLOAD_PRIORITY_HIGH, UPLOAD_PRIORITY_LOW, UPLOAD_PRIORITY_MEDIUM, UploadPlugin, VirtualBlockPlugin, assert, browserDebug, bundledLanguagesInfo, compareNodeOrder, createDebugLogger, createEmptyEditorState, createHeadlessEditor, cursorNodeSerialized, debugLogger, debugLoggers, detectCodeLanguage, detectLanguage, devConsole, disableHotReload, enableHotReload, extractContentBlocks, extractMediaFromEditorState, extractMediaLists, genServiceId, generateEditorId, getHotkeyById, getKernelFromEditor, getKernelFromEditorConfig, getNodeKeyFromDOMNode, getParentElement, isDOMNode, isDocumentFragment, isPunctuationChar, isPureUrl, isValidUrl, moment, noop, prodSafeLogger, reconcileDecorator, registerBlockMoveCommand, registerEditorKernel, registerLinkHighlightCommand, registerToolbarCommand, resetRandomKey, scrollIntoView, unregisterEditorKernel, useHasDiffNode, useLexicalComposerContext, useLexicalEditor };
@@ -2,4 +2,5 @@ import { BlockMovePayload, MOVE_BLOCK_COMMAND, registerBlockMoveCommand } from "
2
2
  import { BlockPlugin, BlockPluginOptions } from "./plugin/index.js";
3
3
  import { BlockDragTarget } from "./react/core/types.js";
4
4
  import { BlockMenuService, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService, IBlockSelectHandler } from "./service/i-block-menu-service.js";
5
- import { ReactBlockPlugin, ReactBlockPluginProps } from "./react/ReactBlockPlugin.js";
5
+ import { ReactBlockPlugin, ReactBlockPluginProps } from "./react/ReactBlockPlugin.js";
6
+ import { ANCHOR_PADDING_CSS_VAR, DEFAULT_BLOCK_ANCHOR_PADDING } from "./react/style.js";
@@ -1,6 +1,15 @@
1
1
  import { IEditorPluginConstructor } from "../../../types/kernel.js";
2
2
  //#region src/plugins/block/plugin/index.d.ts
3
3
  interface BlockPluginOptions {
4
+ /**
5
+ * Inline padding reserved on the editor root so the floating block menu /
6
+ * drag handle has somewhere to render without overlapping the block content.
7
+ * Pass `0` (or `'0'`) when the surrounding layout already provides enough
8
+ * left gutter. Accepts a number (treated as px) or any valid CSS
9
+ * `padding-inline` value (e.g. `'40px 0'`). When omitted, defaults to 54px
10
+ * on each side.
11
+ */
12
+ anchorPadding?: number | string;
4
13
  attributeName?: string;
5
14
  className?: string;
6
15
  }
@@ -7,13 +7,13 @@ import { BlockPlugin } from "../plugin/index.js";
7
7
  import { createRuntimeContext } from "./core/runtime-context.js";
8
8
  import { collectDragBlocks, getBlockMeasureRect, getTableBlockRect, isTableBlockElement } from "./drag/drag-utils.js";
9
9
  import { startBlockDragSession } from "./drag/drag-session.js";
10
- import { styles } from "./style.js";
11
- import { Icon } from "@lobehub/ui";
10
+ import { ANCHOR_PADDING_CSS_VAR, styles } from "./style.js";
11
+ import { DropdownMenu, Icon, useAppElement } from "@lobehub/ui";
12
12
  import { GripVerticalIcon, PlusIcon } from "lucide-react";
13
13
  import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
14
14
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
15
  import { cx } from "antd-style";
16
- import { Button as Button$1, Dropdown as Dropdown$1, theme } from "antd";
16
+ import { Button as Button$1, theme } from "antd";
17
17
  import { $findTableNode, $isTableSelection } from "@lexical/table";
18
18
  import { $getNodeByKey, $getSelection, $isRangeSelection } from "lexical";
19
19
  import { createPortal } from "react-dom";
@@ -33,8 +33,13 @@ const getTableMenuAnchorRect = (element) => {
33
33
  const ReactBlockPlugin = (props) => {
34
34
  const { token } = theme.useToken();
35
35
  const [editor] = useLexicalComposerContext();
36
- const { rootClassName, className, attributeName, locale, onHoverBlockChange, onDragTargetChange, onDragTargetResolve } = props;
36
+ const appElement = useAppElement();
37
+ const { rootClassName, className, attributeName, anchorPadding, locale, onHoverBlockChange, onDragTargetChange, onDragTargetResolve } = props;
37
38
  const mergedRootClassName = cx(styles.root, rootClassName?.trim() || className?.trim());
39
+ const anchorPaddingValue = useMemo(() => {
40
+ if (anchorPadding === void 0) return null;
41
+ return typeof anchorPadding === "number" ? `${anchorPadding}px` : anchorPadding;
42
+ }, [anchorPadding]);
38
43
  const menuRef = useRef(null);
39
44
  const dragLayerRef = useRef(null);
40
45
  const contextRef = useRef(createRuntimeContext());
@@ -53,15 +58,23 @@ const ReactBlockPlugin = (props) => {
53
58
  useLayoutEffect(() => {
54
59
  if (locale) editor.registerLocale(locale);
55
60
  editor.registerPlugin(BlockPlugin, {
61
+ anchorPadding,
56
62
  attributeName,
57
63
  className: mergedRootClassName
58
64
  });
59
65
  }, [
66
+ anchorPadding,
60
67
  attributeName,
61
68
  editor,
62
69
  locale,
63
70
  mergedRootClassName
64
71
  ]);
72
+ useLexicalEditor((lexicalEditor) => lexicalEditor.registerRootListener((rootElement, prevRootElement) => {
73
+ if (prevRootElement) prevRootElement.style.removeProperty(ANCHOR_PADDING_CSS_VAR);
74
+ if (!rootElement) return;
75
+ if (anchorPaddingValue === null) rootElement.style.removeProperty(ANCHOR_PADDING_CSS_VAR);
76
+ else rootElement.style.setProperty(ANCHOR_PADDING_CSS_VAR, anchorPaddingValue);
77
+ }), [anchorPaddingValue]);
65
78
  useLexicalEditor(() => {
66
79
  const service = editor.requireService(IBlockMenuService);
67
80
  if (!service) {
@@ -382,24 +395,50 @@ const ReactBlockPlugin = (props) => {
382
395
  editor,
383
396
  isDragging
384
397
  ]);
398
+ const menuContext = useMemo(() => {
399
+ if (operationMenuOpen && operationMenuContext) return operationMenuContext;
400
+ const lockedContext = blockMenuService?.getMenuLockedContext();
401
+ if (lockedContext) {
402
+ const root = editor.getRootElement();
403
+ const fresh = root?.querySelector(`[data-block-id="${CSS.escape(lockedContext.blockId)}"]`);
404
+ if (fresh && root?.contains(fresh)) return {
405
+ blockElement: fresh,
406
+ blockId: lockedContext.blockId,
407
+ editor
408
+ };
409
+ }
410
+ if (!hoveredBlock) return null;
411
+ return {
412
+ blockElement: hoveredBlock.blockElement,
413
+ blockId: hoveredBlock.blockId,
414
+ editor
415
+ };
416
+ }, [
417
+ editor,
418
+ hoveredBlock,
419
+ operationMenuOpen,
420
+ operationMenuContext,
421
+ blockMenuService,
422
+ menuVersion
423
+ ]);
385
424
  useLayoutEffect(() => {
386
- if (!hoveredBlock) {
425
+ if (!menuContext) {
387
426
  if (operationMenuOpen) return;
388
427
  setMenuPosition({});
389
428
  return;
390
429
  }
391
430
  const updateMenuPosition = () => {
392
- const blockRect = getBlockMeasureRect(hoveredBlock.blockElement);
431
+ const blockRect = getBlockMeasureRect(menuContext.blockElement);
393
432
  if (!blockRect) {
394
433
  setMenuPosition({});
395
434
  return;
396
435
  }
397
436
  const menuWidth = menuRef.current?.offsetWidth || 32;
398
437
  const gap = 8;
399
- const listItemOffset = hoveredBlock.blockElement.tagName === "LI" ? 16 : 0;
400
- const isTableBlock = isTableBlockElement(hoveredBlock.blockElement);
401
- const tableMenuOffset = focusedTableBlockId === hoveredBlock.blockId && isTableBlock ? TABLE_FOCUSED_MENU_OFFSET : 0;
402
- const tableAnchorRect = isTableBlock ? getTableMenuAnchorRect(hoveredBlock.blockElement) : null;
438
+ const listItemOffset = menuContext.blockElement.tagName === "LI" ? 16 : 0;
439
+ const isTableBlock = isTableBlockElement(menuContext.blockElement);
440
+ const tableMenuOffset = focusedTableBlockId === menuContext.blockId && isTableBlock ? TABLE_FOCUSED_MENU_OFFSET : 0;
441
+ const tableAnchorRect = isTableBlock ? getTableMenuAnchorRect(menuContext.blockElement) : null;
403
442
  const root = editor.getRootElement();
404
443
  const rootRect = root?.getBoundingClientRect();
405
444
  const rootPaddingLeft = root ? Number.parseFloat(window.getComputedStyle(root).paddingLeft || "0") : 0;
@@ -422,18 +461,10 @@ const ReactBlockPlugin = (props) => {
422
461
  }, [
423
462
  editor,
424
463
  focusedTableBlockId,
425
- hoveredBlock,
464
+ menuContext,
426
465
  layoutVersion,
427
466
  operationMenuOpen
428
467
  ]);
429
- const menuContext = useMemo(() => {
430
- if (!hoveredBlock) return null;
431
- return {
432
- blockElement: hoveredBlock.blockElement,
433
- blockId: hoveredBlock.blockId,
434
- editor
435
- };
436
- }, [editor, hoveredBlock]);
437
468
  const operationMenus = useMemo(() => {
438
469
  if (!operationMenuContext || !blockMenuService) return [];
439
470
  return blockMenuService.getMenus(operationMenuContext);
@@ -493,19 +524,6 @@ const ReactBlockPlugin = (props) => {
493
524
  contextRef.current.dragTarget = null;
494
525
  setDragIndicator(null);
495
526
  };
496
- const toggleOperationMenu = (context) => {
497
- if (!context) {
498
- setOperationMenuOpen(false);
499
- setOperationMenuContext(null);
500
- return;
501
- }
502
- setOperationMenuOpen((open) => {
503
- const shouldOpen = !(open && contextRef.current.operationMenuAnchorBlockId === context.blockId);
504
- contextRef.current.operationMenuAnchorBlockId = shouldOpen ? context.blockId : null;
505
- setOperationMenuContext(shouldOpen ? context : null);
506
- return shouldOpen;
507
- });
508
- };
509
527
  const handleDragHandlePointerDown = (event) => {
510
528
  if (!menuContext) return;
511
529
  event.stopPropagation();
@@ -521,16 +539,24 @@ const ReactBlockPlugin = (props) => {
521
539
  onDraggingChange: setIsDragging,
522
540
  setDragIndicator,
523
541
  setOperationMenuContext,
524
- setOperationMenuOpen,
525
- toggleOperationMenu
542
+ setOperationMenuOpen
526
543
  });
527
544
  };
528
- const handleDragHandleClick = () => {
545
+ const handleOperationMenuOpenChange = (open) => {
529
546
  if (contextRef.current.ignoreNextHandleClick) {
530
547
  contextRef.current.ignoreNextHandleClick = false;
531
548
  return;
532
549
  }
533
- toggleOperationMenu(menuContext);
550
+ if (!menuContext) return;
551
+ if (open) {
552
+ setOperationMenuOpen(true);
553
+ setOperationMenuContext(menuContext);
554
+ contextRef.current.operationMenuAnchorBlockId = menuContext.blockId;
555
+ } else {
556
+ setOperationMenuOpen(false);
557
+ setOperationMenuContext(null);
558
+ contextRef.current.operationMenuAnchorBlockId = null;
559
+ }
534
560
  };
535
561
  const dropdownItems = useMemo(() => operationMenus.map((item) => ({
536
562
  key: item.key,
@@ -562,19 +588,13 @@ const ReactBlockPlugin = (props) => {
562
588
  title,
563
589
  type: "text"
564
590
  }, item.key);
565
- }), /* @__PURE__ */ jsx(Dropdown$1, {
566
- align: { points: ["tr", "tl"] },
567
- classNames: { root: OPERATION_MENU_OVERLAY_CLASS },
568
- menu: { items: dropdownItems },
569
- onOpenChange: (open) => {
570
- if (!open) {
571
- setOperationMenuOpen(false);
572
- setOperationMenuContext(null);
573
- contextRef.current.operationMenuAnchorBlockId = null;
574
- }
575
- },
591
+ }), /* @__PURE__ */ jsx(DropdownMenu, {
592
+ items: dropdownItems,
593
+ onOpenChange: handleOperationMenuOpenChange,
576
594
  open: operationMenuOpen && operationMenuContext?.blockId === menuContext.blockId,
577
- trigger: [],
595
+ placement: "leftTop",
596
+ popupProps: { className: OPERATION_MENU_OVERLAY_CLASS },
597
+ positionerProps: { style: { zIndex: 1e3 } },
578
598
  children: /* @__PURE__ */ jsx(Button$1, {
579
599
  "aria-label": "Block actions and drag",
580
600
  className: styles.dragHandle,
@@ -583,7 +603,6 @@ const ReactBlockPlugin = (props) => {
583
603
  icon: GripVerticalIcon,
584
604
  size: 14
585
605
  }),
586
- onClick: handleDragHandleClick,
587
606
  onPointerDown: handleDragHandlePointerDown,
588
607
  size: "small",
589
608
  title: "Block actions and drag",
@@ -606,7 +625,7 @@ const ReactBlockPlugin = (props) => {
606
625
  top: dragIndicator.top,
607
626
  width: dragIndicator.width
608
627
  }
609
- })] }), document.body)] });
628
+ })] }), appElement ?? document.body)] });
610
629
  };
611
630
  ReactBlockPlugin.displayName = "ReactBlockPlugin";
612
631
  //#endregion
@@ -64,8 +64,7 @@ const applyDragSourceOpacity = (sourceBlock) => {
64
64
  sourceBlock.style.opacity = previousOpacity;
65
65
  };
66
66
  };
67
- const startBlockDragSession = ({ clientX, clientY, clearDragPreview, contextRef, dragGhostContainer, editor, menuContext, onDraggingChange, onDragTargetResolve, setDragIndicator, setOperationMenuContext, setOperationMenuOpen, toggleOperationMenu }) => {
68
- setOperationMenuOpen(false);
67
+ const startBlockDragSession = ({ clientX, clientY, clearDragPreview, contextRef, dragGhostContainer, editor, menuContext, onDraggingChange, onDragTargetResolve, setDragIndicator, setOperationMenuContext, setOperationMenuOpen }) => {
69
68
  let dragGhost = null;
70
69
  let restoreSourceOpacity = null;
71
70
  contextRef.current.dragStartPoint = {
@@ -148,8 +147,6 @@ const startBlockDragSession = ({ clientX, clientY, clearDragPreview, contextRef,
148
147
  };
149
148
  const onPointerUp = () => {
150
149
  if (!contextRef.current.dragStarted && !contextRef.current.dragMoved) {
151
- toggleOperationMenu(menuContext);
152
- contextRef.current.ignoreNextHandleClick = true;
153
150
  contextRef.current.draggingSource = null;
154
151
  contextRef.current.dragPointerY = null;
155
152
  contextRef.current.dragBlocks = [];
@@ -1,2 +1,3 @@
1
1
  import { BlockDragTarget } from "./core/types.js";
2
- import { ReactBlockPlugin, ReactBlockPluginProps } from "./ReactBlockPlugin.js";
2
+ import { ReactBlockPlugin, ReactBlockPluginProps } from "./ReactBlockPlugin.js";
3
+ import { ANCHOR_PADDING_CSS_VAR, DEFAULT_BLOCK_ANCHOR_PADDING } from "./style.js";
@@ -0,0 +1,11 @@
1
+ //#region src/plugins/block/react/style.d.ts
2
+ declare const ANCHOR_PADDING_CSS_VAR = "--lobe-block-anchor-padding";
3
+ /**
4
+ * Default inline padding (px) reserved on the editor root so the floating
5
+ * block menu / drag handle has room to render. Exported for consumers that
6
+ * need to align surrounding chrome (e.g. a title section above the editor)
7
+ * with the editor's content edge.
8
+ */
9
+ declare const DEFAULT_BLOCK_ANCHOR_PADDING = 54;
10
+ //#endregion
11
+ export { ANCHOR_PADDING_CSS_VAR, DEFAULT_BLOCK_ANCHOR_PADDING };
@@ -1,5 +1,13 @@
1
1
  import { createStaticStyles } from "antd-style";
2
2
  //#region src/plugins/block/react/style.ts
3
+ const ANCHOR_PADDING_CSS_VAR = "--lobe-block-anchor-padding";
4
+ /**
5
+ * Default inline padding (px) reserved on the editor root so the floating
6
+ * block menu / drag handle has room to render. Exported for consumers that
7
+ * need to align surrounding chrome (e.g. a title section above the editor)
8
+ * with the editor's content edge.
9
+ */
10
+ const DEFAULT_BLOCK_ANCHOR_PADDING = 54;
3
11
  const styles = createStaticStyles(({ css, cssVar }) => ({
4
12
  dragHandle: css`
5
13
  cursor: grab !important;
@@ -59,8 +67,8 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
59
67
  }
60
68
  `,
61
69
  root: css`
62
- padding-inline: 54px 54px;
70
+ padding-inline: var(${ANCHOR_PADDING_CSS_VAR}, ${54}px);
63
71
  `
64
72
  }));
65
73
  //#endregion
66
- export { styles };
74
+ export { ANCHOR_PADDING_CSS_VAR, DEFAULT_BLOCK_ANCHOR_PADDING, styles };
@@ -30,12 +30,25 @@ interface IBlockSelectHandler {
30
30
  }
31
31
  interface IBlockMenuService {
32
32
  getActionButtons(context: IBlockMenuRenderContext): IBlockActionButton[];
33
+ /**
34
+ * Returns the currently pinned menu context, or null when no lock is active.
35
+ * While locked, the block menu UI freezes at the pinned block and hover
36
+ * detection no longer reassigns the anchor.
37
+ */
38
+ getMenuLockedContext(): IBlockMenuRenderContext | null;
33
39
  getMenus(context: IBlockMenuRenderContext): IBlockMenuItem[];
34
40
  isMenuSuppressed(): boolean;
35
41
  registerActionButton(item: IBlockActionButton): () => void;
36
42
  registerMenu(item: IBlockMenuItem): () => void;
37
43
  registerSelectHandler(item: IBlockSelectHandler): () => void;
38
44
  selectNode(node: LexicalNode): boolean;
45
+ /**
46
+ * Pin the block menu UI to the provided context (typically while a consumer
47
+ * popup spawned by an action button is open). Pass `null` for `context` to
48
+ * release the lock owned by `key`. Multiple keys may hold locks
49
+ * simultaneously; the most recently set lock wins.
50
+ */
51
+ setMenuLockedContext(key: string, context: IBlockMenuRenderContext | null): void;
39
52
  setMenuSuppressed(key: string, suppressed: boolean): void;
40
53
  subscribe(listener: () => void): () => void;
41
54
  }
@@ -44,15 +57,18 @@ declare class BlockMenuService implements IBlockMenuService {
44
57
  private actionButtons;
45
58
  private items;
46
59
  private listeners;
60
+ private lockedContexts;
47
61
  private selectHandlers;
48
62
  private suppressors;
49
63
  getActionButtons(context: IBlockMenuRenderContext): IBlockActionButton[];
64
+ getMenuLockedContext(): IBlockMenuRenderContext | null;
50
65
  getMenus(context: IBlockMenuRenderContext): IBlockMenuItem[];
51
66
  isMenuSuppressed(): boolean;
52
67
  registerActionButton(item: IBlockActionButton): () => void;
53
68
  registerMenu(item: IBlockMenuItem): () => void;
54
69
  registerSelectHandler(item: IBlockSelectHandler): () => void;
55
70
  selectNode(node: LexicalNode): boolean;
71
+ setMenuLockedContext(key: string, context: IBlockMenuRenderContext | null): void;
56
72
  setMenuSuppressed(key: string, suppressed: boolean): void;
57
73
  subscribe(listener: () => void): () => void;
58
74
  private notify;
@@ -6,12 +6,19 @@ var BlockMenuService = class {
6
6
  this.actionButtons = /* @__PURE__ */ new Map();
7
7
  this.items = /* @__PURE__ */ new Map();
8
8
  this.listeners = /* @__PURE__ */ new Set();
9
+ this.lockedContexts = /* @__PURE__ */ new Map();
9
10
  this.selectHandlers = /* @__PURE__ */ new Map();
10
11
  this.suppressors = /* @__PURE__ */ new Set();
11
12
  }
12
13
  getActionButtons(context) {
13
14
  return Array.from(this.actionButtons.values()).filter((item) => item.when ? item.when(context) : true).sort((a, b) => (a.order || 0) - (b.order || 0));
14
15
  }
16
+ getMenuLockedContext() {
17
+ if (this.lockedContexts.size === 0) return null;
18
+ let last = null;
19
+ for (const ctx of this.lockedContexts.values()) last = ctx;
20
+ return last;
21
+ }
15
22
  getMenus(context) {
16
23
  return Array.from(this.items.values()).filter((item) => item.when ? item.when(context) : true).sort((a, b) => (a.order || 0) - (b.order || 0));
17
24
  }
@@ -47,6 +54,18 @@ var BlockMenuService = class {
47
54
  for (const handler of handlers) if (handler.onSelect(node)) return true;
48
55
  return false;
49
56
  }
57
+ setMenuLockedContext(key, context) {
58
+ const existing = this.lockedContexts.get(key);
59
+ if (context === null) {
60
+ if (!this.lockedContexts.delete(key)) return;
61
+ this.notify();
62
+ return;
63
+ }
64
+ if (existing === context) return;
65
+ this.lockedContexts.delete(key);
66
+ this.lockedContexts.set(key, context);
67
+ this.notify();
68
+ }
50
69
  setMenuSuppressed(key, suppressed) {
51
70
  const size = this.suppressors.size;
52
71
  if (suppressed) this.suppressors.add(key);
@@ -1,17 +1,2 @@
1
- import * as _$lexical from "lexical";
2
- import { LexicalEditor } from "lexical";
3
-
4
- //#region src/plugins/litexml/command/diffCommand.d.ts
5
- declare enum DiffAction {
6
- Reject = 0,
7
- Accept = 1
8
- }
9
- declare const LITEXML_DIFFNODE_COMMAND: _$lexical.LexicalCommand<{
10
- action: DiffAction;
11
- nodeKey: string;
12
- }>;
13
- declare const LITEXML_DIFFNODE_ALL_COMMAND: _$lexical.LexicalCommand<{
14
- action: DiffAction;
15
- }>;
16
- //#endregion
17
- export { DiffAction, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND };
1
+ import { DiffAction, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND } from "./symbols.js";
2
+ import { LexicalEditor } from "lexical";
@@ -1,14 +1,8 @@
1
1
  import { DiffNode } from "../node/DiffNode.js";
2
- import { $getNodeByKey, $isElementNode, COMMAND_PRIORITY_EDITOR, createCommand } from "lexical";
2
+ import { LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND } from "./symbols.js";
3
+ import { $getNodeByKey, $isElementNode, COMMAND_PRIORITY_EDITOR } from "lexical";
3
4
  import { mergeRegister } from "@lexical/utils";
4
5
  //#region src/plugins/litexml/command/diffCommand.ts
5
- let DiffAction = /* @__PURE__ */ function(DiffAction) {
6
- DiffAction[DiffAction["Reject"] = 0] = "Reject";
7
- DiffAction[DiffAction["Accept"] = 1] = "Accept";
8
- return DiffAction;
9
- }({});
10
- const LITEXML_DIFFNODE_COMMAND = createCommand("LITEXML_DIFFNODE_COMMAND");
11
- const LITEXML_DIFFNODE_ALL_COMMAND = createCommand("LITEXML_DIFFNODE_ALL_COMMAND");
12
6
  function doAction(node, action) {
13
7
  if (node.diffType === "modify") {
14
8
  const children = node.getChildren();
@@ -94,4 +88,4 @@ function registerLiteXMLDiffCommand(editor) {
94
88
  }, COMMAND_PRIORITY_EDITOR));
95
89
  }
96
90
  //#endregion
97
- export { DiffAction, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, registerLiteXMLDiffCommand };
91
+ export { registerLiteXMLDiffCommand };
@@ -1,38 +1,2 @@
1
- import * as _$lexical from "lexical";
2
- import { LexicalEditor } from "lexical";
3
-
4
- //#region src/plugins/litexml/command/index.d.ts
5
- declare const LITEXML_MODIFY_COMMAND: _$lexical.LexicalCommand<({
6
- action: "insert";
7
- beforeId: string;
8
- litexml: string;
9
- } | {
10
- action: "insert";
11
- afterId: string;
12
- litexml: string;
13
- } | {
14
- action: "remove";
15
- id: string;
16
- } | {
17
- action: "modify";
18
- litexml: string | string[];
19
- })[]>;
20
- declare const LITEXML_APPLY_COMMAND: _$lexical.LexicalCommand<{
21
- delay?: boolean;
22
- litexml: string | string[];
23
- }>;
24
- declare const LITEXML_REMOVE_COMMAND: _$lexical.LexicalCommand<{
25
- delay?: boolean;
26
- id: string;
27
- }>;
28
- declare const LITEXML_INSERT_COMMAND: _$lexical.LexicalCommand<{
29
- beforeId: string;
30
- delay?: boolean;
31
- litexml: string;
32
- } | {
33
- afterId: string;
34
- delay?: boolean;
35
- litexml: string;
36
- }>;
37
- //#endregion
38
- export { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND };
1
+ import { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "./symbols.js";
2
+ import { LexicalEditor } from "lexical";
@@ -2,7 +2,8 @@ import { createDebugLogger, init_debug } from "../../../utils/debug.js";
2
2
  import { $closest } from "../../../editor-kernel/utils.js";
3
3
  import { $createDiffNode, DiffNode } from "../node/DiffNode.js";
4
4
  import { $cloneNode, $parseSerializedNodeImpl, charToId } from "../utils/index.js";
5
- import { $createParagraphNode, $getNodeByKey, $getRoot, $insertNodes, $isElementNode, COMMAND_PRIORITY_EDITOR, createCommand } from "lexical";
5
+ import { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "./symbols.js";
6
+ import { $createParagraphNode, $getNodeByKey, $getRoot, $insertNodes, $isElementNode, COMMAND_PRIORITY_EDITOR } from "lexical";
6
7
  import { mergeRegister } from "@lexical/utils";
7
8
  import { $isListItemNode } from "@lexical/list";
8
9
  //#region src/plugins/litexml/command/index.ts
@@ -101,10 +102,6 @@ function wrapBlockModify(oldBlock, editor, changeFn) {
101
102
  diffNode.append($cloneNode(newBlock, editor));
102
103
  newBlock.replace(diffNode, false);
103
104
  }
104
- const LITEXML_MODIFY_COMMAND = createCommand("LITEXML_MODIFY_COMMAND");
105
- const LITEXML_APPLY_COMMAND = createCommand("LITEXML_APPLY_COMMAND");
106
- const LITEXML_REMOVE_COMMAND = createCommand("LITEXML_REMOVE_COMMAND");
107
- const LITEXML_INSERT_COMMAND = createCommand("LITEXML_INSERT_COMMAND");
108
105
  function registerLiteXMLCommand(editor, dataSource) {
109
106
  return mergeRegister(editor.registerCommand(LITEXML_MODIFY_COMMAND, (payload) => {
110
107
  const resultPayload = payload.reduce((acc, cur) => {
@@ -330,4 +327,4 @@ function handleInsert(editor, payload, dataSource) {
330
327
  });
331
328
  }
332
329
  //#endregion
333
- export { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, registerLiteXMLCommand };
330
+ export { registerLiteXMLCommand };
@@ -0,0 +1,67 @@
1
+ import * as _$lexical from "lexical";
2
+
3
+ //#region src/plugins/litexml/command/symbols.d.ts
4
+ /**
5
+ * LiteXML command identities.
6
+ *
7
+ * These symbols are intentionally isolated in a side-effect-free module so they
8
+ * keep a SINGLE runtime identity across every entry of this package.
9
+ *
10
+ * Lexical's `dispatchCommand` matches command listeners by object reference, not
11
+ * by the string label. The package ships two independently-bundled entries — the
12
+ * browser build (`index` / `react` / `renderer`) and the node build (`headless`)
13
+ * — and if each entry inlined its own `createCommand(...)` call, dispatching a
14
+ * command obtained from one entry onto an editor registered by the other would
15
+ * silently no-op (different object identities, same label).
16
+ *
17
+ * By keeping every command in this one module — exposed verbatim through
18
+ * `@lobehub/editor/litexml-commands` and emitted as a shared chunk by both
19
+ * builds — a single object backs the command in any runtime, and the module is
20
+ * pure enough to be imported on the server without pulling in the DOM-dependent
21
+ * editor bundle.
22
+ */
23
+ declare enum DiffAction {
24
+ Reject = 0,
25
+ Accept = 1
26
+ }
27
+ declare const LITEXML_MODIFY_COMMAND: _$lexical.LexicalCommand<({
28
+ action: "insert";
29
+ beforeId: string;
30
+ litexml: string;
31
+ } | {
32
+ action: "insert";
33
+ afterId: string;
34
+ litexml: string;
35
+ } | {
36
+ action: "remove";
37
+ id: string;
38
+ } | {
39
+ action: "modify";
40
+ litexml: string | string[];
41
+ })[]>;
42
+ declare const LITEXML_APPLY_COMMAND: _$lexical.LexicalCommand<{
43
+ delay?: boolean;
44
+ litexml: string | string[];
45
+ }>;
46
+ declare const LITEXML_REMOVE_COMMAND: _$lexical.LexicalCommand<{
47
+ delay?: boolean;
48
+ id: string;
49
+ }>;
50
+ declare const LITEXML_INSERT_COMMAND: _$lexical.LexicalCommand<{
51
+ beforeId: string;
52
+ delay?: boolean;
53
+ litexml: string;
54
+ } | {
55
+ afterId: string;
56
+ delay?: boolean;
57
+ litexml: string;
58
+ }>;
59
+ declare const LITEXML_DIFFNODE_COMMAND: _$lexical.LexicalCommand<{
60
+ action: DiffAction;
61
+ nodeKey: string;
62
+ }>;
63
+ declare const LITEXML_DIFFNODE_ALL_COMMAND: _$lexical.LexicalCommand<{
64
+ action: DiffAction;
65
+ }>;
66
+ //#endregion
67
+ export { DiffAction, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND };
@@ -0,0 +1,34 @@
1
+ import { createCommand } from "lexical";
2
+ //#region src/plugins/litexml/command/symbols.ts
3
+ /**
4
+ * LiteXML command identities.
5
+ *
6
+ * These symbols are intentionally isolated in a side-effect-free module so they
7
+ * keep a SINGLE runtime identity across every entry of this package.
8
+ *
9
+ * Lexical's `dispatchCommand` matches command listeners by object reference, not
10
+ * by the string label. The package ships two independently-bundled entries — the
11
+ * browser build (`index` / `react` / `renderer`) and the node build (`headless`)
12
+ * — and if each entry inlined its own `createCommand(...)` call, dispatching a
13
+ * command obtained from one entry onto an editor registered by the other would
14
+ * silently no-op (different object identities, same label).
15
+ *
16
+ * By keeping every command in this one module — exposed verbatim through
17
+ * `@lobehub/editor/litexml-commands` and emitted as a shared chunk by both
18
+ * builds — a single object backs the command in any runtime, and the module is
19
+ * pure enough to be imported on the server without pulling in the DOM-dependent
20
+ * editor bundle.
21
+ */
22
+ let DiffAction = /* @__PURE__ */ function(DiffAction) {
23
+ DiffAction[DiffAction["Reject"] = 0] = "Reject";
24
+ DiffAction[DiffAction["Accept"] = 1] = "Accept";
25
+ return DiffAction;
26
+ }({});
27
+ const LITEXML_MODIFY_COMMAND = createCommand("LITEXML_MODIFY_COMMAND");
28
+ const LITEXML_APPLY_COMMAND = createCommand("LITEXML_APPLY_COMMAND");
29
+ const LITEXML_REMOVE_COMMAND = createCommand("LITEXML_REMOVE_COMMAND");
30
+ const LITEXML_INSERT_COMMAND = createCommand("LITEXML_INSERT_COMMAND");
31
+ const LITEXML_DIFFNODE_COMMAND = createCommand("LITEXML_DIFFNODE_COMMAND");
32
+ const LITEXML_DIFFNODE_ALL_COMMAND = createCommand("LITEXML_DIFFNODE_ALL_COMMAND");
33
+ //#endregion
34
+ export { DiffAction, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND };
@@ -1,7 +1,6 @@
1
1
  import { ILitexmlService, LitexmlService, XMLReaderFunc, XMLReaderRecord, XMLWriterFunc, XMLWriterRecord } from "./service/litexml-service.js";
2
2
  import { LitexmlDataSource } from "./data-source/litexml-data-source.js";
3
- import { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "./command/index.js";
4
- import { DiffAction, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND } from "./command/diffCommand.js";
3
+ import { DiffAction, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "./command/symbols.js";
5
4
  import { LitexmlPlugin, LitexmlPluginOptions } from "./plugin/index.js";
6
5
  import { ReactLiteXmlPlugin } from "./react/index.js";
7
6
  import { useHasDiffNode } from "./react/hooks/useHasDiffNode.js";
@@ -1,5 +1,5 @@
1
1
  import LexicalPortalContainer from "../../../../editor-kernel/react/PortalContainer.js";
2
- import { LITEXML_DIFFNODE_COMMAND } from "../../command/diffCommand.js";
2
+ import { LITEXML_DIFFNODE_COMMAND } from "../../command/symbols.js";
3
3
  import { useTranslation } from "../../../../editor-kernel/react/useTranslation.js";
4
4
  import { styles } from "./style.js";
5
5
  import { ActionIcon, Block } from "@lobehub/ui";
@@ -6,6 +6,7 @@ import { $createParagraphNode, $getNodeByKey, $getSelection, $isRangeSelection,
6
6
  import { $createListItemNode, $isListItemNode } from "@lexical/list";
7
7
  //#region src/plugins/slash/plugin/index.ts
8
8
  init_plugin();
9
+ const SLASH_ADD_LOCK_KEY = "__slash_add_below";
9
10
  const SlashPlugin = class extends KernelPlugin {
10
11
  static {
11
12
  this.pluginName = "SlashPlugin";
@@ -15,6 +16,7 @@ const SlashPlugin = class extends KernelPlugin {
15
16
  this.kernel = kernel;
16
17
  this.config = config;
17
18
  this.service = null;
19
+ this.blockMenuService = null;
18
20
  this.currentSlashTrigger = null;
19
21
  this.currentSlashTriggerIndex = -1;
20
22
  this.manualOpen = false;
@@ -31,13 +33,15 @@ const SlashPlugin = class extends KernelPlugin {
31
33
  this.currentSlashTriggerIndex = -1;
32
34
  this.manualOpen = false;
33
35
  this.suppressOpen = true;
36
+ this.blockMenuService?.setMenuLockedContext(SLASH_ADD_LOCK_KEY, null);
34
37
  }
35
38
  onInit(editor) {
36
39
  const blockMenuService = this.kernel.requireService(IBlockMenuService);
40
+ this.blockMenuService = blockMenuService;
37
41
  if (blockMenuService) {
38
42
  const unregisterAddBlockButton = blockMenuService.registerActionButton({
39
43
  icon: "plus",
40
- key: "__slash_add_below",
44
+ key: SLASH_ADD_LOCK_KEY,
41
45
  onClick: (context) => {
42
46
  const lexicalEditor = context.editor.getLexicalEditor();
43
47
  if (!lexicalEditor) return;
@@ -76,6 +80,7 @@ const SlashPlugin = class extends KernelPlugin {
76
80
  match: null,
77
81
  trigger: slashOptions.trigger
78
82
  });
83
+ blockMenuService.setMenuLockedContext(SLASH_ADD_LOCK_KEY, context);
79
84
  },
80
85
  order: -100,
81
86
  title: "Add block below"
@@ -153,6 +158,8 @@ const SlashPlugin = class extends KernelPlugin {
153
158
  }));
154
159
  }
155
160
  destroy() {
161
+ this.blockMenuService?.setMenuLockedContext(SLASH_ADD_LOCK_KEY, null);
162
+ this.blockMenuService = null;
156
163
  super.destroy();
157
164
  }
158
165
  };
@@ -1,23 +1,19 @@
1
1
  "use client";
2
- import { Menu } from "@lobehub/ui";
3
- import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from "react";
4
- import { jsx } from "react/jsx-runtime";
2
+ import { Icon, menuSharedStyles } from "@lobehub/ui";
3
+ import { useEffect, useLayoutEffect, useMemo, useRef } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { createStaticStyles } from "antd-style";
6
6
  import { createPortal } from "react-dom";
7
7
  import { flip, offset, shift, useFloating } from "@floating-ui/react";
8
8
  //#region src/plugins/slash/react/components/DefaultSlashMenu.tsx
9
9
  const LOBE_THEME_APP_ID$1 = "lobe-ui-theme-app";
10
10
  const styles = createStaticStyles(({ css, cssVar }) => ({
11
- menu: css`
12
- z-index: 9999;
13
- width: max-content;
14
- `,
15
11
  popup: css`
16
12
  scrollbar-width: none;
17
13
 
18
14
  overflow-y: auto;
19
15
 
20
- min-width: 120px;
16
+ min-width: 200px;
21
17
  max-height: min(50vh, 400px);
22
18
  padding: 4px;
23
19
  border-radius: ${cssVar.borderRadius};
@@ -28,68 +24,13 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
28
24
  0 0 15px 0 #00000008,
29
25
  0 2px 30px 0 #00000014,
30
26
  0 0 0 1px ${cssVar.colorBorder} inset;
31
-
32
- .ant-menu {
33
- min-width: 200px;
34
- padding: 0 !important;
35
- border-inline-end: none !important;
36
-
37
- color: ${cssVar.colorText} !important;
38
-
39
- background: transparent !important;
40
- background-color: transparent !important;
41
- }
42
-
43
- .ant-menu-item {
44
- overflow: hidden;
45
- display: flex !important;
46
- align-items: center;
47
-
48
- width: 100% !important;
49
- min-height: 36px;
50
- margin: 0 !important;
51
- padding-block: 8px !important;
52
- padding-inline: 12px !important;
53
- border-radius: ${cssVar.borderRadiusSM} !important;
54
-
55
- font-size: 14px;
56
- line-height: 20px;
57
- color: ${cssVar.colorText} !important;
58
-
59
- background: transparent !important;
60
-
61
- transition: all 150ms ${cssVar.motionEaseOut};
62
-
63
- &:hover,
64
- &.ant-menu-item-active {
65
- background: ${cssVar.colorFillTertiary} !important;
66
- }
67
-
68
- &:active {
69
- background: ${cssVar.colorFillSecondary} !important;
70
- }
71
- }
72
-
73
- .ant-menu-item-divider {
74
- height: 1px;
75
- margin-block: 4px !important;
76
- margin-inline: 0 !important;
77
- background: ${cssVar.colorBorder} !important;
78
- }
79
-
80
- .ant-menu-title-content,
81
- .ant-menu-title-content-with-extra {
82
- overflow: visible;
83
- display: inline-flex;
84
- gap: 24px;
85
- justify-content: space-between;
86
-
87
- width: 100%;
88
-
89
- text-overflow: unset;
90
- }
27
+ `,
28
+ root: css`
29
+ z-index: 1100;
30
+ width: max-content;
91
31
  `
92
32
  }));
33
+ const isDividerOption = (option) => "type" in option && option.type === "divider";
93
34
  const DefaultSlashMenu = ({ activeKey, getPopupContainer, loading, onSelect, open, options, placement: forcePlacement, position }) => {
94
35
  const resolvedPlacement = forcePlacement ? `${forcePlacement}-start` : "top-start";
95
36
  const middleware = useMemo(() => [
@@ -99,7 +40,7 @@ const DefaultSlashMenu = ({ activeKey, getPopupContainer, loading, onSelect, ope
99
40
  ], [forcePlacement]);
100
41
  const getRectRef = useRef(position.getRect);
101
42
  getRectRef.current = position.getRect;
102
- const { refs, floatingStyles, update } = useFloating({
43
+ const { refs, floatingStyles, isPositioned, update } = useFloating({
103
44
  middleware,
104
45
  open,
105
46
  placement: resolvedPlacement,
@@ -136,30 +77,55 @@ const DefaultSlashMenu = ({ activeKey, getPopupContainer, loading, onSelect, ope
136
77
  getPopupContainer,
137
78
  update
138
79
  ]);
139
- const handleMenuClick = useCallback(({ key }) => {
140
- const option = options.find((item) => "key" in item && item.key === key);
141
- if (option) onSelect(option);
142
- }, [options, onSelect]);
143
- const hasVisibleItems = options?.some((item) => !("type" in item && item.type === "divider"));
80
+ const hasVisibleItems = options?.some((item) => !isDividerOption(item));
144
81
  if (!open || !hasVisibleItems) return null;
145
82
  const portalContainer = getPopupContainer?.() || document.getElementById(LOBE_THEME_APP_ID$1) || document.body;
83
+ const renderedItems = loading ? /* @__PURE__ */ jsx("div", {
84
+ className: menuSharedStyles.empty,
85
+ children: "Loading..."
86
+ }) : options.map((opt, index) => {
87
+ if (isDividerOption(opt)) return /* @__PURE__ */ jsx("div", { className: menuSharedStyles.separator }, `__divider_${index}`);
88
+ const item = opt;
89
+ const isHighlighted = item.key === activeKey;
90
+ const isDisabled = Boolean(item.disabled);
91
+ return /* @__PURE__ */ jsxs("div", {
92
+ "aria-disabled": isDisabled || void 0,
93
+ className: menuSharedStyles.item,
94
+ "data-disabled": isDisabled ? "" : void 0,
95
+ "data-highlighted": isHighlighted ? "" : void 0,
96
+ onClick: () => {
97
+ if (isDisabled) return;
98
+ onSelect(item);
99
+ },
100
+ onMouseDown: (event) => event.preventDefault(),
101
+ role: "menuitem",
102
+ children: [
103
+ item.icon ? /* @__PURE__ */ jsx("span", {
104
+ className: menuSharedStyles.icon,
105
+ children: /* @__PURE__ */ jsx(Icon, { icon: item.icon })
106
+ }) : null,
107
+ /* @__PURE__ */ jsx("span", {
108
+ className: menuSharedStyles.label,
109
+ children: item.label
110
+ }),
111
+ item.extra ? /* @__PURE__ */ jsx("span", {
112
+ className: menuSharedStyles.extra,
113
+ children: item.extra
114
+ }) : null
115
+ ]
116
+ }, String(item.key));
117
+ });
146
118
  return createPortal(/* @__PURE__ */ jsx("div", {
147
- className: styles.menu,
119
+ className: styles.root,
148
120
  "data-resloved-placement": resolvedPlacement,
149
121
  ref: refs.setFloating,
150
- style: floatingStyles,
122
+ style: {
123
+ ...floatingStyles,
124
+ visibility: isPositioned ? "visible" : "hidden"
125
+ },
151
126
  children: /* @__PURE__ */ jsx("div", {
152
127
  className: styles.popup,
153
- children: /* @__PURE__ */ jsx(Menu, {
154
- activeKey,
155
- items: loading ? [{
156
- disabled: true,
157
- key: "loading",
158
- label: "Loading..."
159
- }] : options,
160
- onClick: handleMenuClick,
161
- selectable: false
162
- })
128
+ children: renderedItems
163
129
  })
164
130
  }), portalContainer);
165
131
  };
package/es/react.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { ANCHOR_PADDING_CSS_VAR, DEFAULT_BLOCK_ANCHOR_PADDING } from "./plugins/block/react/style.js";
1
2
  import { ChatInputProps } from "./react/ChatInput/type.js";
2
3
  import { ChatInput } from "./react/ChatInput/ChatInput.js";
3
4
  import { ChatInputActionBarProps } from "./react/ChatInputActionBar/type.js";
@@ -20,4 +21,4 @@ import { SendButtonProps } from "./react/SendButton/type.js";
20
21
  import { SendButton } from "./react/SendButton/SendButton.js";
21
22
  import { SlashMenuProps } from "./react/SlashMenu/type.js";
22
23
  import { SlashMenu } from "./react/SlashMenu/SlashMenu.js";
23
- export { ChatInput, ChatInputActionBar, type ChatInputActionBarProps, type ChatInputActionEvent, ChatInputActions, type ChatInputActionsProps, type ChatInputProps, CodeLanguageSelect, type CodeLanguageSelectProps, Editor, type EditorProps, EditorProvider, type EditorProviderConfig, type EditorProviderProps, type EditorState, FloatActions, type FloatActionsProps, FloatMenu, type FloatMenuProps, SendButton, type SendButtonProps, SlashMenu, type SlashMenuProps, useEditor, useEditorContent, useEditorState, withProps };
24
+ export { ANCHOR_PADDING_CSS_VAR, ChatInput, ChatInputActionBar, type ChatInputActionBarProps, type ChatInputActionEvent, ChatInputActions, type ChatInputActionsProps, type ChatInputProps, CodeLanguageSelect, type CodeLanguageSelectProps, DEFAULT_BLOCK_ANCHOR_PADDING, Editor, type EditorProps, EditorProvider, type EditorProviderConfig, type EditorProviderProps, type EditorState, FloatActions, type FloatActionsProps, FloatMenu, type FloatMenuProps, SendButton, type SendButtonProps, SlashMenu, type SlashMenuProps, useEditor, useEditorContent, useEditorState, withProps };
package/es/react.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { ANCHOR_PADDING_CSS_VAR, DEFAULT_BLOCK_ANCHOR_PADDING } from "./plugins/block/react/style.js";
1
2
  import ChatInput from "./react/ChatInput/ChatInput.js";
2
3
  import ChatInputActionBar from "./react/ChatInputActionBar/ChatInputActionBar.js";
3
4
  import ChatInputActions from "./react/ChatInputActions/ChatInputActions.js";
@@ -11,4 +12,4 @@ import FloatActions from "./react/FloatActions/FloatActions.js";
11
12
  import FloatMenu from "./react/FloatMenu/FloatMenu.js";
12
13
  import SendButton from "./react/SendButton/SendButton.js";
13
14
  import SlashMenu from "./react/SlashMenu/SlashMenu.js";
14
- export { ChatInput, ChatInputActionBar, ChatInputActions, CodeLanguageSelect, Editor, EditorProvider, FloatActions, FloatMenu, SendButton, SlashMenu, useEditor, useEditorContent, useEditorState, withProps };
15
+ export { ANCHOR_PADDING_CSS_VAR, ChatInput, ChatInputActionBar, ChatInputActions, CodeLanguageSelect, DEFAULT_BLOCK_ANCHOR_PADDING, Editor, EditorProvider, FloatActions, FloatMenu, SendButton, SlashMenu, useEditor, useEditorContent, useEditorState, withProps };
@@ -0,0 +1,67 @@
1
+ import * as _$lexical from "lexical";
2
+
3
+ //#region src/plugins/litexml/command/symbols.d.ts
4
+ /**
5
+ * LiteXML command identities.
6
+ *
7
+ * These symbols are intentionally isolated in a side-effect-free module so they
8
+ * keep a SINGLE runtime identity across every entry of this package.
9
+ *
10
+ * Lexical's `dispatchCommand` matches command listeners by object reference, not
11
+ * by the string label. The package ships two independently-bundled entries — the
12
+ * browser build (`index` / `react` / `renderer`) and the node build (`headless`)
13
+ * — and if each entry inlined its own `createCommand(...)` call, dispatching a
14
+ * command obtained from one entry onto an editor registered by the other would
15
+ * silently no-op (different object identities, same label).
16
+ *
17
+ * By keeping every command in this one module — exposed verbatim through
18
+ * `@lobehub/editor/litexml-commands` and emitted as a shared chunk by both
19
+ * builds — a single object backs the command in any runtime, and the module is
20
+ * pure enough to be imported on the server without pulling in the DOM-dependent
21
+ * editor bundle.
22
+ */
23
+ declare enum DiffAction {
24
+ Reject = 0,
25
+ Accept = 1
26
+ }
27
+ declare const LITEXML_MODIFY_COMMAND: _$lexical.LexicalCommand<({
28
+ action: "insert";
29
+ beforeId: string;
30
+ litexml: string;
31
+ } | {
32
+ action: "insert";
33
+ afterId: string;
34
+ litexml: string;
35
+ } | {
36
+ action: "remove";
37
+ id: string;
38
+ } | {
39
+ action: "modify";
40
+ litexml: string | string[];
41
+ })[]>;
42
+ declare const LITEXML_APPLY_COMMAND: _$lexical.LexicalCommand<{
43
+ delay?: boolean;
44
+ litexml: string | string[];
45
+ }>;
46
+ declare const LITEXML_REMOVE_COMMAND: _$lexical.LexicalCommand<{
47
+ delay?: boolean;
48
+ id: string;
49
+ }>;
50
+ declare const LITEXML_INSERT_COMMAND: _$lexical.LexicalCommand<{
51
+ beforeId: string;
52
+ delay?: boolean;
53
+ litexml: string;
54
+ } | {
55
+ afterId: string;
56
+ delay?: boolean;
57
+ litexml: string;
58
+ }>;
59
+ declare const LITEXML_DIFFNODE_COMMAND: _$lexical.LexicalCommand<{
60
+ action: DiffAction;
61
+ nodeKey: string;
62
+ }>;
63
+ declare const LITEXML_DIFFNODE_ALL_COMMAND: _$lexical.LexicalCommand<{
64
+ action: DiffAction;
65
+ }>;
66
+ //#endregion
67
+ export { LITEXML_INSERT_COMMAND as a, LITEXML_DIFFNODE_COMMAND as i, LITEXML_APPLY_COMMAND as n, LITEXML_MODIFY_COMMAND as o, LITEXML_DIFFNODE_ALL_COMMAND as r, LITEXML_REMOVE_COMMAND as s, DiffAction as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/editor",
3
- "version": "4.16.0",
3
+ "version": "4.17.0",
4
4
  "description": "A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.",
5
5
  "keywords": [
6
6
  "lobehub",
@@ -34,6 +34,10 @@
34
34
  "types": "./es/headless.d.ts",
35
35
  "import": "./es/headless.js"
36
36
  },
37
+ "./litexml-commands": {
38
+ "types": "./es/plugins/litexml/command/symbols.d.ts",
39
+ "import": "./es/plugins/litexml/command/symbols.js"
40
+ },
37
41
  "./react": {
38
42
  "types": "./es/react.d.ts",
39
43
  "import": "./es/react.js"