@blocklet/editor 2.1.39 → 2.1.41

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.
@@ -0,0 +1,13 @@
1
+ import { LexicalEditor } from 'lexical';
2
+ import { ReactNode } from 'react';
3
+ interface ContextValue {
4
+ editor: LexicalEditor | null;
5
+ setEditor: (editor: LexicalEditor | null) => void;
6
+ }
7
+ export declare const EditorHolderContext: import("react").Context<ContextValue | null>;
8
+ export declare function useEditorHolder(): ContextValue | null;
9
+ export declare function EditorHolderProvider({ children }: {
10
+ children: ReactNode;
11
+ }): import("react/jsx-runtime").JSX.Element;
12
+ export declare function useEditor(): LexicalEditor | null;
13
+ export {};
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext, useMemo, useState } from 'react';
3
+ export const EditorHolderContext = createContext(null);
4
+ export function useEditorHolder() {
5
+ return useContext(EditorHolderContext);
6
+ }
7
+ export function EditorHolderProvider({ children }) {
8
+ const [editor, setEditor] = useState(null);
9
+ const value = useMemo(() => ({ editor, setEditor }), [editor]);
10
+ return _jsx(EditorHolderContext.Provider, { value: value, children: children });
11
+ }
12
+ export function useEditor() {
13
+ const holder = useEditorHolder();
14
+ return holder?.editor ?? null;
15
+ }
@@ -0,0 +1 @@
1
+ export declare function EditorHolderPlugin(): null;
@@ -0,0 +1,11 @@
1
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
2
+ import { useEffect } from 'react';
3
+ import { useEditorHolder } from './EditorHolderContext';
4
+ export function EditorHolderPlugin() {
5
+ const [editor] = useLexicalComposerContext();
6
+ const holder = useEditorHolder();
7
+ useEffect(() => {
8
+ holder?.setEditor(editor);
9
+ }, [editor]);
10
+ return null;
11
+ }
@@ -0,0 +1,2 @@
1
+ export * from './EditorHolderPlugin';
2
+ export * from './EditorHolderContext';
@@ -0,0 +1,2 @@
1
+ export * from './EditorHolderPlugin';
2
+ export * from './EditorHolderContext';
@@ -0,0 +1,7 @@
1
+ import { TranslateService } from './utils';
2
+ interface Props {
3
+ translateService: TranslateService;
4
+ detectLanguage: (text: string) => string | null;
5
+ }
6
+ export declare function InlineTranslationPlugin(props: Props): import("react/jsx-runtime").JSX.Element | null;
7
+ export {};
@@ -0,0 +1,58 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
3
+ import { useEffect } from 'react';
4
+ import { Box } from '@mui/material';
5
+ import { useInViewport } from 'ahooks';
6
+ import { restoreTranslation, translateEditorNodes } from './utils';
7
+ import { useInlineTranslationStore, useStatus, useTargetLanguage } from './store';
8
+ function InternalInlineTranslationPlugin({ translateService, detectLanguage }) {
9
+ const [editor] = useLexicalComposerContext();
10
+ const [inViewport] = useInViewport(editor.getRootElement());
11
+ const setStatus = useInlineTranslationStore((s) => s.setStatus);
12
+ const registerEditor = useInlineTranslationStore((s) => s.registerEditor);
13
+ const targetLanguage = useTargetLanguage();
14
+ const status = useStatus(editor);
15
+ const handleTranslate = async () => {
16
+ try {
17
+ setStatus(editor, 'processing');
18
+ await translateEditorNodes({
19
+ editor,
20
+ translateService,
21
+ targetLanguage,
22
+ detectLanguage,
23
+ });
24
+ setStatus(editor, 'completed');
25
+ }
26
+ catch (err) {
27
+ console.error(err);
28
+ }
29
+ };
30
+ const handleRestore = () => {
31
+ restoreTranslation(editor);
32
+ setStatus(editor, 'idle');
33
+ };
34
+ useEffect(() => {
35
+ return registerEditor(editor);
36
+ }, [editor]);
37
+ useEffect(() => {
38
+ if (inViewport && status === 'pending') {
39
+ handleTranslate();
40
+ }
41
+ }, [editor, status, inViewport]);
42
+ useEffect(() => {
43
+ if (status === 'idle') {
44
+ handleRestore();
45
+ }
46
+ }, [editor, status]);
47
+ if (editor.isEditable()) {
48
+ return null;
49
+ }
50
+ return _jsx(Box, { sx: { position: 'absolute', top: 0, bottom: 0, left: 0, width: '1px' } });
51
+ }
52
+ export function InlineTranslationPlugin(props) {
53
+ const [editor] = useLexicalComposerContext();
54
+ if (editor.isEditable()) {
55
+ return null;
56
+ }
57
+ return _jsx(InternalInlineTranslationPlugin, { ...props });
58
+ }
@@ -0,0 +1,25 @@
1
+ /// <reference types="react" />
2
+ import type { DOMConversionMap, DOMExportOutput, EditorConfig, LexicalNode, NodeKey, SerializedLexicalNode, Spread } from 'lexical';
3
+ import { DecoratorNode } from 'lexical';
4
+ export interface TranslationPayload {
5
+ key?: NodeKey;
6
+ html: string;
7
+ }
8
+ export type SerializedTranslationNode = Spread<{
9
+ html: string;
10
+ }, SerializedLexicalNode>;
11
+ export declare class TranslationNode extends DecoratorNode<JSX.Element> {
12
+ __html: string;
13
+ static getType(): string;
14
+ static clone(node: TranslationNode): TranslationNode;
15
+ static importJSON(serializedNode: SerializedTranslationNode): TranslationNode;
16
+ exportDOM(): DOMExportOutput;
17
+ static importDOM(): DOMConversionMap | null;
18
+ constructor(html: string, key?: NodeKey);
19
+ exportJSON(): SerializedTranslationNode;
20
+ createDOM(config: EditorConfig): HTMLElement;
21
+ updateDOM(): false;
22
+ decorate(): JSX.Element;
23
+ }
24
+ export declare function $createTranslationNode({ html, key }: TranslationPayload): TranslationNode;
25
+ export declare function $isTranslationNode(node: LexicalNode | null | undefined): node is TranslationNode;
@@ -0,0 +1,64 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /* eslint-disable @typescript-eslint/no-use-before-define */
3
+ import { Box } from '@mui/material';
4
+ import { $applyNodeReplacement, DecoratorNode } from 'lexical';
5
+ function $convertTranslationElement(domNode) {
6
+ const element = domNode;
7
+ const node = $createTranslationNode({ html: element.innerHTML });
8
+ return { node };
9
+ }
10
+ export class TranslationNode extends DecoratorNode {
11
+ __html;
12
+ static getType() {
13
+ return 'translation';
14
+ }
15
+ static clone(node) {
16
+ return new TranslationNode(node.__html, node.__key);
17
+ }
18
+ static importJSON(serializedNode) {
19
+ const { html } = serializedNode;
20
+ const node = $createTranslationNode({ html });
21
+ return node;
22
+ }
23
+ exportDOM() {
24
+ const element = document.createElement('span');
25
+ element.innerHTML = this.__html;
26
+ return { element };
27
+ }
28
+ static importDOM() {
29
+ return {
30
+ span: (node) => ({
31
+ conversion: $convertTranslationElement,
32
+ priority: 0,
33
+ }),
34
+ };
35
+ }
36
+ constructor(html, key) {
37
+ super(key);
38
+ this.__html = html;
39
+ }
40
+ exportJSON() {
41
+ return {
42
+ html: this.__html,
43
+ type: 'translation',
44
+ version: 1,
45
+ };
46
+ }
47
+ createDOM(config) {
48
+ const span = document.createElement('span');
49
+ span.className = 'lexical-editor-inline-translation';
50
+ return span;
51
+ }
52
+ updateDOM() {
53
+ return false;
54
+ }
55
+ decorate() {
56
+ return (_jsxs(_Fragment, { children: [_jsx("br", {}), _jsx(Box, { component: "span", sx: { borderBottom: '2px dashed', borderColor: 'info.light' }, dangerouslySetInnerHTML: { __html: this.__html } })] }));
57
+ }
58
+ }
59
+ export function $createTranslationNode({ html, key }) {
60
+ return $applyNodeReplacement(new TranslationNode(html, key));
61
+ }
62
+ export function $isTranslationNode(node) {
63
+ return node instanceof TranslationNode;
64
+ }
@@ -0,0 +1,4 @@
1
+ export * from './TranslationNode';
2
+ export * from './utils';
3
+ export * from './InlineTranslationPlugin';
4
+ export * from './store';
@@ -0,0 +1,4 @@
1
+ export * from './TranslationNode';
2
+ export * from './utils';
3
+ export * from './InlineTranslationPlugin';
4
+ export * from './store';
@@ -0,0 +1,37 @@
1
+ import { LexicalEditor } from 'lexical';
2
+ type TranslateStatus = 'idle' | 'pending' | 'processing' | 'completed';
3
+ interface State {
4
+ targetLanguage: string | null;
5
+ autoTranslate: boolean;
6
+ editors: Map<LexicalEditor, TranslateStatus>;
7
+ }
8
+ interface Action {
9
+ translate: (editor?: LexicalEditor) => void;
10
+ showOriginal: (editor?: LexicalEditor) => void;
11
+ setTargetLanguage: (targetLanguage: string) => void;
12
+ setAutoTranslate: (autoTranslate: boolean) => void;
13
+ setStatus: (editor: LexicalEditor, status: TranslateStatus) => void;
14
+ registerEditor: (editor: LexicalEditor) => () => void;
15
+ unregisterEditor: (editor: LexicalEditor) => void;
16
+ }
17
+ export declare const useInlineTranslationStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<State & Action>, "persist"> & {
18
+ persist: {
19
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<State & Action, {
20
+ targetLanguage: string | null;
21
+ autoTranslate: boolean;
22
+ }>>) => void;
23
+ clearStorage: () => void;
24
+ rehydrate: () => void | Promise<void>;
25
+ hasHydrated: () => boolean;
26
+ onHydrate: (fn: (state: State & Action) => void) => () => void;
27
+ onFinishHydration: (fn: (state: State & Action) => void) => () => void;
28
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<State & Action, {
29
+ targetLanguage: string | null;
30
+ autoTranslate: boolean;
31
+ }>>;
32
+ };
33
+ }>;
34
+ export declare const useStatus: (editor: LexicalEditor) => TranslateStatus | undefined;
35
+ export declare const useIsIdle: () => boolean;
36
+ export declare const useTargetLanguage: () => any;
37
+ export {};
@@ -0,0 +1,67 @@
1
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
+ import { create } from 'zustand';
3
+ import { persist } from 'zustand/middleware';
4
+ export const useInlineTranslationStore = create()(persist((set, get) => ({
5
+ targetLanguage: null,
6
+ autoTranslate: false,
7
+ editors: new Map(),
8
+ translate: (editor) => {
9
+ if (editor) {
10
+ set((state) => ({ editors: new Map(state.editors).set(editor, 'pending') }));
11
+ }
12
+ else {
13
+ set((state) => ({
14
+ editors: new Map(Array.from(state.editors.entries()).map(([k, v]) => [k, v === 'idle' ? 'pending' : v])),
15
+ }));
16
+ }
17
+ },
18
+ showOriginal: (editor) => {
19
+ if (editor) {
20
+ set((state) => ({ editors: new Map(state.editors).set(editor, 'idle') }));
21
+ }
22
+ else {
23
+ set((state) => ({
24
+ editors: new Map(Array.from(state.editors.entries()).map(([k]) => [k, 'idle'])),
25
+ }));
26
+ }
27
+ },
28
+ setTargetLanguage: (targetLanguage) => set({ targetLanguage }),
29
+ setAutoTranslate: (autoTranslate) => {
30
+ set({ autoTranslate });
31
+ if (autoTranslate) {
32
+ get().translate();
33
+ }
34
+ },
35
+ setStatus: (editor, status) => {
36
+ set((state) => ({ editors: new Map(state.editors).set(editor, status) }));
37
+ },
38
+ registerEditor: (editor) => {
39
+ set((state) => ({
40
+ editors: new Map(state.editors).set(editor, state.autoTranslate ? 'pending' : 'idle'),
41
+ }));
42
+ return () => {
43
+ get().unregisterEditor(editor);
44
+ };
45
+ },
46
+ unregisterEditor: (editor) => {
47
+ set((state) => ({
48
+ editors: new Map(Array.from(state.editors.entries()).filter(([k]) => k !== editor)),
49
+ }));
50
+ },
51
+ }), {
52
+ name: 'inline-translation-settings',
53
+ partialize: (state) => ({
54
+ targetLanguage: state.targetLanguage,
55
+ autoTranslate: state.autoTranslate,
56
+ }),
57
+ }));
58
+ export const useStatus = (editor) => {
59
+ return useInlineTranslationStore((state) => state.editors.get(editor));
60
+ };
61
+ export const useIsIdle = () => {
62
+ return useInlineTranslationStore((state) => Array.from(state.editors.values()).every((x) => x === 'idle'));
63
+ };
64
+ export const useTargetLanguage = () => {
65
+ const { locale } = useLocaleContext();
66
+ return useInlineTranslationStore((state) => state.targetLanguage ?? locale);
67
+ };
@@ -0,0 +1,27 @@
1
+ import { ElementNode, LexicalNode, LexicalEditor } from 'lexical';
2
+ interface TranslateItem {
3
+ uid: string;
4
+ text: string;
5
+ }
6
+ export type TranslateService = ({ sourceItems, targetLanguage, }: {
7
+ sourceItems: TranslateItem[];
8
+ targetLanguage: string;
9
+ }) => Promise<TranslateItem[]>;
10
+ export declare const translateEditorNodes: ({ editor, targetLanguage, translateService, detectLanguage, }: {
11
+ editor: LexicalEditor;
12
+ targetLanguage: string;
13
+ translateService: TranslateService;
14
+ detectLanguage: (text: string) => string | null;
15
+ }) => Promise<void>;
16
+ export declare const restoreTranslation: (editor: LexicalEditor) => void;
17
+ /**
18
+ * 判断 node 是否是有效的 TextNode
19
+ * - $isTextNode => true
20
+ * - format 不是 code
21
+ */
22
+ export declare const $isValidTextNode: (node: LexicalNode) => boolean;
23
+ /**
24
+ * 判断一个 top level node 是否含有效的 TextNode 的 child
25
+ */
26
+ export declare const $hasValidTextNode: (elementNode: ElementNode) => boolean;
27
+ export {};
@@ -0,0 +1,113 @@
1
+ import { $isParagraphNode, $isTextNode, $isElementNode, $createNodeSelection, $nodesOfType, $getNodeByKey, } from 'lexical';
2
+ import { $isHeadingNode, $isQuoteNode } from '@lexical/rich-text';
3
+ import { $isListNode, $isListItemNode } from '@lexical/list';
4
+ import { $dfs } from '@lexical/utils';
5
+ import { $generateHtmlFromNodes } from '@lexical/html';
6
+ import { $createEmojiNode } from '../../main/nodes/EmojiNode';
7
+ import { $createTranslationNode, TranslationNode } from './TranslationNode';
8
+ /**
9
+ * Creates a mapping between node keys and sequential IDs
10
+ * @param reversed If true, maps ID -> key. If false, maps key -> ID
11
+ * @returns Map of either [key -> ID] or [ID -> key]
12
+ */
13
+ const getNodeIds = (reversed = false) => {
14
+ const nodes = $dfs();
15
+ let counter = 0;
16
+ if (reversed) {
17
+ return new Map(nodes.map((x) => [`${counter++}`, x.node.getKey()]));
18
+ }
19
+ return new Map(nodes.map((x) => [x.node.getKey(), `${counter++}`]));
20
+ };
21
+ const collectTranslatableNodes = () => {
22
+ const nodes = $dfs()
23
+ .map((x) => x.node)
24
+ .filter((node) => $isElementNode(node) &&
25
+ ($isHeadingNode(node) || $isParagraphNode(node) || $isQuoteNode(node) || $isListItemNode(node)))
26
+ .filter((node) => {
27
+ return $hasValidTextNode(node) && !!node.getTextContent().trim();
28
+ });
29
+ return nodes;
30
+ };
31
+ const nodeToHtml = (editor, node) => {
32
+ const selection = $createNodeSelection();
33
+ const children = node.getChildren();
34
+ // 剔除嵌套 list 节点
35
+ children
36
+ .filter((x) => !$isListNode(x))
37
+ .forEach((child) => $dfs(child).forEach((x) => selection.add(x.node.getKey())));
38
+ const html = $generateHtmlFromNodes(editor, selection);
39
+ return html;
40
+ };
41
+ export const translateEditorNodes = async ({ editor, targetLanguage, translateService, detectLanguage, }) => {
42
+ let nodes = [];
43
+ const nodeUniqueIdMap = editor.getEditorState().read(() => getNodeIds());
44
+ editor.update(() => {
45
+ nodes = collectTranslatableNodes()
46
+ .filter((node) => detectLanguage(node.getTextContent()) !== targetLanguage)
47
+ .map((node) => {
48
+ const html = nodeToHtml(editor, node);
49
+ return { node, html };
50
+ });
51
+ nodes.forEach(({ node }) => {
52
+ node.append($createEmojiNode('global-loading', ''));
53
+ });
54
+ }, { discrete: true });
55
+ if (nodes.length === 0) {
56
+ return;
57
+ }
58
+ let translations = null;
59
+ try {
60
+ translations = await translateService({
61
+ sourceItems: nodes.map(({ node, html }) => ({ uid: nodeUniqueIdMap.get(node.getKey()), text: html })),
62
+ targetLanguage,
63
+ });
64
+ }
65
+ catch (err) {
66
+ console.error(err);
67
+ }
68
+ finally {
69
+ nodes.forEach(({ node }, index) => {
70
+ editor.update(() => {
71
+ const lastChild = node.getLastChild();
72
+ if (lastChild) {
73
+ lastChild.remove();
74
+ }
75
+ });
76
+ });
77
+ }
78
+ if (translations) {
79
+ applyTranslations(editor, translations);
80
+ }
81
+ };
82
+ const applyTranslations = (editor, translations) => {
83
+ editor.update(() => {
84
+ const nodeUniqueIdMap = getNodeIds(true);
85
+ translations.forEach(({ uid, text }) => {
86
+ const node = $getNodeByKey(nodeUniqueIdMap.get(uid));
87
+ if (node) {
88
+ node.append($createTranslationNode({ html: text }));
89
+ }
90
+ });
91
+ });
92
+ };
93
+ export const restoreTranslation = (editor) => {
94
+ editor.update(() => {
95
+ $nodesOfType(TranslationNode).forEach((node) => {
96
+ node.remove();
97
+ });
98
+ });
99
+ };
100
+ /**
101
+ * 判断 node 是否是有效的 TextNode
102
+ * - $isTextNode => true
103
+ * - format 不是 code
104
+ */
105
+ export const $isValidTextNode = (node) => {
106
+ return $isTextNode(node) && !node.hasFormat('code');
107
+ };
108
+ /**
109
+ * 判断一个 top level node 是否含有效的 TextNode 的 child
110
+ */
111
+ export const $hasValidTextNode = (elementNode) => {
112
+ return elementNode.getChildren().some($isValidTextNode);
113
+ };
@@ -82,6 +82,7 @@ import { BlurTextPlugin } from '../ext/BlurTextPlugin';
82
82
  import { useResponsiveTable } from './hooks/useResponsiveTable';
