mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
🐛 fix: fix editor modal when Markdown rendering off (#11251)
fix: fix editor modal
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { HotkeyEnum, getHotkeyById } from '@lobehub/editor';
|
||||
import { FloatActions } from '@lobehub/editor/react';
|
||||
import { type ChatInputActionsProps, CodeLanguageSelect } from '@lobehub/editor/react';
|
||||
import { type ChatInputActionsProps } from '@lobehub/editor/react';
|
||||
import {
|
||||
BoldIcon,
|
||||
CodeXmlIcon,
|
||||
@@ -116,16 +116,6 @@ const TypoBar = memo(() => {
|
||||
label: t('typobar.codeblock'),
|
||||
onClick: editorState.codeblock,
|
||||
},
|
||||
editorState.isCodeblock && {
|
||||
children: (
|
||||
<CodeLanguageSelect
|
||||
onSelect={(value) => editorState.updateCodeblockLang(value)}
|
||||
value={editorState.codeblockLang}
|
||||
/>
|
||||
),
|
||||
disabled: !editorState.isCodeblock,
|
||||
key: 'codeblockLang',
|
||||
},
|
||||
].filter(Boolean) as ChatInputActionsProps['items'];
|
||||
}, [editorState, t]);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { HotkeyEnum, getHotkeyById } from '@lobehub/editor';
|
||||
import { FloatActions } from '@lobehub/editor/react';
|
||||
import { type ChatInputActionsProps, CodeLanguageSelect } from '@lobehub/editor/react';
|
||||
import { type ChatInputActionsProps } from '@lobehub/editor/react';
|
||||
import {
|
||||
BoldIcon,
|
||||
CodeXmlIcon,
|
||||
@@ -116,16 +116,6 @@ const TypoBar = memo(() => {
|
||||
label: t('typobar.codeblock'),
|
||||
onClick: editorState.codeblock,
|
||||
},
|
||||
editorState.isCodeblock && {
|
||||
children: (
|
||||
<CodeLanguageSelect
|
||||
onSelect={(value) => editorState.updateCodeblockLang(value)}
|
||||
value={editorState.codeblockLang}
|
||||
/>
|
||||
),
|
||||
disabled: !editorState.isCodeblock,
|
||||
key: 'codeblockLang',
|
||||
},
|
||||
].filter(Boolean) as ChatInputActionsProps['items'];
|
||||
}, [editorState, t]);
|
||||
|
||||
|
||||
@@ -1,66 +1,16 @@
|
||||
import {
|
||||
ReactCodePlugin,
|
||||
ReactCodemirrorPlugin,
|
||||
ReactHRPlugin,
|
||||
ReactLinkHighlightPlugin,
|
||||
ReactListPlugin,
|
||||
ReactMathPlugin,
|
||||
ReactTablePlugin,
|
||||
} from '@lobehub/editor';
|
||||
import { Editor, useEditor } from '@lobehub/editor/react';
|
||||
import { Flexbox, Modal } from '@lobehub/ui';
|
||||
import { memo, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { labPreferSelectors } from '@/store/user/slices/preference/selectors';
|
||||
import { EditorModal } from '@/features/EditorModal';
|
||||
import { useUserMemoryStore } from '@/store/userMemory';
|
||||
import { LayersEnum } from '@/types/userMemory';
|
||||
|
||||
import TypoBar from './Typobar';
|
||||
|
||||
const EditableModal = memo(() => {
|
||||
const { t } = useTranslation('common');
|
||||
const editor = useEditor();
|
||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||
const editingMemoryId = useUserMemoryStore((s) => s.editingMemoryId);
|
||||
const editingMemoryContent = useUserMemoryStore((s) => s.editingMemoryContent);
|
||||
const editingMemoryLayer = useUserMemoryStore((s) => s.editingMemoryLayer);
|
||||
const clearEditingMemory = useUserMemoryStore((s) => s.clearEditingMemory);
|
||||
const updateMemory = useUserMemoryStore((s) => s.updateMemory);
|
||||
|
||||
const enableRichRender = useUserStore(labPreferSelectors.enableInputMarkdown);
|
||||
|
||||
const richRenderProps = useMemo(
|
||||
() =>
|
||||
!enableRichRender
|
||||
? {
|
||||
enablePasteMarkdown: false,
|
||||
markdownOption: {
|
||||
bold: false,
|
||||
code: false,
|
||||
header: false,
|
||||
italic: false,
|
||||
quote: false,
|
||||
strikethrough: false,
|
||||
underline: false,
|
||||
underlineStrikethrough: false,
|
||||
},
|
||||
}
|
||||
: {
|
||||
plugins: [
|
||||
ReactListPlugin,
|
||||
ReactCodePlugin,
|
||||
ReactCodemirrorPlugin,
|
||||
ReactHRPlugin,
|
||||
ReactLinkHighlightPlugin,
|
||||
ReactTablePlugin,
|
||||
ReactMathPlugin,
|
||||
],
|
||||
},
|
||||
[enableRichRender],
|
||||
);
|
||||
|
||||
const layerMap = {
|
||||
context: LayersEnum.Context,
|
||||
experience: LayersEnum.Experience,
|
||||
@@ -69,58 +19,15 @@ const EditableModal = memo(() => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
cancelText={t('cancel')}
|
||||
closable={false}
|
||||
confirmLoading={confirmLoading}
|
||||
destroyOnHidden
|
||||
okText={t('ok')}
|
||||
<EditorModal
|
||||
onCancel={clearEditingMemory}
|
||||
onOk={async () => {
|
||||
if (!editor || !editingMemoryId || !editingMemoryLayer) return;
|
||||
setConfirmLoading(true);
|
||||
const newValue = editor.getDocument('markdown') as unknown as string;
|
||||
await updateMemory(editingMemoryId, newValue, layerMap[editingMemoryLayer]);
|
||||
setConfirmLoading(false);
|
||||
onConfirm={async (value) => {
|
||||
if (!editingMemoryId || !editingMemoryLayer) return;
|
||||
await updateMemory(editingMemoryId, value, layerMap[editingMemoryLayer]);
|
||||
}}
|
||||
open={!!editingMemoryId}
|
||||
styles={{
|
||||
body: {
|
||||
overflow: 'hidden',
|
||||
padding: 0,
|
||||
},
|
||||
}}
|
||||
title={null}
|
||||
width={'min(90vw, 960px)'}
|
||||
>
|
||||
<TypoBar editor={editor} />
|
||||
<Flexbox
|
||||
onClick={() => {
|
||||
editor.focus();
|
||||
}}
|
||||
paddingBlock={16}
|
||||
paddingInline={48}
|
||||
style={{ cursor: 'text', maxHeight: '80vh', minHeight: '50vh', overflowY: 'auto' }}
|
||||
>
|
||||
<Editor
|
||||
autoFocus
|
||||
content={''}
|
||||
editor={editor}
|
||||
onInit={(editor) => {
|
||||
if (!editor) return;
|
||||
try {
|
||||
editor?.setDocument('markdown', editingMemoryContent);
|
||||
} catch {}
|
||||
}}
|
||||
style={{
|
||||
paddingBottom: 120,
|
||||
}}
|
||||
type={'text'}
|
||||
variant={'chat'}
|
||||
{...richRenderProps}
|
||||
/>
|
||||
</Flexbox>
|
||||
</Modal>
|
||||
value={editingMemoryContent}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@ const InputEditor = memo<{ defaultRows?: number }>(({ defaultRows = 2 }) => {
|
||||
underline: false,
|
||||
underlineStrikethrough: false,
|
||||
},
|
||||
plugins: [ReactCodemirrorPlugin],
|
||||
}
|
||||
: {
|
||||
plugins: [
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
ChatInputActionBar,
|
||||
ChatInputActions,
|
||||
type ChatInputActionsProps,
|
||||
CodeLanguageSelect,
|
||||
} from '@lobehub/editor/react';
|
||||
import { cssVar } from 'antd-style';
|
||||
import {
|
||||
@@ -122,16 +121,6 @@ const TypoBar = memo(() => {
|
||||
label: t('typobar.codeblock'),
|
||||
onClick: editorState.codeblock,
|
||||
},
|
||||
editorState.isCodeblock && {
|
||||
children: (
|
||||
<CodeLanguageSelect
|
||||
onSelect={(value) => editorState.updateCodeblockLang(value)}
|
||||
value={editorState.codeblockLang}
|
||||
/>
|
||||
),
|
||||
disabled: !editorState.isCodeblock,
|
||||
key: 'codeblockLang',
|
||||
},
|
||||
].filter(Boolean) as ChatInputActionsProps['items'],
|
||||
[editorState],
|
||||
);
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
import {
|
||||
ReactCodePlugin,
|
||||
ReactCodemirrorPlugin,
|
||||
ReactHRPlugin,
|
||||
ReactLinkHighlightPlugin,
|
||||
ReactListPlugin,
|
||||
ReactMathPlugin,
|
||||
ReactTablePlugin,
|
||||
} from '@lobehub/editor';
|
||||
import { Editor, useEditor } from '@lobehub/editor/react';
|
||||
import { Flexbox, Modal } from '@lobehub/ui';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { labPreferSelectors } from '@/store/user/slices/preference/selectors';
|
||||
|
||||
import TypoBar from './Typobar';
|
||||
|
||||
interface EditableMessageProps {
|
||||
editing?: boolean;
|
||||
onChange?: (value: string) => void;
|
||||
onEditingChange?: (editing: boolean) => void;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
const EditableMessage = memo<EditableMessageProps>(
|
||||
({ editing, onEditingChange, onChange, value }) => {
|
||||
const { t } = useTranslation('common');
|
||||
const editor = useEditor();
|
||||
|
||||
const enableRichRender = useUserStore(labPreferSelectors.enableInputMarkdown);
|
||||
|
||||
const richRenderProps = useMemo(
|
||||
() =>
|
||||
!enableRichRender
|
||||
? {
|
||||
enablePasteMarkdown: false,
|
||||
markdownOption: {
|
||||
bold: false,
|
||||
code: false,
|
||||
header: false,
|
||||
italic: false,
|
||||
quote: false,
|
||||
strikethrough: false,
|
||||
underline: false,
|
||||
underlineStrikethrough: false,
|
||||
},
|
||||
}
|
||||
: {
|
||||
plugins: [
|
||||
ReactListPlugin,
|
||||
ReactCodePlugin,
|
||||
ReactCodemirrorPlugin,
|
||||
ReactHRPlugin,
|
||||
ReactLinkHighlightPlugin,
|
||||
ReactTablePlugin,
|
||||
ReactMathPlugin,
|
||||
],
|
||||
},
|
||||
[enableRichRender],
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
cancelText={t('cancel')}
|
||||
closable={false}
|
||||
destroyOnHidden
|
||||
okText={t('ok')}
|
||||
onCancel={() => onEditingChange?.(false)}
|
||||
onOk={() => {
|
||||
if (!editor) return;
|
||||
const newValue = editor.getDocument('markdown') as unknown as string;
|
||||
onChange?.(newValue);
|
||||
onEditingChange?.(false);
|
||||
}}
|
||||
open={editing}
|
||||
styles={{
|
||||
body: {
|
||||
overflow: 'hidden',
|
||||
padding: 0,
|
||||
},
|
||||
}}
|
||||
title={null}
|
||||
width={'min(90vw, 960px)'}
|
||||
>
|
||||
<TypoBar editor={editor} />
|
||||
<Flexbox
|
||||
onClick={() => {
|
||||
editor.focus();
|
||||
}}
|
||||
paddingBlock={16}
|
||||
paddingInline={48}
|
||||
style={{ cursor: 'text', maxHeight: '80vh', minHeight: '50vh', overflowY: 'auto' }}
|
||||
>
|
||||
<Editor
|
||||
autoFocus
|
||||
content={''}
|
||||
editor={editor}
|
||||
onInit={(editor) => {
|
||||
if (!editor) return;
|
||||
try {
|
||||
editor?.setDocument('markdown', value);
|
||||
} catch {}
|
||||
}}
|
||||
style={{
|
||||
paddingBottom: 120,
|
||||
}}
|
||||
type={'text'}
|
||||
variant={'chat'}
|
||||
{...richRenderProps}
|
||||
/>
|
||||
</Flexbox>
|
||||
</Modal>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default EditableMessage;
|
||||
@@ -1,150 +0,0 @@
|
||||
import { HotkeyEnum, type IEditor, getHotkeyById } from '@lobehub/editor';
|
||||
import { useEditorState } from '@lobehub/editor/react';
|
||||
import {
|
||||
ChatInputActionBar,
|
||||
ChatInputActions,
|
||||
type ChatInputActionsProps,
|
||||
CodeLanguageSelect,
|
||||
} from '@lobehub/editor/react';
|
||||
import { cssVar } from 'antd-style';
|
||||
import {
|
||||
BoldIcon,
|
||||
CodeXmlIcon,
|
||||
ItalicIcon,
|
||||
ListIcon,
|
||||
ListOrderedIcon,
|
||||
ListTodoIcon,
|
||||
MessageSquareQuote,
|
||||
SigmaIcon,
|
||||
SquareDashedBottomCodeIcon,
|
||||
StrikethroughIcon,
|
||||
UnderlineIcon,
|
||||
} from 'lucide-react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const TypoBar = memo<{ editor?: IEditor }>(({ editor }) => {
|
||||
const { t } = useTranslation('editor');
|
||||
const editorState = useEditorState(editor);
|
||||
|
||||
const items: ChatInputActionsProps['items'] = useMemo(
|
||||
() =>
|
||||
[
|
||||
{
|
||||
active: editorState.isBold,
|
||||
icon: BoldIcon,
|
||||
key: 'bold',
|
||||
label: t('typobar.bold'),
|
||||
onClick: editorState.bold,
|
||||
tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.Bold).keys },
|
||||
},
|
||||
{
|
||||
active: editorState.isItalic,
|
||||
icon: ItalicIcon,
|
||||
key: 'italic',
|
||||
label: t('typobar.italic'),
|
||||
onClick: editorState.italic,
|
||||
tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.Italic).keys },
|
||||
},
|
||||
{
|
||||
active: editorState.isUnderline,
|
||||
icon: UnderlineIcon,
|
||||
key: 'underline',
|
||||
label: t('typobar.underline'),
|
||||
onClick: editorState.underline,
|
||||
tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.Underline).keys },
|
||||
},
|
||||
{
|
||||
active: editorState.isStrikethrough,
|
||||
icon: StrikethroughIcon,
|
||||
key: 'strikethrough',
|
||||
label: t('typobar.strikethrough'),
|
||||
onClick: editorState.strikethrough,
|
||||
tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.Strikethrough).keys },
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
|
||||
{
|
||||
icon: ListIcon,
|
||||
key: 'bulletList',
|
||||
label: t('typobar.bulletList'),
|
||||
onClick: editorState.bulletList,
|
||||
tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.BulletList).keys },
|
||||
},
|
||||
{
|
||||
icon: ListOrderedIcon,
|
||||
key: 'numberlist',
|
||||
label: t('typobar.numberList'),
|
||||
onClick: editorState.numberList,
|
||||
tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.NumberList).keys },
|
||||
},
|
||||
{
|
||||
icon: ListTodoIcon,
|
||||
key: 'tasklist',
|
||||
label: t('typobar.taskList'),
|
||||
onClick: editorState.checkList,
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
active: editorState.isBlockquote,
|
||||
icon: MessageSquareQuote,
|
||||
key: 'blockquote',
|
||||
label: t('typobar.blockquote'),
|
||||
onClick: editorState.blockquote,
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
icon: SigmaIcon,
|
||||
key: 'math',
|
||||
label: t('typobar.tex'),
|
||||
onClick: editorState.insertMath,
|
||||
},
|
||||
{
|
||||
active: editorState.isCode,
|
||||
icon: CodeXmlIcon,
|
||||
key: 'code',
|
||||
label: t('typobar.code'),
|
||||
onClick: editorState.code,
|
||||
tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.CodeInline).keys },
|
||||
},
|
||||
{
|
||||
icon: SquareDashedBottomCodeIcon,
|
||||
key: 'codeblock',
|
||||
label: t('typobar.codeblock'),
|
||||
onClick: editorState.codeblock,
|
||||
},
|
||||
editorState.isCodeblock && {
|
||||
children: (
|
||||
<CodeLanguageSelect
|
||||
onSelect={(value) => editorState.updateCodeblockLang(value)}
|
||||
value={editorState.codeblockLang}
|
||||
/>
|
||||
),
|
||||
disabled: !editorState.isCodeblock,
|
||||
key: 'codeblockLang',
|
||||
},
|
||||
].filter(Boolean) as ChatInputActionsProps['items'],
|
||||
[editorState],
|
||||
);
|
||||
|
||||
return (
|
||||
<ChatInputActionBar
|
||||
left={<ChatInputActions items={items} />}
|
||||
style={{
|
||||
background: cssVar.colorFillQuaternary,
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 8,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
TypoBar.displayName = 'TypoBar';
|
||||
|
||||
export default TypoBar;
|
||||
@@ -7,7 +7,10 @@ import { useConversationStore } from '@/features/Conversation/store';
|
||||
|
||||
import { type ChatItemProps } from '../../type';
|
||||
|
||||
const EditableModal = dynamic(() => import('./EditableModal'), { ssr: false });
|
||||
const EditorModal = dynamic(
|
||||
() => import('@/features/EditorModal').then((mode) => mode.EditorModal),
|
||||
{ ssr: false },
|
||||
);
|
||||
|
||||
export const MSG_CONTENT_CLASSNAME = 'msg_content_flag';
|
||||
|
||||
@@ -60,13 +63,6 @@ const MessageContent = memo<MessageContentProps>(
|
||||
s.updateMessageContent,
|
||||
]);
|
||||
|
||||
const onChange = useCallback(
|
||||
(value: string) => {
|
||||
updateMessageContent(id, value);
|
||||
},
|
||||
[id, updateMessageContent],
|
||||
);
|
||||
|
||||
const onEditingChange = useCallback(
|
||||
(edit: boolean) => toggleMessageEditing(id, edit),
|
||||
[id, toggleMessageEditing],
|
||||
@@ -90,10 +86,13 @@ const MessageContent = memo<MessageContentProps>(
|
||||
</Flexbox>
|
||||
<Suspense fallback={null}>
|
||||
{editing && (
|
||||
<EditableModal
|
||||
editing={editing}
|
||||
onChange={onChange}
|
||||
onEditingChange={onEditingChange}
|
||||
<EditorModal
|
||||
onCancel={() => onEditingChange(false)}
|
||||
onConfirm={async (value) => {
|
||||
await updateMessageContent(id, value);
|
||||
onEditingChange(false);
|
||||
}}
|
||||
open={editing}
|
||||
value={message ? String(message) : ''}
|
||||
/>
|
||||
)}
|
||||
|
||||
81
src/features/EditorModal/EditorCanvas.tsx
Normal file
81
src/features/EditorModal/EditorCanvas.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import {
|
||||
ReactCodePlugin,
|
||||
ReactCodemirrorPlugin,
|
||||
ReactHRPlugin,
|
||||
ReactLinkPlugin,
|
||||
ReactListPlugin,
|
||||
ReactMathPlugin,
|
||||
ReactTablePlugin,
|
||||
} from '@lobehub/editor';
|
||||
import { Editor, useEditor } from '@lobehub/editor/react';
|
||||
import { Flexbox } from '@lobehub/ui';
|
||||
import { FC } from 'react';
|
||||
|
||||
import TypoBar from './Typobar';
|
||||
|
||||
interface EditorCanvasProps {
|
||||
onChange?: (value: string) => void;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
const EditorCanvas: FC<EditorCanvasProps> = ({ value, onChange }) => {
|
||||
const editor = useEditor();
|
||||
return (
|
||||
<>
|
||||
<TypoBar editor={editor} />
|
||||
<Flexbox
|
||||
onClick={() => {
|
||||
editor?.focus();
|
||||
}}
|
||||
padding={16}
|
||||
style={{ cursor: 'text', maxHeight: '80vh', minHeight: '50vh', overflowY: 'auto' }}
|
||||
>
|
||||
<div
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
<Editor
|
||||
autoFocus
|
||||
content={''}
|
||||
editor={editor}
|
||||
onInit={(editor) => {
|
||||
if (!editor || !value) return;
|
||||
try {
|
||||
editor?.setDocument('markdown', value);
|
||||
} catch (e) {
|
||||
console.error('setDocument error:', e);
|
||||
}
|
||||
}}
|
||||
onTextChange={(editor) => {
|
||||
try {
|
||||
const newValue = editor.getDocument('markdown') as unknown as string;
|
||||
onChange?.(newValue);
|
||||
} catch (e) {
|
||||
console.error('getDocument error:', e);
|
||||
onChange?.('');
|
||||
}
|
||||
}}
|
||||
plugins={[
|
||||
ReactListPlugin,
|
||||
ReactCodePlugin,
|
||||
ReactCodemirrorPlugin,
|
||||
ReactHRPlugin,
|
||||
ReactLinkPlugin,
|
||||
ReactTablePlugin,
|
||||
ReactMathPlugin,
|
||||
]}
|
||||
style={{
|
||||
paddingBottom: 120,
|
||||
}}
|
||||
type={'text'}
|
||||
variant={'chat'}
|
||||
/>
|
||||
</div>
|
||||
</Flexbox>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditorCanvas;
|
||||
28
src/features/EditorModal/TextareCanvas.tsx
Normal file
28
src/features/EditorModal/TextareCanvas.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { TextArea } from '@lobehub/ui';
|
||||
import { FC } from 'react';
|
||||
|
||||
interface EditorCanvasProps {
|
||||
onChange?: (value: string) => void;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
const EditorCanvas: FC<EditorCanvasProps> = ({ value, onChange }) => {
|
||||
return (
|
||||
<TextArea
|
||||
onChange={(e) => {
|
||||
onChange?.(e.target.value);
|
||||
}}
|
||||
style={{
|
||||
cursor: 'text',
|
||||
maxHeight: '80vh',
|
||||
minHeight: '50vh',
|
||||
overflowY: 'auto',
|
||||
padding: 16,
|
||||
}}
|
||||
value={value}
|
||||
variant={'borderless'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditorCanvas;
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
ChatInputActionBar,
|
||||
ChatInputActions,
|
||||
type ChatInputActionsProps,
|
||||
CodeLanguageSelect,
|
||||
} from '@lobehub/editor/react';
|
||||
import { cssVar } from 'antd-style';
|
||||
import {
|
||||
@@ -119,16 +118,6 @@ const TypoBar = memo<{ editor?: IEditor }>(({ editor }) => {
|
||||
label: t('typobar.codeblock'),
|
||||
onClick: editorState.codeblock,
|
||||
},
|
||||
editorState.isCodeblock && {
|
||||
children: (
|
||||
<CodeLanguageSelect
|
||||
onSelect={(value) => editorState.updateCodeblockLang(value)}
|
||||
value={editorState.codeblockLang}
|
||||
/>
|
||||
),
|
||||
disabled: !editorState.isCodeblock,
|
||||
key: 'codeblockLang',
|
||||
},
|
||||
].filter(Boolean) as ChatInputActionsProps['items'],
|
||||
[editorState],
|
||||
);
|
||||
51
src/features/EditorModal/index.tsx
Normal file
51
src/features/EditorModal/index.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Modal, ModalProps, createRawModal } from '@lobehub/ui';
|
||||
import { memo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { labPreferSelectors } from '@/store/user/slices/preference/selectors';
|
||||
|
||||
import EditorCanvas from './EditorCanvas';
|
||||
import TextareCanvas from './TextareCanvas';
|
||||
|
||||
interface EditorModalProps extends ModalProps {
|
||||
onConfirm?: (value: string) => Promise<void>;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export const EditorModal = memo<EditorModalProps>(({ value, onConfirm, ...rest }) => {
|
||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||
const { t } = useTranslation('common');
|
||||
const [v, setV] = useState(value);
|
||||
const enableRichRender = useUserStore(labPreferSelectors.enableInputMarkdown);
|
||||
|
||||
const Canvas = enableRichRender ? EditorCanvas : TextareCanvas;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
cancelText={t('cancel')}
|
||||
closable={false}
|
||||
confirmLoading={confirmLoading}
|
||||
destroyOnHidden
|
||||
okText={t('ok')}
|
||||
onOk={async () => {
|
||||
setConfirmLoading(true);
|
||||
await onConfirm?.(v || '');
|
||||
setConfirmLoading(false);
|
||||
}}
|
||||
styles={{
|
||||
body: {
|
||||
overflow: 'hidden',
|
||||
padding: 0,
|
||||
},
|
||||
}}
|
||||
title={null}
|
||||
width={'min(90vw, 920px)'}
|
||||
{...rest}
|
||||
>
|
||||
<Canvas onChange={(v) => setV(v)} value={v} />
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
|
||||
export const createEditorModal = (props: EditorModalProps) => createRawModal(EditorModal, props);
|
||||
@@ -7,12 +7,7 @@ import {
|
||||
INSERT_HEADING_COMMAND,
|
||||
getHotkeyById,
|
||||
} from '@lobehub/editor';
|
||||
import {
|
||||
ChatInputActions,
|
||||
type ChatInputActionsProps,
|
||||
CodeLanguageSelect,
|
||||
FloatActions,
|
||||
} from '@lobehub/editor/react';
|
||||
import { ChatInputActions, type ChatInputActionsProps, FloatActions } from '@lobehub/editor/react';
|
||||
import { Block } from '@lobehub/ui';
|
||||
import { createStaticStyles, cssVar } from 'antd-style';
|
||||
import {
|
||||
@@ -283,17 +278,6 @@ const TypoBar = memo<ToolbarProps>(({ floating, style, className }) => {
|
||||
label: t('typobar.codeblock'),
|
||||
onClick: editorState.codeblock,
|
||||
},
|
||||
!floating &&
|
||||
editorState.isCodeblock && {
|
||||
children: (
|
||||
<CodeLanguageSelect
|
||||
onSelect={(value) => editorState.updateCodeblockLang(value)}
|
||||
value={editorState.codeblockLang}
|
||||
/>
|
||||
),
|
||||
disabled: !editorState.isCodeblock,
|
||||
key: 'codeblockLang',
|
||||
},
|
||||
];
|
||||
|
||||
return baseItems.filter(Boolean) as ChatInputActionsProps['items'];
|
||||
|
||||
Reference in New Issue
Block a user