From 7831bc23f0cdc09bd3a9d5242a79904709a6c84f Mon Sep 17 00:00:00 2001 From: SkyJourney Date: Thu, 30 Jul 2026 12:39:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8D=87=E7=BA=A7=20CodeMirror=20?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=99=A8=E5=B9=B6=E6=96=B0=E5=A2=9E=20Markdo?= =?UTF-8?q?wn=20=E5=B7=A5=E5=85=B7=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/package.json | 5 + apps/web/src/App.tsx | 54 +- apps/web/src/MarkdownEditor.tsx | 158 ++++++ apps/web/src/MarkdownTableTool.tsx | 208 +++++++ apps/web/src/MarkdownToolbar.tsx | 115 ++++ apps/web/src/markdown-editor-commands.ts | 514 ++++++++++++++++++ apps/web/src/styles.css | 136 ++++- .../tests/markdown-editor-commands.test.ts | 87 +++ docs/PROGRESS.md | 13 +- docs/V0.6.0_DESIGN.md | 29 +- package-lock.json | 319 +++++++++++ 11 files changed, 1603 insertions(+), 35 deletions(-) create mode 100644 apps/web/src/MarkdownEditor.tsx create mode 100644 apps/web/src/MarkdownTableTool.tsx create mode 100644 apps/web/src/MarkdownToolbar.tsx create mode 100644 apps/web/src/markdown-editor-commands.ts create mode 100644 apps/web/tests/markdown-editor-commands.test.ts diff --git a/apps/web/package.json b/apps/web/package.json index 5dbb1bd..a4738ce 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -10,9 +10,14 @@ "typecheck": "tsc -b --pretty false" }, "dependencies": { + "@codemirror/commands": "^6.10.4", + "@codemirror/lang-markdown": "^6.5.1", + "@codemirror/state": "^6.7.1", + "@codemirror/view": "^6.43.7", "@md-to-pdf/core": "0.1.0", "@md-to-pdf/markdown-echarts": "0.1.0", "@md-to-pdf/preview-engine": "0.1.0", + "@uiw/react-codemirror": "^4.25.11", "highlight.js": "^11.11.1", "katex": "^0.16.28", "pdfjs-dist": "6.1.200", diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 3c84114..f322a6c 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -9,6 +9,7 @@ import { useRef, useState } from "react"; +import type { EditorView } from "@codemirror/view"; import { createPagedDocumentPayload, getPaperDimensionsMm, @@ -71,6 +72,11 @@ import { applyThemeDefaults, selectTheme } from "./theme-margins"; +import { + MarkdownEditor, + type MarkdownEditorHandle +} from "./MarkdownEditor"; +import { MarkdownToolbar } from "./MarkdownToolbar"; const PrecisePdfPreview = lazy(async () => { const module = await import("./PrecisePdfPreview"); @@ -107,6 +113,11 @@ const initialDocumentKind: DocumentKind = startsWithBlankMarkdown export function App() { const [markdown, setMarkdown] = useState(initialMarkdown); + const [editorDocumentVersion, setEditorDocumentVersion] = + useState(0); + const [editorView, setEditorView] = useState( + null + ); const [fileName, setFileName] = useState(initialFileName); const [documentKind, setDocumentKind] = useState(initialDocumentKind); @@ -141,7 +152,7 @@ export function App() { current: 0, total: 0 }); - const editorRef = useRef(null); + const editorRef = useRef(null); const markdownFileInputRef = useRef(null); const moreMenuRef = useRef(null); const previewScrollRef = useRef(null); @@ -158,6 +169,11 @@ export function App() { const themeRefreshTargetRef = useRef(undefined); documentDirtyRef.current = documentDirty; + function replaceMarkdownDocument(content: string) { + setMarkdown(content); + setEditorDocumentVersion((version) => version + 1); + } + useEffect(() => { const bridge = window.mdToPdfDesktop; if (!bridge) { @@ -191,7 +207,7 @@ export function App() { return; } setAppError(""); - setMarkdown(opened.markdown); + replaceMarkdownDocument(opened.markdown); setFileName(opened.fileName); setDocumentKind("file"); setSavedMarkdown(opened.markdown); @@ -658,9 +674,13 @@ export function App() { function synchronizeScroll(origin: "editor" | "preview") { const source = - origin === "editor" ? editorRef.current : getPreviewScroller(); + origin === "editor" + ? editorRef.current?.getScrollElement() + : getPreviewScroller(); const target = - origin === "editor" ? getPreviewScroller() : editorRef.current; + origin === "editor" + ? getPreviewScroller() + : editorRef.current?.getScrollElement(); if (!source || !target) { return; } @@ -866,7 +886,7 @@ export function App() { try { setAppError(""); const content = await file.text(); - setMarkdown(content); + replaceMarkdownDocument(content); setFileName(file.name); setDocumentKind("file"); setSavedMarkdown(content); @@ -885,7 +905,7 @@ export function App() { if (!opened) { return; } - setMarkdown(opened.markdown); + replaceMarkdownDocument(opened.markdown); setFileName(opened.fileName); setDocumentKind("file"); setSavedMarkdown(opened.markdown); @@ -923,7 +943,7 @@ export function App() { try { setAppError(""); await window.mdToPdfDesktop?.startNewMarkdown(); - setMarkdown(""); + replaceMarkdownDocument(""); setFileName(""); setDocumentKind("new"); setSavedMarkdown(""); @@ -1034,7 +1054,7 @@ export function App() { try { setAppError(""); await window.mdToPdfDesktop?.startNewMarkdown(); - setMarkdown(sample.markdown); + replaceMarkdownDocument(sample.markdown); setFileName(sample.fileName); setDocumentKind("sample"); setSavedMarkdown(sample.markdown); @@ -1078,7 +1098,7 @@ export function App() { try { setAppError(""); await window.mdToPdfDesktop?.startNewMarkdown(); - setMarkdown(tutorial.markdown); + replaceMarkdownDocument(tutorial.markdown); setFileName(tutorial.fileName); setDocumentKind("sample"); setSavedMarkdown(tutorial.markdown); @@ -1522,12 +1542,13 @@ export function App() { 收起 -