83
83
  import { useTranslationListener } from './hooks/useTranslationListener';
84
84
  import { PagesKitComponentPlugin } from '../ext/PagesKitComponent/PagesKitComponentPlugin';
85
+ import { EditorHolderPlugin } from '../ext/EditorHolderPlugin';
85
86
  export default function Editor({ children, placeholder, onChange, autoFocus = true, showToolbar = true, editorRef, onReady, enableHeadingsIdPlugin, }) {
86
87
  const [editor] = useLexicalComposerContext();
87
88
  const [editable, setEditable] = useState(false);
@@ -113,7 +114,7 @@ export default function Editor({ children, placeholder, onChange, autoFocus = tr
113
114
  if (minimalMode) {
114
115
  return (_jsxs(_Fragment, { children: [isRichText && editable && showToolbar && _jsx(ToolbarPlugin, {}), hasNodes('image') && hasUploader && _jsx(DragDropPaste, {}), autoFocus && _jsx(AutoFocusPlugin, { defaultSelection: "rootEnd" }), _jsx(ClearEditorPlugin, {}), !!editable && _jsx(ComponentPickerPlugin, {}), !!editable && _jsx(MentionsPlugin, {}), hasNodes('link') && _jsx(AutoEmbedPlugin, {}), _jsx(EditorContent, { ref: onRef, className: cx('be-content', editable && 'editable'), children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: cx('be-editable', 'notranslate') }), placeholder: _jsx(Placeholder, { className: "be-placeholder", children: placeholder }), ErrorBoundary: LexicalErrorBoundary }) }), hasNodes('code', 'code-highlight') && _jsx(CodeHighlightPlugin, {}), hasNodes('image') && hasUploader && _jsx(ImagesPlugin, {}), hasNodes('video') && _jsx(VideoPlugin, {}), hasNodes('link') && _jsx(LinkPlugin, {}), hasNodes('tweet') && _jsx(TwitterPlugin, {}), hasNodes('youtube') && _jsx(YouTubePlugin, {}), hasNodes('figma') && _jsx(FigmaPlugin, {}), _jsx(BilibiliPlugin, {}), _jsx(PostLinkEmbedPlugin, {}), _jsx(BookmarkPlugin, {}), editable && _jsx(CustomOnChangePlugin, { placeholder: placeholder }), editable && _jsx(TemplatePlugin, {}), !editable && _jsx(BlurTextPlugin, {}), hasNodes('pdf') && _jsx(PdfPlugin, {}), hasNodes('file') && _jsx(FilePlugin, {}), hasNodes('horizontalrule') && _jsx(HorizontalRulePlugin, {}), hasNodes('excalidraw') && _jsx(ExcalidrawPlugin, {}), hasNodes('alert') && _jsx(AlertPlugin, {}), hasNodes('pages-kit-component') && _jsx(PagesKitComponentPlugin, {}), _jsx(TabFocusPlugin, {}), onChange && _jsx(OnChangePlugin, { onChange: onChange }), floatingAnchorElem && editable && (_jsxs(_Fragment, { children: [hasNodes('code') && _jsx(CodeActionMenuPlugin, { anchorElem: floatingAnchorElem }), hasNodes('link') && _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem })] })), _jsx(AiImagePlugin, {}), editorRef && _jsx(EditorRefPlugin, { editorRef: editorRef }), children] }));
