@lobehub/editor 4.17.2 → 4.18.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.
Files changed (51) hide show
  1. package/es/headless/index.js +12 -2
  2. package/es/headless.js +3119 -1224
  3. package/es/index.d.ts +1 -1
  4. package/es/index.js +13 -13
  5. package/es/plugins/codeblock/command/index.d.ts +2 -8
  6. package/es/plugins/codeblock/command/index.js +3 -3
  7. package/es/plugins/codeblock/command/symbols.d.ts +8 -0
  8. package/es/plugins/codeblock/command/symbols.js +5 -0
  9. package/es/plugins/codeblock/index.d.ts +1 -1
  10. package/es/plugins/codeblock/index.js +1 -1
  11. package/es/plugins/codeblock/plugin/FacadeShiki.js +1 -1
  12. package/es/plugins/codeblock/plugin/index.js +1 -1
  13. package/es/plugins/codemirror-block/command/index.js +1 -1
  14. package/es/plugins/codemirror-block/node/CodeMirrorNode.js +10 -1
  15. package/es/plugins/codemirror-block/plugin/index.d.ts +1 -1
  16. package/es/plugins/file/plugin/index.d.ts +2 -2
  17. package/es/plugins/file/plugin/index.js +26 -23
  18. package/es/plugins/image/node/block-image-node.d.ts +1 -0
  19. package/es/plugins/image/node/block-image-node.js +6 -0
  20. package/es/plugins/image/node/image-node.d.ts +1 -0
  21. package/es/plugins/image/node/image-node.js +6 -0
  22. package/es/plugins/image/plugin/index.d.ts +3 -3
  23. package/es/plugins/image/plugin/index.js +5 -23
  24. package/es/plugins/image/react/ReactImagePlugin.js +18 -0
  25. package/es/plugins/image/react/components/Image.js +1 -1
  26. package/es/plugins/math/plugin/index.d.ts +1 -1
  27. package/es/plugins/math/react/components/MathEditor.js +1 -1
  28. package/es/plugins/math/react/components/MathInline.js +1 -1
  29. package/es/plugins/mention/plugin/index.d.ts +1 -1
  30. package/es/plugins/mention/plugin/index.js +1 -1
  31. package/es/plugins/slash/react/components/DefaultSlashMenu.js +111 -53
  32. package/es/plugins/slash/react/type.d.ts +5 -2
  33. package/es/react/ChatInput/ChatInput.js +1 -1
  34. package/es/react/ChatInput/type.d.ts +5 -0
  35. package/es/react/hooks/useEditorState/index.js +2 -2
  36. package/es/react.d.ts +1 -5
  37. package/es/react.js +1 -3
  38. package/es/renderer/engine/shiki.js +1 -1
  39. package/es/renderer/nodes/index.js +4 -4
  40. package/es/renderer/renderers/codeblock.js +1 -0
  41. package/package.json +1 -1
  42. package/es/headless/plugins/codeblock.js +0 -82
  43. package/es/react/FloatMenu/FloatMenu.d.ts +0 -7
  44. package/es/react/FloatMenu/FloatMenu.js +0 -32
  45. package/es/react/FloatMenu/index.d.ts +0 -2
  46. package/es/react/FloatMenu/style.js +0 -28
  47. package/es/react/FloatMenu/type.d.ts +0 -25
  48. package/es/react/SlashMenu/SlashMenu.d.ts +0 -7
  49. package/es/react/SlashMenu/SlashMenu.js +0 -40
  50. package/es/react/SlashMenu/index.d.ts +0 -2
  51. package/es/react/SlashMenu/type.d.ts +0 -17
@@ -4,7 +4,7 @@ import { useEffect, useLayoutEffect, useMemo, useRef } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { createStaticStyles } from "antd-style";
6
6
  import { createPortal } from "react-dom";
