@lobehub/editor 4.16.1 → 4.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/es/headless.js CHANGED
@@ -6852,10 +6852,12 @@ function ensureTableControllerDOM(element) {
6852
6852
  if (legacyPlainToolbar instanceof HTMLElement) {
6853
6853
  legacyPlainToolbar.className = "toolbar-row";
6854
6854
  markTableControllerHost(legacyPlainToolbar, true);
6855
- element.append(legacyPlainToolbar);
6855
+ scrollWrapper.append(legacyPlainToolbar);
6856
6856
  }
6857
+ const legacyOuterRowToolbar = element.querySelector(":scope > .toolbar-row");
6858
+ if (legacyOuterRowToolbar instanceof HTMLElement) scrollWrapper.append(legacyOuterRowToolbar);
6857
6859
  ensureToolbar(scrollWrapper, "toolbar-col", true);
6858
- ensureToolbar(element, "toolbar-row", true);
6860
+ ensureToolbar(scrollWrapper, "toolbar-row", true);
6859
6861
  ensureTableScrollIndicators(scrollWrapper);
6860
6862
  }
6861
6863
  function reconcileTableDecorator(editor, node, decorator) {
package/es/index.d.ts CHANGED
@@ -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";
@@ -121,4 +122,4 @@ declare function enableHotReload(): void;
121
122
  */
122
123
  declare function disableHotReload(): void;
123
124
  //#endregion
124
- 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
@@ -52,6 +52,7 @@ import { AutoCompletePlugin } from "./plugins/auto-complete/plugin/index.js";
52
52
  import ReactAutoCompletePlugin from "./plugins/auto-complete/react/ReactAutoCompletePlugin.js";
53
53
  import { MOVE_BLOCK_COMMAND, registerBlockMoveCommand } from "./plugins/block/command/index.js";
54
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";
55
56
  import ReactBlockPlugin from "./plugins/block/react/ReactBlockPlugin.js";
56
57
  import ReactCodePlugin from "./plugins/code/react/CodeReactPlugin.js";
57
58
  import { UPDATE_CODEBLOCK_LANG } from "./plugins/codeblock/command/index.js";
@@ -126,4 +127,4 @@ function disableHotReload() {
126
127
  }
127
128
  }
128
129
  //#endregion
129
- 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;
@@ -32,9 +40,6 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
32
40
  pointer-events: auto;
33
41
  position: fixed;
34
42
  z-index: 100;
35
- transition:
36
- inset-inline-start 200ms ease,
37
- inset-block-start 200ms ease;
38
43
  `,
39
44
  menuInner: css`
40
45
  display: flex;
@@ -59,8 +64,8 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
59
64
  }
60
65
  `,
61
66
  root: css`
62
- padding-inline: 54px 54px;
67
+ padding-inline: var(${ANCHOR_PADDING_CSS_VAR}, ${54}px);
63
68
  `
64
69
  }));
65
70
  //#endregion
66
- export { styles };
71
+ 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);
@@ -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
  };
@@ -86,10 +86,12 @@ function ensureTableControllerDOM(element) {
86
86
  if (legacyPlainToolbar instanceof HTMLElement) {
87
87
  legacyPlainToolbar.className = "toolbar-row";
88
88
  markTableControllerHost(legacyPlainToolbar, true);
89
- element.append(legacyPlainToolbar);
89
+ scrollWrapper.append(legacyPlainToolbar);
90
90
  }
91
+ const legacyOuterRowToolbar = element.querySelector(":scope > .toolbar-row");
92
+ if (legacyOuterRowToolbar instanceof HTMLElement) scrollWrapper.append(legacyOuterRowToolbar);
91
93
  ensureToolbar(scrollWrapper, "toolbar-col", true);
92
- ensureToolbar(element, "toolbar-row", true);
94
+ ensureToolbar(scrollWrapper, "toolbar-row", true);
93
95
  ensureTableScrollIndicators(scrollWrapper);
94
96
  }
95
97
  function reconcileTableDecorator(editor, node, decorator) {
@@ -1,6 +1,6 @@
1
1
  import { useTranslation } from "../../../../editor-kernel/react/useTranslation.js";
2
2
  import { $canUnmerge, $selectLastDescendant, computeSelectionCount, currentCellBackgroundColor } from "./utils.js";
3
- import { Dropdown } from "@lobehub/ui";
3
+ import { DropdownMenu } from "@lobehub/ui";
4
4
  import { Grid2X2XIcon, PanelBottomCloseIcon, PanelLeftCloseIcon, PanelRightCloseIcon, PanelTopCloseIcon, TableColumnsSplitIcon, TableRowsSplitIcon } from "lucide-react";
5
5
  import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
6
6
  import { jsx } from "react/jsx-runtime";
@@ -221,8 +221,8 @@ const TableActionMenu = memo(({ editor, tableCellNode: _tableCellNode, cellMerge
221
221
  tableCellNode,
222
222
  clearTableSelection
223
223
  ]);
224
- return /* @__PURE__ */ jsx(Dropdown, {
225
- menu: { items: useMemo(() => {
224
+ return /* @__PURE__ */ jsx(DropdownMenu, {
225
+ items: useMemo(() => {
226
226
  return [
227
227
  {
228
228
  icon: PanelTopCloseIcon,
@@ -287,7 +287,7 @@ const TableActionMenu = memo(({ editor, tableCellNode: _tableCellNode, cellMerge
287
287
  renderDeleteMenuLabel,
288
288
  toggleTableRowIsHeader,
289
289
  toggleTableColumnIsHeader
290
- ]) },
290
+ ]),
291
291
  children
292
292
  });
293
293
  });
@@ -9,6 +9,7 @@ const styles = createStaticStyles(({ css, cssVar }) => css`
9
9
  .lobe-editor-table-scroll-wrapper {
10
10
  position: relative;
11
11
  overflow: auto visible;
12
+ margin-inline: calc(var(--lobe-block-anchor-padding, 54px) * -1);
12
13
  padding-block-start: 14px;
13
14
  }
14
15
 
@@ -62,6 +63,11 @@ const styles = createStaticStyles(({ css, cssVar }) => css`
62
63
  height: 0;
63
64
  }
64
65
 
66
+ .toolbar-col,
67
+ .toolbar-row {
68
+ inset-inline-start: var(--lobe-block-anchor-padding, 54px);
69
+ }
70
+
65
71
  .table-controller,
66
72
  .table-controller-col,
67
73
  .table-controller-row {
@@ -83,6 +89,7 @@ const styles = createStaticStyles(({ css, cssVar }) => css`
83
89
  border-collapse: collapse;
84
90
 
85
91
  width: fit-content;
92
+ margin-inline: var(--lobe-block-anchor-padding, 54px);
86
93
 
87
94
  text-align: start;
88
95
  text-indent: initial;
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/editor",
3
- "version": "4.16.1",
3
+ "version": "4.17.1",
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",