Conversation
|
🚀 Cloudflare Workers Preview has been successfully deployed! Preview URL: https://md-pr-1484.doocs.workers.dev Built with commit 7f64864 |
|
🚀 Surge Preview has been successfully deployed! Preview URL: https://doocs-md-preview-pr-1484.surge.sh Built with commit 7f64864 |
- Remove katex npm dependency, use existing window.MathJax instead - Add renderWithMathJax() helper in FormulaEditorDialog for preview and snippet rendering - Add escapeHtml to packages/core/src/utils/basicHelpers.ts - Handle formula click in preview area to open formula editor (useCursorSync) - Fix mobile scroll: formula library panel now scrollable on small screens
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a formula editor workflow in the Vue web app while preserving the existing MathJax-based rendering in @md/core. It adds entry points to open a modal dialog for editing/inserting LaTeX and enables opening the editor by clicking formulas in the preview.
Changes:
- Add a new Formula Editor dialog (LaTeX input + live preview + snippet library) and wire it into the main editor view.
- Add UI triggers (context menu + insert dropdown) to open the formula editor using the current editor selection.
- Embed math source metadata into rendered formula HTML (
data-math-*) and use it to open the editor from preview clicks.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/core/src/utils/basicHelpers.ts | Adds an HTML escaping helper used for safe attribute embedding. |
| packages/core/src/extensions/katex.ts | Adds data-math-* attributes to rendered math nodes to support “click-to-edit” in the web app. |
| apps/web/vite.config.ts | Configures Vue compiler to treat math-field as a custom element. |
| apps/web/src/views/CodemirrorEditor.vue | Mounts the new FormulaEditorDialog in the editor view. |
| apps/web/src/utils/formula.ts | Adds helpers to unwrap/normalize/wrap LaTeX formulas for editor integration. |
| apps/web/src/stores/ui.ts | Adds global UI state/actions for opening/closing the formula editor dialog. |
| apps/web/src/stores/editor.ts | Adds replaceText() helper used by the formula editor to update existing source. |
| apps/web/src/composables/useCursorSync.ts | Opens the formula editor when clicking rendered math in the preview. |
| apps/web/src/components/editor/FormulaEditorDialog.vue | Implements the formula editor modal UI and insertion/editing behavior. |
| apps/web/src/components/editor/EditorContextMenu.vue | Adds a context-menu entry to open the formula editor. |
| apps/web/src/components/editor/editor-header/InsertDropdown.vue | Adds a header insert-menu entry to open the formula editor. |
Comment on lines
+89
to
+96
| function openFormulaEditor() { | ||
| const selection = normalizeFormulaInput(editorStore.getSelection()) | ||
| uiStore.openFormulaEditor({ | ||
| value: selection.latex, | ||
| displayMode: selection.displayMode, | ||
| sourceRaw: selection.sourceRaw, | ||
| }) | ||
| } |
Comment on lines
+19
to
+26
| function openFormulaEditor() { | ||
| const selection = normalizeFormulaInput(editorStore.getSelection()) | ||
| uiStore.openFormulaEditor({ | ||
| value: selection.latex, | ||
| displayMode: selection.displayMode, | ||
| sourceRaw: selection.sourceRaw, | ||
| }) | ||
| } |
Comment on lines
+54
to
+68
| export function normalizeFormulaInput(text: string): FormulaInput { | ||
| const trimmed = text.trim() | ||
| if (!trimmed) { | ||
| return { latex: ``, displayMode: false, sourceRaw: null } | ||
| } | ||
|
|
||
| const unwrapped = unwrapFormula(trimmed) | ||
| const isWrapped = unwrapped.latex !== trimmed || unwrapped.displayMode || /^(\$\$|\\\[|\\\(|\$)/.test(trimmed) | ||
|
|
||
| return { | ||
| latex: unwrapped.latex, | ||
| displayMode: unwrapped.displayMode || trimmed.includes(`\n`), | ||
| sourceRaw: isWrapped ? trimmed : null, | ||
| } | ||
| } |
Comment on lines
+67
to
+81
| const replaceText = (oldText: string, newText: string) => { | ||
| if (!editor.value || !oldText) | ||
| return false | ||
|
|
||
| const content = editor.value.state.doc.toString() | ||
| const from = content.indexOf(oldText) | ||
| if (from === -1) | ||
| return false | ||
|
|
||
| editor.value.dispatch({ | ||
| changes: { from, to: from + oldText.length, insert: newText }, | ||
| }) | ||
| editor.value.focus() | ||
| return true | ||
| } |
Comment on lines
+170
to
+185
| function renderWithMathJax(latex: string, display: boolean): string { | ||
| try { | ||
| // @ts-expect-error MathJax is a global variable | ||
| window.MathJax.texReset() | ||
| // @ts-expect-error MathJax is a global variable | ||
| const mjxContainer = window.MathJax.tex2svg(latex, { display }) | ||
| const svg = mjxContainer.firstChild as SVGElement | ||
| svg.style.display = display ? `block` : `initial` | ||
| svg.style.setProperty(`max-width`, `300vw`, `important`) | ||
| svg.style.flexShrink = `0` | ||
| return svg.outerHTML | ||
| } | ||
| catch { | ||
| return `<div class="text-sm text-destructive break-all">${escapeHtml(latex)}</div>` | ||
| } | ||
| } |
| class="rounded-lg border bg-background px-3 py-2 text-left text-sm hover:border-primary hover:bg-primary/5 transition-colors" | ||
| @click="insertSnippet(snippet)" | ||
| > | ||
| <span class="flex items-center overflow-x-auto whitespace-nowrap font-mono h-15" v-html="renderWithMathJax(snippet, false)" /> |
Comment on lines
+40
to
+45
| if (!display) { | ||
| // 新主题系统:使用 class 而非内联样式 | ||
| return `<span class="katex-inline">${svg.outerHTML}</span>` | ||
| return `<span class="katex-inline" data-math-latex="${escapeHtml(token.text)}" data-math-display="false" data-math-raw="${escapeHtml(token.raw ?? token.text)}">${svg.outerHTML}</span>` | ||
| } | ||
|
|
||
| return `<section class="katex-block">${svg.outerHTML}</section>` | ||
| return `<section class="katex-block" data-math-latex="${escapeHtml(token.text)}" data-math-display="true" data-math-raw="${escapeHtml(token.raw ?? token.text)}">${svg.outerHTML}</section>` |
- EditorContextMenu/InsertDropdown: don't pass sourceRaw for selection-based editing, use replaceSelection() instead - formula.ts: fix isWrapped detection to only check if unwrapFormula actually stripped delimiters - editor.ts: replaceText() now replaces the occurrence nearest to the current cursor - FormulaEditorDialog: cache MathJax snippet renders to avoid redundant re-renders - katex.ts: remove unused data-math-latex attribute to reduce HTML output size
|
🗑️ Cloudflare Workers preview deployment has been cleaned up. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
close #1483