115
116
  }
116
- return (_jsxs(_Fragment, { children: [isRichText && editable && showToolbar && _jsx(ToolbarPlugin, {}), isMaxLength && _jsx(MaxLengthPlugin, { maxLength: 30 }), hasNodes('image') && hasUploader && _jsx(DragDropPaste, {}), autoFocus && _jsx(AutoFocusPlugin, { defaultSelection: "rootEnd" }), _jsx(ClearEditorPlugin, {}), !!editable && _jsx(ComponentPickerPlugin, {}), !!editable && _jsx(EmojiPickerPlugin, {}), hasNodes('link') && _jsx(AutoEmbedPlugin, {}), !!editable && _jsx(MentionsPlugin, {}), hasNodes('emoji') && _jsx(EmojisPlugin, {}), hasNodes('hashtag') && _jsx(HashtagPlugin, {}), _jsx(SpeechToTextPlugin, {}), hasNodes('autolink') && _jsx(AutoLinkPlugin, {}), isRichText ? (_jsxs(_Fragment, { children: [_jsx(HistoryPlugin, { externalHistoryState: historyState }), _jsx(EditorContent, { ref: onRef, className: cx('be-content', editable && 'editable'), children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: cx('be-editable', 'notranslate') }), placeholder: _jsx(Placeholder, { className: "be-placeholder", children: placeholder }), ErrorBoundary: LexicalErrorBoundary }) }), _jsx(MarkdownShortcutPlugin, {}), _jsx(TabIndentationPlugin, {}), hasNodes('code', 'code-highlight') && _jsx(CodeHighlightPlugin, {}), hasNodes('list', 'listitem') && _jsx(ListPlugin, {}), hasNodes('list', 'listitem') && _jsx(CheckListPlugin, {}), hasNodes('list', 'listitem') && _jsx(ListMaxIndentLevelPlugin, { maxDepth: 7 }), hasNodes('table', 'tablerow', 'tablecell') && editable && (_jsx(TablePlugin, { hasCellMerge: true, hasCellBackgroundColor: true })), hasNodes('table', 'tablerow', 'tablecell') && _jsx(TableCellResizer, {}), hasNodes('image') && hasUploader && _jsx(ImagesPlugin, {}), hasNodes('video') && _jsx(VideoPlugin, {}), hasNodes('link') && _jsx(LinkPlugin, {}), hasNodes('tweet') && _jsx(TwitterPlugin, {}), hasNodes('youtube') && _jsx(YouTubePlugin, {}), hasNodes('figma') && _jsx(FigmaPlugin, {}), _jsx(BilibiliPlugin, {}), _jsx(BlockletEmbedPlugin, {}), _jsx(PostLinkEmbedPlugin, {}), _jsx(BookmarkPlugin, {}), _jsx(AidePlugin, {}), editable && _jsx(CustomOnChangePlugin, { placeholder: placeholder }), editable && _jsx(TemplatePlugin, {}), !editable && _jsx(BlurTextPlugin, {}), hasNodes('pdf') && _jsx(PdfPlugin, {}), hasNodes('file') && _jsx(FilePlugin, {}), hasNodes('link') && _jsx(ClickableLinkPlugin, {}), hasNodes('horizontalrule') && _jsx(HorizontalRulePlugin, {}), hasNodes('excalidraw') && _jsx(ExcalidrawPlugin, {}), hasNodes('alert') && _jsx(AlertPlugin, {}), hasNodes('pages-kit-component') && _jsx(PagesKitComponentPlugin, {}), _jsx(TabFocusPlugin, {}), hasNodes('collapsible-container', 'collapsible-content', 'collapsible-title') && _jsx(CollapsiblePlugin, {}), onChange && _jsx(OnChangePlugin, { onChange: onChange }), floatingAnchorElem && editable && (_jsxs(_Fragment, { children: [_jsx(DraggableBlockPlugin, { anchorElem: floatingAnchorElem }), hasNodes('code') && _jsx(CodeActionMenuPlugin, { anchorElem: floatingAnchorElem }), hasNodes('link') && _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem }), hasNodes('table') && _jsx(TableCellActionMenuPlugin, { anchorElem: floatingAnchorElem, cellMerge: true }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem })] })), enableHeadingsIdPlugin && _jsx(HeadingsIdPlugin, {})] })) : (_jsxs(_Fragment, { children: [_jsx(PlainTextPlugin, { contentEditable: _jsx(ContentEditable, {}), placeholder: _jsx(Placeholder, { children: "placeholder" }), ErrorBoundary: LexicalErrorBoundary }), _jsx(HistoryPlugin, { externalHistoryState: historyState })] })), (isCharLimit || isCharLimitUtf8) && (_jsx(CharacterLimitPlugin, { charset: isCharLimit ? 'UTF-16' : 'UTF-8', maxLength: 5 })), isAutocomplete && hasNodes('autocomplete') && _jsx(AutocompletePlugin, {}), _jsx("div", { children: showTableOfContents && _jsx(TableOfContentsPlugin, {}) }), _jsx(SelectBlockPlugin, {}), _jsx(RemoveListPlugin, {}), _jsx(MarkdownHeadTextPlugin, {}), _jsx(AiImagePlugin, {}), editorRef && _jsx(EditorRefPlugin, { editorRef: editorRef }), onReady && _jsx(EditorReadyPlugin, { onReady: onReady }), children] }));
117
+ return (_jsxs(_Fragment, { children: [isRichText && editable && showToolbar && _jsx(ToolbarPlugin, {}), isMaxLength && _jsx(MaxLengthPlugin, { maxLength: 30 }), hasNodes('image') && hasUploader && _jsx(DragDropPaste, {}), autoFocus && _jsx(AutoFocusPlugin, { defaultSelection: "rootEnd" }), _jsx(ClearEditorPlugin, {}), !!editable && _jsx(ComponentPickerPlugin, {}), !!editable && _jsx(EmojiPickerPlugin, {}), hasNodes('link') && _jsx(AutoEmbedPlugin, {}), !!editable && _jsx(MentionsPlugin, {}), hasNodes('emoji') && _jsx(EmojisPlugin, {}), hasNodes('hashtag') && _jsx(HashtagPlugin, {}), _jsx(SpeechToTextPlugin, {}), hasNodes('autolink') && _jsx(AutoLinkPlugin, {}), isRichText ? (_jsxs(_Fragment, { children: [_jsx(HistoryPlugin, { externalHistoryState: historyState }), _jsx(EditorContent, { ref: onRef, className: cx('be-content', editable && 'editable'), children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: cx('be-editable', 'notranslate') }), placeholder: _jsx(Placeholder, { className: "be-placeholder", children: placeholder }), ErrorBoundary: LexicalErrorBoundary }) }), _jsx(MarkdownShortcutPlugin, {}), _jsx(TabIndentationPlugin, {}), hasNodes('code', 'code-highlight') && _jsx(CodeHighlightPlugin, {}), hasNodes('list', 'listitem') && _jsx(ListPlugin, {}), hasNodes('list', 'listitem') && _jsx(CheckListPlugin, {}), hasNodes('list', 'listitem') && _jsx(ListMaxIndentLevelPlugin, { maxDepth: 7 }), hasNodes('table', 'tablerow', 'tablecell') && editable && (_jsx(TablePlugin, { hasCellMerge: true, hasCellBackgroundColor: true })), hasNodes('table', 'tablerow', 'tablecell') && _jsx(TableCellResizer, {}), hasNodes('image') && hasUploader && _jsx(ImagesPlugin, {}), hasNodes('video') && _jsx(VideoPlugin, {}), hasNodes('link') && _jsx(LinkPlugin, {}), hasNodes('tweet') && _jsx(TwitterPlugin, {}), hasNodes('youtube') && _jsx(YouTubePlugin, {}), hasNodes('figma') && _jsx(FigmaPlugin, {}), _jsx(BilibiliPlugin, {}), _jsx(BlockletEmbedPlugin, {}), _jsx(PostLinkEmbedPlugin, {}), _jsx(BookmarkPlugin, {}), _jsx(AidePlugin, {}), editable && _jsx(CustomOnChangePlugin, { placeholder: placeholder }), editable && _jsx(TemplatePlugin, {}), !editable && _jsx(BlurTextPlugin, {}), hasNodes('pdf') && _jsx(PdfPlugin, {}), hasNodes('file') && _jsx(FilePlugin, {}), hasNodes('link') && _jsx(ClickableLinkPlugin, {}), hasNodes('horizontalrule') && _jsx(HorizontalRulePlugin, {}), hasNodes('excalidraw') && _jsx(ExcalidrawPlugin, {}), hasNodes('alert') && _jsx(AlertPlugin, {}), hasNodes('pages-kit-component') && _jsx(PagesKitComponentPlugin, {}), _jsx(TabFocusPlugin, {}), hasNodes('collapsible-container', 'collapsible-content', 'collapsible-title') && _jsx(CollapsiblePlugin, {}), onChange && _jsx(OnChangePlugin, { onChange: onChange }), floatingAnchorElem && editable && (_jsxs(_Fragment, { children: [_jsx(DraggableBlockPlugin, { anchorElem: floatingAnchorElem }), hasNodes('code') && _jsx(CodeActionMenuPlugin, { anchorElem: floatingAnchorElem }), hasNodes('link') && _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem }), hasNodes('table') && _jsx(TableCellActionMenuPlugin, { anchorElem: floatingAnchorElem, cellMerge: true }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem })] })), enableHeadingsIdPlugin && _jsx(HeadingsIdPlugin, {})] })) : (_jsxs(_Fragment, { children: [_jsx(PlainTextPlugin, { contentEditable: _jsx(ContentEditable, {}), placeholder: _jsx(Placeholder, { children: "placeholder" }), ErrorBoundary: LexicalErrorBoundary }), _jsx(HistoryPlugin, { externalHistoryState: historyState })] })), (isCharLimit || isCharLimitUtf8) && (_jsx(CharacterLimitPlugin, { charset: isCharLimit ? 'UTF-16' : 'UTF-8', maxLength: 5 })), isAutocomplete && hasNodes('autocomplete') && _jsx(AutocompletePlugin, {}), _jsx("div", { children: showTableOfContents && _jsx(TableOfContentsPlugin, {}) }), _jsx(SelectBlockPlugin, {}), _jsx(RemoveListPlugin, {}), _jsx(MarkdownHeadTextPlugin, {}), _jsx(AiImagePlugin, {}), _jsx(EditorHolderPlugin, {}), editorRef && _jsx(EditorRefPlugin, { editorRef: editorRef }), onReady && _jsx(EditorReadyPlugin, { onReady: onReady }), children] }));
117
118
  }