7
- import { flip, offset, shift, useFloating } from "@floating-ui/react";
7
+ import { autoUpdate, flip, offset, shift, size, 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 }) => ({
@@ -13,7 +13,6 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
13
13
 
14
14
  overflow-y: auto;
15
15
 
16
- min-width: 200px;
17
16
  max-height: min(50vh, 400px);
18
17
  padding: 4px;
19
18
  border-radius: ${cssVar.borderRadius};
@@ -24,14 +23,92 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
24
23
  0 0 15px 0 #00000008,
25
24
  0 2px 30px 0 #00000014,
26
25
  0 0 0 1px ${cssVar.colorBorder} inset;
26
+ `,
27
+ popupCaret: css`
28
+ width: max-content;
29
+ min-width: 200px;
27
30
  `,
28
31
  root: css`
29
32
  z-index: 1100;
30
- width: max-content;
31
33
  `
32
34
  }));
33
35
  const isDividerOption = (option) => "type" in option && option.type === "divider";
34
- const DefaultSlashMenu = ({ activeKey, getPopupContainer, loading, onSelect, open, options, placement: forcePlacement, position }) => {
36
+ const renderItems = (options, activeKey, loading, onSelect) => {
37
+ if (loading) return /* @__PURE__ */ jsx("div", {
38
+ className: menuSharedStyles.empty,
39
+ children: "Loading..."
40
+ });
41
+ return options.map((opt, index) => {
42
+ if (isDividerOption(opt)) return /* @__PURE__ */ jsx("div", { className: menuSharedStyles.separator }, `__divider_${index}`);
43
+ const item = opt;
44
+ const isHighlighted = item.key === activeKey;
45
+ const isDisabled = Boolean(item.disabled);
46
+ return /* @__PURE__ */ jsxs("div", {
47
+ "aria-disabled": isDisabled || void 0,
48
+ className: menuSharedStyles.item,
49
+ "data-disabled": isDisabled ? "" : void 0,
50
+ "data-highlighted": isHighlighted ? "" : void 0,
51
+ onClick: () => {
52
+ if (isDisabled) return;
53
+ onSelect(item);
54
+ },
55
+ onMouseDown: (event) => event.preventDefault(),
56
+ role: "menuitem",
57
+ children: [
58
+ item.icon ? /* @__PURE__ */ jsx("span", {
59
+ className: menuSharedStyles.icon,
60
+ children: /* @__PURE__ */ jsx(Icon, { icon: item.icon })
61
+ }) : null,
62
+ /* @__PURE__ */ jsx("span", {
63
+ className: menuSharedStyles.label,
64
+ children: item.label
65
+ }),
66
+ item.extra ? /* @__PURE__ */ jsx("span", {
67
+ className: menuSharedStyles.extra,
68
+ children: item.extra
69
+ }) : null
70
+ ]
71
+ }, String(item.key));
72
+ });
73
+ };
74
+ const resolvePortalContainer = () => {
75
+ if (typeof document === "undefined") return null;
76
+ return document.getElementById(LOBE_THEME_APP_ID$1) ?? document.body;
77
+ };
78
+ const FullWidthSlashMenu = ({ activeKey, anchor, loading, onSelect, open, options, placement }) => {
79
+ const resolvedPlacement = placement === "bottom" ? "bottom-start" : "top-start";
80
+ const { refs, floatingStyles, isPositioned } = useFloating({
81
+ elements: { reference: anchor },
82
+ middleware: [
83
+ offset(8),
84
+ size({ apply({ rects, elements }) {
85
+ elements.floating.style.width = `${rects.reference.width}px`;
86
+ } }),
87
+ flip({ fallbackPlacements: placement === "bottom" ? ["top-start"] : ["bottom-start"] }),
88
+ shift({ padding: 8 })
89
+ ],
90
+ open,
91
+ placement: resolvedPlacement,
92
+ strategy: "fixed",
93
+ whileElementsMounted: autoUpdate
94
+ });
95
+ const portalContainer = resolvePortalContainer();
96
+ if (!portalContainer) return null;
97
+ return createPortal(/* @__PURE__ */ jsx("div", {
98
+ className: styles.root,
99
+ "data-resolved-placement": resolvedPlacement,
100
+ ref: refs.setFloating,
101
+ style: {
102
+ ...floatingStyles,
103
+ visibility: isPositioned ? "visible" : "hidden"
104
+ },
105
+ children: /* @__PURE__ */ jsx("div", {
106
+ className: styles.popup,
107
+ children: renderItems(options, activeKey, loading, onSelect)
108
+ })
109
+ }), portalContainer);
110
+ };
111
+ const CaretSlashMenu = ({ activeKey, loading, onSelect, open, options, placement: forcePlacement, position }) => {
35
112
  const resolvedPlacement = forcePlacement ? `${forcePlacement}-start` : "top-start";
36
113
  const middleware = useMemo(() => [
37
114
  offset(8),
@@ -62,62 +139,19 @@ const DefaultSlashMenu = ({ activeKey, getPopupContainer, loading, onSelect, ope
62
139
  useEffect(() => {
63
140
  if (!open) return;
64
141
  const onScroll = () => update();
65
- const container = getPopupContainer?.();
66
142
  window.addEventListener("scroll", onScroll, {
67
143
  capture: true,
68
144
  passive: true
69
145
  });
70
- if (container) container.addEventListener("scroll", onScroll, { passive: true });
71
146
  return () => {
72
147
  window.removeEventListener("scroll", onScroll, { capture: true });
73
- if (container) container.removeEventListener("scroll", onScroll);
74
148
  };
75
- }, [
76
- open,
77
- getPopupContainer,
78
- update
79
- ]);
80
- const hasVisibleItems = options?.some((item) => !isDividerOption(item));
81
- if (!open || !hasVisibleItems) return null;
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
- });
149
+ }, [open, update]);
150
+ const portalContainer = resolvePortalContainer();
151
+ if (!portalContainer) return null;
118
152
  return createPortal(/* @__PURE__ */ jsx("div", {
119
- className: styles.root,
120
- "data-resloved-placement": resolvedPlacement,
153
+ className: `${styles.root} ${styles.popupCaret}`,
154
+ "data-resolved-placement": resolvedPlacement,
121
155
  ref: refs.setFloating,
122
156
  style: {
123
157
  ...floatingStyles,
@@ -125,10 +159,34 @@ const DefaultSlashMenu = ({ activeKey, getPopupContainer, loading, onSelect, ope
125
159
  },
126
160
  children: /* @__PURE__ */ jsx("div", {
127
161
  className: styles.popup,
128
- children: renderedItems
162
+ children: renderItems(options, activeKey, loading, onSelect)
129
163
  })
130
164
  }), portalContainer);
131
165
  };
166
+ const DefaultSlashMenu = ({ activeKey, getPopupContainer, loading, onSelect, open, options, placement, position }) => {
167
+ const hasVisibleItems = options?.some((item) => !isDividerOption(item));
168
+ if (!open || !hasVisibleItems) return null;
169
+ const anchor = getPopupContainer?.() ?? null;
170
+ if (anchor) return /* @__PURE__ */ jsx(FullWidthSlashMenu, {
171
+ activeKey,
172
+ anchor,
173
+ loading,
174
+ onSelect,
175
+ open,
176
+ options,
177
+ placement: placement ?? "top"
178
+ });
179
+ if (!position) return null;
180
+ return /* @__PURE__ */ jsx(CaretSlashMenu, {
181
+ activeKey,
182
+ loading,
183
+ onSelect,
184
+ open,
185
+ options,
186
+ placement,
187
+ position
188
+ });
189
+ };
132
190
  DefaultSlashMenu.displayName = "DefaultSlashMenu";
133
191
  //#endregion
134
192
  export { DefaultSlashMenu as default };
@@ -94,8 +94,11 @@ interface SlashMenuProps {
94
94
  options: Array<ISlashOption>;
95
95
  /** Force menu placement direction, skipping auto-flip detection */
96
96
  placement?: 'bottom' | 'top';
97
- /** Menu position */
98
- position: {
97
+ /**
98
+ * Caret position used by the floating fallback when no getPopupContainer
99
+ * is provided. Ignored in full-width mode.
100
+ */
101
+ position?: {
99
102
  getRect?: () => DOMRect;
100
103
  rect?: DOMRect;
101
104
  x: number;
@@ -38,11 +38,11 @@ const ChatInput = (props) => {
38
38
  return /* @__PURE__ */ jsxs(Flexbox, {
39
39
  className: cx(isDarkMode ? styles.containerDark : styles.containerLight, styles.root, className),
40
40
  height: fullscreen ? "100%" : void 0,
41
+ ref: slashMenuRef,
41
42
  style,
42
43
  width: "100%",
43
44
  ...rest,
44
45
  children: [
45
- slashMenuRef && /* @__PURE__ */ jsx("div", { ref: slashMenuRef }),
46
46
  /* @__PURE__ */ jsx("div", {
47
47
  className: cx(styles.header, classNames?.header),
48
48
  ref: headerRef,
@@ -21,6 +21,11 @@ interface ChatInputProps extends Omit<FlexboxProps, 'height'> {
21
21
  resize?: boolean;
22
22
  resizeMaxHeightOffset?: number;
23
23
  showResizeHandle?: boolean;
24
+ /**
25
+ * Ref attached to the ChatInput root element. Pass the same ref to
26
+ * `<Editor getPopupContainer={() => ref.current}>` to anchor the slash /
27
+ * mention menu to the ChatInput's outer edges (full-width).
28
+ */
24
29
  slashMenuRef?: Ref<HTMLDivElement>;
25
30
  styles?: {
26
31
  body?: CSSProperties;
@@ -3,13 +3,13 @@ import { noop } from "../../../editor-kernel/utils.js";
3
3
  import { $createCodeMirrorNode, $isCodeMirrorNode } from "../../../plugins/codemirror-block/node/CodeMirrorNode.js";
4
4
  import { $isSelectionInCodeInline } from "../../../plugins/code/node/code.js";
5
5
  import { INSERT_CODEINLINE_COMMAND } from "../../../plugins/code/command/index.js";
6
+ import { UPDATE_CODEBLOCK_LANG } from "../../../plugins/codeblock/command/symbols.js";
6
7
  import { $isLinkNode, TOGGLE_LINK_COMMAND, formatUrl } from "../../../plugins/link/node/LinkNode.js";
7
8
  import { extractUrlFromText, sanitizeUrl, validateUrl } from "../../../plugins/link/utils/index.js";
8
9
  import { INSERT_CHECK_LIST_COMMAND } from "../../../plugins/list/plugin/checkList.js";
9
- import { UPDATE_CODEBLOCK_LANG } from "../../../plugins/codeblock/command/index.js";
10
+ import { $createMathBlockNode, $createMathInlineNode } from "../../../plugins/math/node/index.js";
10
11
  import { $isLinkHighlightNode } from "../../../plugins/link-highlight/node/link-highlight.js";
11
12
  import { INSERT_LINK_HIGHLIGHT_COMMAND } from "../../../plugins/link-highlight/command/index.js";
12
- import { $createMathBlockNode, $createMathInlineNode } from "../../../plugins/math/node/index.js";
13
13
  import { $findTopLevelElement, formatParagraph, getSelectedNode } from "./utils.js";
14
14
  import { useCallback, useEffect, useMemo, useState } from "react";
15
15
  import { $createNodeSelection, $getSelection, $isParagraphNode, $isRangeSelection, $setSelection, CAN_REDO_COMMAND, CAN_UNDO_COMMAND, COMMAND_PRIORITY_LOW, FORMAT_TEXT_COMMAND, REDO_COMMAND, SELECTION_CHANGE_COMMAND, UNDO_COMMAND } from "lexical";
package/es/react.d.ts CHANGED
@@ -15,10 +15,6 @@ import { Editor } from "./react/Editor/index.js";
15
15
  import { EditorProvider, EditorProviderConfig, EditorProviderProps, useEditorContent } from "./react/EditorProvider/index.js";
16
16
  import { FloatActionsProps } from "./react/FloatActions/type.js";
17
17
  import { FloatActions } from "./react/FloatActions/FloatActions.js";
18
- import { FloatMenuProps } from "./react/FloatMenu/type.js";
19
- import { FloatMenu } from "./react/FloatMenu/FloatMenu.js";
20
18
  import { SendButtonProps } from "./react/SendButton/type.js";
21
19
  import { SendButton } from "./react/SendButton/SendButton.js";
22
- import { SlashMenuProps } from "./react/SlashMenu/type.js";
23
- import { SlashMenu } from "./react/SlashMenu/SlashMenu.js";
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 };
20
+ 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, SendButton, type SendButtonProps, useEditor, useEditorContent, useEditorState, withProps };
package/es/react.js CHANGED
@@ -9,7 +9,5 @@ import { EditorProvider, useEditorContent } from "./react/EditorProvider/index.j
9
9
  import { withProps } from "./react/Editor/utils.js";
10
10
  import Editor from "./react/Editor/index.js";
11
11
  import FloatActions from "./react/FloatActions/FloatActions.js";
12
- import FloatMenu from "./react/FloatMenu/FloatMenu.js";
13
12
  import SendButton from "./react/SendButton/SendButton.js";
14
- import SlashMenu from "./react/SlashMenu/SlashMenu.js";
15
- export { ANCHOR_PADDING_CSS_VAR, ChatInput, ChatInputActionBar, ChatInputActions, CodeLanguageSelect, DEFAULT_BLOCK_ANCHOR_PADDING, Editor, EditorProvider, FloatActions, FloatMenu, SendButton, SlashMenu, useEditor, useEditorContent, useEditorState, withProps };
13
+ export { ANCHOR_PADDING_CSS_VAR, ChatInput, ChatInputActionBar, ChatInputActions, CodeLanguageSelect, DEFAULT_BLOCK_ANCHOR_PADDING, Editor, EditorProvider, FloatActions, SendButton, useEditor, useEditorContent, useEditorState, withProps };
@@ -1,6 +1,6 @@
1
- import { bundledLanguagesInfo } from "shiki";
2
1
  import { createHighlighterCoreSync, isSpecialLang } from "@shikijs/core";
3
2
  import { createJavaScriptRegexEngine } from "@shikijs/engine-javascript";
3
+ import { bundledLanguagesInfo } from "shiki";
4
4
  //#region src/renderer/engine/shiki.ts
5
5
  let _highlighter = null;
6
6
  function getHighlighter() {
@@ -2,15 +2,15 @@ import { CursorNode } from "../../plugins/common/node/cursor.js";
2
2
  import { DiffNode } from "../../plugins/litexml/node/DiffNode.js";
3
3
  import { CodeMirrorNode } from "../../plugins/codemirror-block/node/CodeMirrorNode.js";
4
4
  import { CodeNode } from "../../plugins/code/node/code.js";
5
- import { HorizontalRuleNode } from "../../plugins/hr/node/HorizontalRuleNode.js";
6
- import { AutoLinkNode, LinkNode } from "../../plugins/link/node/LinkNode.js";
7
- import { PlaceholderBlockNode, PlaceholderNode } from "../../plugins/auto-complete/node/placeholderNode.js";
8
5
  import { FileNode } from "../../plugins/file/node/FileNode.js";
6
+ import { HorizontalRuleNode } from "../../plugins/hr/node/HorizontalRuleNode.js";
9
7
  import { BlockImageNode } from "../../plugins/image/node/block-image-node.js";
10
8
  import { ImageNode } from "../../plugins/image/node/image-node.js";
11
- import { LinkHighlightNode } from "../../plugins/link-highlight/node/link-highlight.js";
9
+ import { AutoLinkNode, LinkNode } from "../../plugins/link/node/LinkNode.js";
12
10
  import { MathBlockNode, MathInlineNode } from "../../plugins/math/node/index.js";
13
11
  import { MentionNode } from "../../plugins/mention/node/MentionNode.js";
12
+ import { PlaceholderBlockNode, PlaceholderNode } from "../../plugins/auto-complete/node/placeholderNode.js";
13
+ import { LinkHighlightNode } from "../../plugins/link-highlight/node/link-highlight.js";
14
14
  import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";
15
15
  import { HeadingNode, QuoteNode } from "@lexical/rich-text";
16
16
  import { ListItemNode, ListNode } from "@lexical/list";
@@ -10,6 +10,7 @@ function CodeBlockRenderer({ node }) {
10
10
  defaultExpand: true,
11
11
  language: language || "text",
12
12
  variant: "filled",
13
+ wrap: true,
13
14
  children: code
14
15
  });
15
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/editor",
3
- "version": "4.17.2",
3
+ "version": "4.18.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",
@@ -1,82 +0,0 @@
1
- import { INodeHelper, init_helper } from "../../editor-kernel/inode/helper.js";
2
- import { KernelPlugin, init_plugin } from "../../editor-kernel/plugin.js";
3
- import { ILitexmlService } from "../../plugins/litexml/service/litexml-service.js";
4
- import { IMarkdownShortCutService } from "../../plugins/markdown/service/shortcut.js";
5
- import { getCodeLanguageByInput } from "../../plugins/codeblock/utils/language.js";
6
- import { TabNode } from "lexical";
7
- import { $isCodeHighlightNode, $isCodeNode, CodeHighlightNode, CodeNode } from "@lexical/code-core";
8
- //#region src/headless/plugins/codeblock.ts
9
- init_helper();
10
- init_plugin();
11
- const createCodeChildren = (code) => code.split("\n").flatMap((text, index, array) => {
12
- const textNode = INodeHelper.createTextNode(text);
13
- textNode.type = "code-highlight";
14
- if (index === array.length - 1) return textNode;
15
- return [textNode, {
16
- type: "linebreak",
17
- version: 1
18
- }];
19
- }).flat();
20
- const HeadlessCodeblockPlugin = class extends KernelPlugin {
21
- static {
22
- this.pluginName = "HeadlessCodeblockPlugin";
23
- }
24
- constructor(kernel) {
25
- super();
26
- this.kernel = kernel;
27
- kernel.registerNodes([CodeNode, CodeHighlightNode]);
28
- kernel.registerThemes({ code: "editor-code" });
29
- }
30
- onInit() {
31
- this.registerMarkdown();
32
- this.registerLiteXml();
33
- }
34
- registerLiteXml() {
35
- const litexmlService = this.kernel.requireService(ILitexmlService);
36
- if (!litexmlService) return;
37
- litexmlService.registerXMLWriter(CodeNode.getType(), (node, ctx) => {
38
- const codeNode = node;
39
- return ctx.createXmlNode("code", { lang: codeNode.getLanguage() || "plaintext" }, codeNode.getTextContent());
40
- });
41
- litexmlService.registerXMLReader("code", (xmlElement, children) => {
42
- const text = children.map((value) => value.text || "").join("");
43
- return INodeHelper.createElementNode(CodeNode.getType(), {
44
- children: createCodeChildren(text),
45
- direction: "ltr",
46
- format: "",
47
- indent: 0,
48
- language: xmlElement.getAttribute("lang"),
49
- version: 1
50
- });
51
- });
52
- }
53
- registerMarkdown() {
54
- const markdownService = this.kernel.requireService(IMarkdownShortCutService);
55
- if (!markdownService) return;
56
- markdownService.registerMarkdownWriter(CodeNode.getType(), (ctx, node) => {
57
- if ($isCodeNode(node)) ctx.wrap("```" + (node.getLanguage() || "") + "\n", "\n```\n");
58
- });
59
- markdownService.registerMarkdownWriter(TabNode.getType(), (ctx) => {
60
- ctx.appendLine(" ");
61
- });
62
- markdownService.registerMarkdownWriter(CodeHighlightNode.getType(), (ctx, node) => {
63
- if ($isCodeHighlightNode(node)) ctx.appendLine(node.getTextContent());
64
- });
65
- markdownService.registerMarkdownWriter("linebreak", (ctx, node) => {
66
- if ($isCodeNode(node.getParent())) ctx.appendLine("\n");
67
- });
68
- markdownService.registerMarkdownReader("code", (node) => {
69
- const language = node.lang ? getCodeLanguageByInput(node.lang) : "plaintext";
70
- return INodeHelper.createElementNode("code", {
71
- children: createCodeChildren(node.value),
72
- direction: "ltr",
73
- format: "",
74
- indent: 0,
75
- language,
76
- version: 1
77
- });
78
- });
79
- }
80
- };
81
- //#endregion
82
- export { HeadlessCodeblockPlugin };
@@ -1,7 +0,0 @@
1
- import { FloatMenuProps } from "./type.js";
2
- import { FC } from "react";
3
-
4
- //#region src/react/FloatMenu/FloatMenu.d.ts
5
- declare const FloatMenu: FC<FloatMenuProps>;
6
- //#endregion
7
- export { FloatMenu };
@@ -1,32 +0,0 @@
1
- "use client";
2
- import { styles } from "./style.js";
3
- import { Block, Flexbox } from "@lobehub/ui";
4
- import { jsx } from "react/jsx-runtime";
5
- import { cx } from "antd-style";
6
- import { createPortal } from "react-dom";
7
- //#region src/react/FloatMenu/FloatMenu.tsx
8
- const FloatMenu = ({ className, style, getPopupContainer, children, maxHeight = "min(50vh, 640px)", open, placement = "top", styles: customStyles, classNames }) => {
9
- const parent = getPopupContainer();
10
- if (!parent) return;
11
- if (!open) return;
12
- return createPortal(/* @__PURE__ */ jsx(Flexbox, {
13
- className: cx(placement === "bottom" ? styles.rootBottom : styles.rootTop, classNames?.root),
14
- paddingInline: 8,
15
- style: customStyles?.root,
16
- width: "100%",
17
- children: /* @__PURE__ */ jsx(Block, {
18
- className: cx(styles.container, className, classNames?.container),
19
- shadow: true,
20
- style: {
21
- maxHeight,
22
- ...style,
23
- ...customStyles?.container
24
- },
25
- variant: "outlined",
26
- children
27
- })
28
- }), parent);
29
- };
30
- FloatMenu.displayName = "FloatMenu";
31
- //#endregion
32
- export { FloatMenu as default };
@@ -1,2 +0,0 @@
1
- import { FloatMenuProps } from "./type.js";
2
- import { FloatMenu } from "./FloatMenu.js";
@@ -1,28 +0,0 @@
1
- import { createStaticStyles } from "antd-style";
2
- //#region src/react/FloatMenu/style.ts
3
- const styles = createStaticStyles(({ css, cssVar }) => ({
4
- container: css`
5
- position: relative;
6
- overflow: hidden auto;
7
- background: ${cssVar.colorBgElevated};
8
- `,
9
- containerWithMaxHeight: css`
10
- /* maxHeight is set via inline style as it's dynamic */
11
- `,
12
- rootBottom: css`
13
- position: absolute;
14
- z-index: 9999;
15
- inset-block-start: 100%;
16
- inset-inline-start: 0;
17
-
18
- padding-block-start: 8px;
19
- `,
20
- rootTop: css`
21
- position: absolute;
22
- inset-block-start: -8px;
23
- inset-inline-start: 0;
24
- transform: translateY(-100%);
25
- `
26
- }));
27
- //#endregion
28
- export { styles };
@@ -1,25 +0,0 @@
1
- import { BlockProps } from "@lobehub/ui";
2
- import { CSSProperties, ReactNode } from "react";
3
-
4
- //#region src/react/FloatMenu/type.d.ts
5
- interface FloatMenuProps {
6
- children?: ReactNode;
7
- className?: string;
8
- classNames?: {
9
- container?: string;
10
- root?: string;
11
- };
12
- containerProps?: Omit<BlockProps, 'children'>;
13
- getPopupContainer: () => HTMLDivElement | null;
14
- maxHeight?: string | number;
15
- open?: boolean;
16
- /** Menu placement direction: 'top' (default) or 'bottom' */
17
- placement?: 'bottom' | 'top';
18
- style?: CSSProperties;
19
- styles?: {
20
- container?: CSSProperties;
21
- root?: CSSProperties;
22
- };
23
- }
24
- //#endregion
25
- export { FloatMenuProps };
@@ -1,7 +0,0 @@
1
- import { SlashMenuProps } from "./type.js";
2
- import { FC } from "react";
3
-
4
- //#region src/react/SlashMenu/SlashMenu.d.ts
5
- declare const SlashMenu: FC<SlashMenuProps>;
6
- //#endregion
7
- export { SlashMenu };
@@ -1,40 +0,0 @@
1
- "use client";
2
- import FloatMenu from "../FloatMenu/FloatMenu.js";
3
- import { Menu } from "@lobehub/ui";
4
- import { useCallback } from "react";
5
- import { jsx } from "react/jsx-runtime";
6
- //#region src/react/SlashMenu/SlashMenu.tsx
7
- const SlashMenu = ({ options, activeKey, loading, onSelect, classNames, styles: customStyles, menuProps, ...floatMenuProps }) => {
8
- const handleMenuClick = useCallback(({ key }) => {
9
- if (!onSelect) return;
10
- const option = options.find((item) => "key" in item && item.key === key);
11
- if (option) onSelect?.(option);
12
- }, [options, onSelect]);
13
- return /* @__PURE__ */ jsx(FloatMenu, {
14
- classNames: {
15
- container: classNames?.container,
16
- root: classNames?.root
17
- },
18
- styles: {
19
- container: customStyles?.container,
20
- root: customStyles?.root
21
- },
22
- ...floatMenuProps,
23
- children: /* @__PURE__ */ jsx(Menu, {
24
- className: classNames?.menu,
25
- items: loading ? [{
26
- disabled: true,
27
- key: "loading",
28
- label: "Loading..."
29
- }] : options,
30
- mode: "inline",
31
- onClick: handleMenuClick,
32
- selectedKeys: activeKey ? [activeKey] : void 0,
33
- style: customStyles?.menu,
34
- ...menuProps
35
- })
36
- });
37
- };
38
- SlashMenu.displayName = "SlashMenu";
39
- //#endregion
40
- export { SlashMenu as default };
@@ -1,2 +0,0 @@
1
- import { SlashMenuProps } from "./type.js";
2
- import { SlashMenu } from "./SlashMenu.js";
@@ -1,17 +0,0 @@
1
- import { MenuRenderProps } from "../../plugins/slash/react/type.js";
2
- import { FloatMenuProps } from "../FloatMenu/type.js";
3
- import { MenuProps } from "@lobehub/ui";
4
- import { CSSProperties } from "react";
5
-
6
- //#region src/react/SlashMenu/type.d.ts
7
- interface SlashMenuProps extends MenuRenderProps, Omit<FloatMenuProps, 'children'> {
8
- classNames?: FloatMenuProps['classNames'] & {
9
- menu?: string;
10
- };
11
- menuProps?: MenuProps;
12
- styles?: FloatMenuProps['styles'] & {
13
- menu?: CSSProperties;
14
- };
15
- }
16
- //#endregion
17
- export { SlashMenuProps };