diff --git a/src/pages/Note/Hlexical/index.jsx b/src/pages/Note/Hlexical/index.jsx index 2e7332b..4b24602 100644 --- a/src/pages/Note/Hlexical/index.jsx +++ b/src/pages/Note/Hlexical/index.jsx @@ -15,31 +15,28 @@ import {AutoLinkNode, LinkNode} from "@lexical/link"; import {MarkdownShortcutPlugin} from "@lexical/react/LexicalMarkdownShortcutPlugin"; import { TRANSFORMERS, $convertFromMarkdownString, - $convertToMarkdownString, } from "@lexical/markdown"; import "./index.less" -import {useEffect, useId, useState} from 'react'; +import {useEffect, useState} from 'react'; import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext'; -import {importFile, overWriteFile, saveFileWithName} from "../../../utils/File" -import {$getRoot, CLEAR_HISTORY_COMMAND, $createTextNode,} from "lexical"; -import md5 from "md5" +import {importFile} from "../../../utils/File" + import {isEmpty} from "../../../utils/ObjectUtils"; -import {useDispatch, useSelector} from "react-redux"; -import {updatedSavedFile} from "../../../redux/tableBarItem_reducer"; import {ListPlugin} from "@lexical/react/LexicalListPlugin"; import {LinkPlugin} from "@lexical/react/LexicalLinkPlugin"; import AutoLinkPlugin from "./plugins/AutoLinkPlugin"; import ListMaxIndentLevelPlugin from "./plugins/ListMaxIndentLevelPlugin"; import CodeHighlightPlugin from "./plugins/CodeHighlightPlugin"; -import {getFileNameByPath} from "../../../utils/PathOperate"; +import ImportFilePlugin from "./plugins/ImportFilePlugin"; +import {TablePlugin} from "@lexical/react/LexicalTablePlugin"; +import SaveFilePlugin from "./plugins/SaveFilePlugin"; -const {ipcRenderer} = window.require('electron') function Placeholder() { return
Enter some rich text...
; } -const editorConfig = { +let editorConfig = { // The editor theme theme: ExampleTheme, // Handling of errors during update @@ -61,133 +58,21 @@ const editorConfig = { LinkNode ] }; - -function OnChangePlugin({onChange}) { - const [editor] = useLexicalComposerContext(); - console.log('editor', editor) - useEffect(() => { - return editor.registerUpdateListener(({editorState}) => { - onChange(editorState); - }); - }, [editor, onChange]); -} - -function ImportFilePlugin(props) { - const [editor] = useLexicalComposerContext(); - useEffect(() => { - if (props.filePath) { - importFile(props.filePath).then(value => { - if (isEmpty(value)) { - return - } - if (props.filePath.endsWith(".md")) { - const root = $getRoot(); - const firstChild = root.getFirstChild(); - if ($isCodeNode(firstChild) && firstChild.getLanguage() === 'markdown') { - $convertFromMarkdownString( - firstChild.getTextContent(), - PLAYGROUND_TRANSFORMERS, - ); - } else { - const markdown = $convertToMarkdownString(PLAYGROUND_TRANSFORMERS); - root - .clear() - .append( - $createCodeNode('markdown').append($createTextNode(markdown)), - ); - } - root.selectEnd(); - } else { - const editorState = editor.parseEditorState( - JSON.stringify(JSON.parse(value.toString()).editorState) - ); - editor.setEditorState(editorState); - editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined); - } - }).catch(error => - console.error(error) - ) - } - }, []) -} - - // 从字符串化 JSON 设置编辑器状态 // const editorState = editor.parseEditorState(editorStateJSONString); // editor.setEditorState(editorState); - export default function Hlexical(props) { console.log("Hlexical(props):this.props.filePath:", props.filePath) - const [editorState, setEditorState] = useState(); - const dispatch = useDispatch(); - - function onChange(editorState) { - // Call toJSON on the EditorState object, which produces a serialization safe string - const editorStateJSON = editorState.toJSON(); - console.log('onChange-editorStateJSON') - // However, we still have a JavaScript object, so we need to convert it to an actual string with JSON.stringify - setEditorState(JSON.stringify(editorStateJSON)); - } - - let pushHotKey = useSelector(state => state.pushHotkeys.data); - let activeKey = useSelector(state => state.tableBarItem.activeKey); - const saveFile = (event, args) => { - console.log("event,args:", event, args) - console.log("触发保存pushHotKey:", pushHotKey) - if (pushHotKey !== "CTRL+S") { - return - } - let filePath = props.filePath; - if (filePath !== activeKey) { - console.log("文件不同", filePath, activeKey) - return; - } - console.log("触发保存filePath:", filePath) - if (isEmpty(editorState)) { - return - } - const editorStateSave = {"editorState": JSON.parse(editorState)}; - let resultSave = JSON.stringify(editorStateSave); - - // 如果文件地址为空需要用户选择目录并设置文件。 - if (!filePath) { - let saveDialogReturnValuePromise = saveFileWithName(); - console.log("saveDialogReturnValuePromise", saveDialogReturnValuePromise) - saveDialogReturnValuePromise.then(result => { - if (!result.canceled) { - let fileKey = getFileNameByPath(result.filePath) - if (isEmpty(fileKey)){ - fileKey = fileKey+".lexical" - } - overWriteFile(fileKey, resultSave) - // 修改当前文件名 - dispatch(updatedSavedFile({filePath: result.filePath})) - // 文件目录更新 - dispatch() - } - }) - return - } - importFile(filePath).then(value => { - let save = (isEmpty(value)) || md5(resultSave) !== md5(JSON.stringify(JSON.parse(value.toString()))); - if (save) { - console.log("保存重写" + filePath) - overWriteFile(filePath, resultSave) + if (props.filePath.endsWith(".md")){ + importFile(props.filePath).then(value => { + if (isEmpty(value)) { + return } + editorConfig={...editorConfig,editorState: () => $convertFromMarkdownString(value.toString(), TRANSFORMERS)} }).catch(error => console.error(error) ) } - useEffect(() => { - ipcRenderer.on("pushHotkeys", saveFile); - return () => { - console.log("销毁取消监听"); - ipcRenderer.removeListener("pushHotkeys", saveFile) - }; - } - ) - - return (
@@ -202,17 +87,17 @@ export default function Hlexical(props) { {/*黑窗口动态记录当前操作*/} {/**/} - + {/*markdown 快捷键*/} - + {/*文件操作导入文件*/} {/**/}
diff --git a/src/pages/Note/Hlexical/index.less b/src/pages/Note/Hlexical/index.less index 25103b1..d5adae1 100644 --- a/src/pages/Note/Hlexical/index.less +++ b/src/pages/Note/Hlexical/index.less @@ -147,8 +147,8 @@ body { margin-top: 8px; margin-bottom: 8px; tab-size: 2; - /* white-space: pre; */ - overflow-x: auto; + white-space: pre; + overflow: auto; position: relative; } diff --git a/src/pages/Note/Hlexical/plugins/ImportFilePlugin.js b/src/pages/Note/Hlexical/plugins/ImportFilePlugin.js new file mode 100644 index 0000000..b1a67b3 --- /dev/null +++ b/src/pages/Note/Hlexical/plugins/ImportFilePlugin.js @@ -0,0 +1,37 @@ +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import {useEffect} from "react"; +import {importFile} from "../../../../utils/File"; +import {isEmpty} from "../../../../utils/ObjectUtils"; +import {CLEAR_HISTORY_COMMAND} from "lexical"; +import {TRANSFORMERS, $convertFromMarkdownString, $convertToMarkdownString,} from "@lexical/markdown"; +export default function ImportFilePlugin(props) { + const [editor] = useLexicalComposerContext(); + useEffect(() => { + if (props.filePath) { + importFile(props.filePath).then(value => { + if (isEmpty(value)) { + return + } + if (props.filePath.endsWith(".md")) { + console.log("$convertFromMarkdownString(value.toString(), TRANSFORMERS)",editor) + // const editorState = editor.parseEditorState($convertFromMarkdownString(value.toString(), TRANSFORMERS)) + // editor.setEditorState(editorState); + // editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined); + + editor.update(() => { + $convertFromMarkdownString(value.toString(), TRANSFORMERS); + }) + + } else { + const editorState = editor.parseEditorState( + JSON.stringify(JSON.parse(value.toString()).editorState) + ); + editor.setEditorState(editorState); + editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined); + } + }).catch(error => + console.error(error) + ) + } + }, []) +} diff --git a/src/pages/Note/Hlexical/plugins/SaveFilePlugin.js b/src/pages/Note/Hlexical/plugins/SaveFilePlugin.js new file mode 100644 index 0000000..3cad213 --- /dev/null +++ b/src/pages/Note/Hlexical/plugins/SaveFilePlugin.js @@ -0,0 +1,111 @@ +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import {Fragment, useEffect, useState} from "react"; +import {importFile, overWriteFile, saveFileWithName} from "../../../../utils/File"; +import {isEmpty} from "../../../../utils/ObjectUtils"; +import {CLEAR_HISTORY_COMMAND} from "lexical"; +import {TRANSFORMERS, $convertFromMarkdownString, $convertToMarkdownString,} from "@lexical/markdown"; +import {getFileNameByPath} from "../../../../utils/PathOperate"; +import {updatedSavedFile} from "../../../../redux/tableBarItem_reducer"; +import {useDispatch, useSelector} from "react-redux"; +import md5 from "md5" +import {message} from "antd"; +const {ipcRenderer} = window.require('electron') +export default function SaveFilePlugin(props) { + let activeKey = useSelector(state => state.tableBarItem.activeKey); + const dispatch = useDispatch(); + const [messageApi,contextHolder] = message.useMessage(); + const [editor] = useLexicalComposerContext(); + const [editorState,setEditorState]=useState(); + + function onChange(editorState) { + if (props.filePath.endsWith(".md")){ + let read = editorState.read(() => $convertToMarkdownString(TRANSFORMERS)); + setEditorState(read) + }else { + // Call toJSON on the EditorState object, which produces a serialization safe string + const editorStateJSON = editorState.toJSON(); + console.log('onChange-editorStateJSON') + // However, we still have a JavaScript object, so we need to convert it to an actual string with JSON.stringify + setEditorState(JSON.stringify(editorStateJSON)); + } + } + + const saveFile = (event, args) => { + console.log("event,args:", event, args) + if (args !== "CTRL+S") { + return + } + let filePath = props.filePath; + if (filePath !== activeKey) { + console.log("文件不同", filePath, activeKey) + return; + } + console.log("触发保存filePath:", filePath) + if (isEmpty(editorState)) { + return + } + let resultSave; + if (props.filePath.endsWith(".md")){ + resultSave=editorState + }else { + const editorStateSave = {"editorState": JSON.parse(editorState)}; + resultSave = JSON.stringify(editorStateSave); + } + + // 如果文件地址为空需要用户选择目录并设置文件。 + if (!filePath) { + let saveDialogReturnValuePromise = saveFileWithName(); + console.log("saveDialogReturnValuePromise", saveDialogReturnValuePromise) + saveDialogReturnValuePromise.then(result => { + if (!result.canceled) { + let fileKey = getFileNameByPath(result.filePath) + if (isEmpty(fileKey)){ + fileKey = fileKey+".lexical" + } + overWriteFile(fileKey, resultSave) + // 修改当前文件名 + dispatch(updatedSavedFile({filePath: result.filePath})) + // 文件目录更新 + dispatch() + } + }) + return + } + importFile(filePath).then(value => { + let save + if (props.filePath.endsWith(".md")){ + // editorState + save = (isEmpty(value)) || md5(resultSave) !== md5(value.toString()); + }else { + save = (isEmpty(value)) || md5(resultSave) !== md5(JSON.stringify(JSON.parse(value.toString()))); + } + + if (save) { + overWriteFile(filePath, resultSave) + console.log("保存成功"+ filePath) + messageApi.open({type:"success",content:"保存成功:" + filePath}) + } + }).catch(error => + console.error(error) + ) + } + + useEffect(() => { + ipcRenderer.on("pushHotkeys", saveFile); + let updateListener = editor.registerUpdateListener(({editorState}) => { + console.log("SaveFilePlugin:editorState",editorState) + onChange(editorState); + }) + return () => { + console.log("销毁取消监听"); + updateListener() + ipcRenderer.removeListener("pushHotkeys", saveFile) + }; + },[editor,onChange] + ) + return ( + + {contextHolder} + + ) +} diff --git a/src/redux/dirMessage_reducer.js b/src/redux/dirMessage_reducer.js index f64eeff..00a412d 100644 --- a/src/redux/dirMessage_reducer.js +++ b/src/redux/dirMessage_reducer.js @@ -19,6 +19,7 @@ export const dirMessageSlice = createSlice({ // selectDirKey:"" }, reducers: { + // 一级目录有相同的就要和并,从第一次展开处和并,不需要展示上一级。 dirAdd: (state, action) => { console.log("dirMessage:dirAdd", state, action) if (action.payload) {