118
119
  const EditorContent = styled.div `
119
120
  position: relative;
@@ -39,6 +39,7 @@ import { PdfNode } from '../../ext/PdfPlugin';
39
39
  import { FileNode } from '../../ext/FilePlugin';
40
40
  import { SubpageListingNode } from '../../ext/SubpageListingPlugin/SubpageListingNode';
41
41
  import { PagesKitComponentNode } from '../../ext/PagesKitComponent/PagesKitComponentNode';
42
+ import { TranslationNode } from '../../ext/InlineTranslationPlugin';
42
43
  const PlaygroundNodes = [
43
44
  HeadingNode,
44
45
  ListNode,
@@ -80,5 +81,6 @@ const PlaygroundNodes = [
80
81
  FileNode,
81
82
  SubpageListingNode,
82
83
  PagesKitComponentNode,
84
+ TranslationNode,
83
85
  ];
84
86
  export default PlaygroundNodes;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/editor",
3
- "version": "2.1.39",
3
+ "version": "2.1.41",
4
4
  "main": "lib/index.js",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -64,7 +64,8 @@
64
64
  "react-popper": "^2.3.0",
65
65
  "ufo": "^1.5.4",
66
66
  "url-join": "^4.0.1",
67
- "@blocklet/pdf": "^2.1.39"
67
+ "zustand": "^4.5.5",
68
+ "@blocklet/pdf": "^2.1.41"
68
69
  },
69
70
  "devDependencies": {
70
71
  "@babel/core": "^7.25.2",