mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-29 03:23:25 +08:00 
			
		
		
		
	Merge pull request #2003 from TriliumNext/math-edit
feat(math): support multi-line formula editing
This commit is contained in:
		
						commit
						6a29fae7c0
					
				| @ -27,6 +27,18 @@ export default class MathEditing extends Plugin { | |||||||
| 
 | 
 | ||||||
| 	public init(): void { | 	public init(): void { | ||||||
| 		const editor = this.editor; | 		const editor = this.editor; | ||||||
|  | 
 | ||||||
|  | 		const originalProcessor = editor.data.processor; | ||||||
|  | 		const originalToView = originalProcessor.toView.bind(originalProcessor); | ||||||
|  | 		const mathSpanRegex = /<span class="math-tex">([\s\S]*?)<\/span>/g; | ||||||
|  | 		originalProcessor.toView = (data: string) => { | ||||||
|  | 			// Preprocessing: preserve line breaks inside math formulas by replacing \n with <!--LF-->
 | ||||||
|  | 			const processedData = data.replace(mathSpanRegex, (_, content) => | ||||||
|  | 				`<span class="math-tex">${content.replace(/\n/g, '___MATH_TEX_LF___')}</span>` | ||||||
|  | 			); | ||||||
|  | 			return originalToView(processedData); | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
| 		editor.commands.add( 'math', new MathCommand( editor ) ); | 		editor.commands.add( 'math', new MathCommand( editor ) ); | ||||||
| 
 | 
 | ||||||
| 		this._defineSchema(); | 		this._defineSchema(); | ||||||
| @ -120,8 +132,7 @@ export default class MathEditing extends Plugin { | |||||||
| 				model: ( viewElement, { writer } ) => { | 				model: ( viewElement, { writer } ) => { | ||||||
| 					const child = viewElement.getChild( 0 ); | 					const child = viewElement.getChild( 0 ); | ||||||
| 					if ( child?.is( '$text' ) ) { | 					if ( child?.is( '$text' ) ) { | ||||||
| 						const equation = child.data.trim(); | 						const equation = child.data.trim().replace(/___MATH_TEX_LF___/g, '\n'); | ||||||
| 
 |  | ||||||
| 						const params = Object.assign( extractDelimiters( equation ), { | 						const params = Object.assign( extractDelimiters( equation ), { | ||||||
| 							type: mathConfig.forceOutputType ? | 							type: mathConfig.forceOutputType ? | ||||||
| 								mathConfig.outputType : | 								mathConfig.outputType : | ||||||
|  | |||||||
| @ -110,6 +110,27 @@ export default class MathUI extends Plugin { | |||||||
| 			cancel(); | 			cancel(); | ||||||
| 		} ); | 		} ); | ||||||
| 
 | 
 | ||||||
|  | 		// Allow pressing Enter to submit changes, and use Shift+Enter to insert a new line
 | ||||||
|  | 		formView.keystrokes.set('enter', (data, cancel) => { | ||||||
|  | 			if (!data.shiftKey) { | ||||||
|  | 				formView.fire('submit'); | ||||||
|  | 				cancel(); | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		// Allow the textarea to be resizable
 | ||||||
|  | 		formView.mathInputView.fieldView.once('render', () => { | ||||||
|  | 			const textarea = formView.mathInputView.fieldView.element; | ||||||
|  | 			if (!textarea) return; | ||||||
|  | 			textarea.focus(); | ||||||
|  | 			Object.assign(textarea.style, { | ||||||
|  | 				resize: 'both', | ||||||
|  | 				height: '100px', | ||||||
|  | 				width: '400px', | ||||||
|  | 				minWidth: '100%', | ||||||
|  | 			}); | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
| 		return formView; | 		return formView; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,16 +1,16 @@ | |||||||
| import { ButtonView, createLabeledInputText, FocusCycler, LabelView, LabeledFieldView, submitHandler, SwitchButtonView, View, ViewCollection, type InputTextView, type FocusableView, Locale, FocusTracker, KeystrokeHandler } from 'ckeditor5'; | import { ButtonView, createLabeledTextarea, FocusCycler, LabelView, LabeledFieldView, submitHandler, SwitchButtonView, View, ViewCollection, type TextareaView, type FocusableView, Locale, FocusTracker, KeystrokeHandler } from 'ckeditor5'; | ||||||
| import { IconCheck, IconCancel } from "@ckeditor/ckeditor5-icons"; | import { IconCheck, IconCancel } from "@ckeditor/ckeditor5-icons"; | ||||||
| import { extractDelimiters, hasDelimiters } from '../utils.js'; | import { extractDelimiters, hasDelimiters } from '../utils.js'; | ||||||
| import MathView from './mathview.js'; | import MathView from './mathview.js'; | ||||||
| import '../../theme/mathform.css'; | import '../../theme/mathform.css'; | ||||||
| import type { KatexOptions } from '../typings-external.js'; | import type { KatexOptions } from '../typings-external.js'; | ||||||
| 
 | 
 | ||||||
| class MathInputView extends LabeledFieldView<InputTextView> { | class MathInputView extends LabeledFieldView<TextareaView> { | ||||||
| 	public value: null | string = null; | 	public value: null | string = null; | ||||||
| 	public isReadOnly = false; | 	public isReadOnly = false; | ||||||
| 
 | 
 | ||||||
| 	constructor( locale: Locale ) { | 	constructor( locale: Locale ) { | ||||||
| 		super( locale, createLabeledInputText ); | 		super( locale, createLabeledTextarea ); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -49,7 +49,7 @@ export default class MathView extends View { | |||||||
| 		this.setTemplate( { | 		this.setTemplate( { | ||||||
| 			tag: 'div', | 			tag: 'div', | ||||||
| 			attributes: { | 			attributes: { | ||||||
| 				class: [ 'ck', 'ck-math-preview' ] | 				class: [ 'ck', 'ck-math-preview', 'ck-reset_all-excluded' ] | ||||||
| 			} | 			} | ||||||
| 		} ); | 		} ); | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -94,7 +94,6 @@ export async function renderEquation( | |||||||
| 				el => { | 				el => { | ||||||
| 					renderMathJax3( equation, el, display, () => { | 					renderMathJax3( equation, el, display, () => { | ||||||
| 						if ( preview ) { | 						if ( preview ) { | ||||||
| 							moveAndScaleElement( element, el ); |  | ||||||
| 							el.style.visibility = 'visible'; | 							el.style.visibility = 'visible'; | ||||||
| 						} | 						} | ||||||
| 					} ); | 					} ); | ||||||
| @ -115,7 +114,6 @@ export async function renderEquation( | |||||||
| 						if ( preview && isMathJaxVersion2( MathJax ) ) { | 						if ( preview && isMathJaxVersion2( MathJax ) ) { | ||||||
| 							// eslint-disable-next-line new-cap
 | 							// eslint-disable-next-line new-cap
 | ||||||
| 							MathJax.Hub.Queue( () => { | 							MathJax.Hub.Queue( () => { | ||||||
| 								moveAndScaleElement( element, el ); |  | ||||||
| 								el.style.visibility = 'visible'; | 								el.style.visibility = 'visible'; | ||||||
| 							} ); | 							} ); | ||||||
| 						} | 						} | ||||||
| @ -139,7 +137,6 @@ export async function renderEquation( | |||||||
| 					} ); | 					} ); | ||||||
| 				} | 				} | ||||||
| 				if ( preview ) { | 				if ( preview ) { | ||||||
| 					moveAndScaleElement( element, el ); |  | ||||||
| 					el.style.visibility = 'visible'; | 					el.style.visibility = 'visible'; | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| @ -295,47 +292,7 @@ function getPreviewElement( | |||||||
| 		previewEl.setAttribute( 'id', previewUid ); | 		previewEl.setAttribute( 'id', previewUid ); | ||||||
| 		previewEl.classList.add( ...previewClassName ); | 		previewEl.classList.add( ...previewClassName ); | ||||||
| 		previewEl.style.visibility = 'hidden'; | 		previewEl.style.visibility = 'hidden'; | ||||||
| 		document.body.appendChild( previewEl ); | 		element.appendChild( previewEl ); | ||||||
| 
 |  | ||||||
| 		let ticking = false; |  | ||||||
| 
 |  | ||||||
| 		const renderTransformation = () => { |  | ||||||
| 			if ( !ticking ) { |  | ||||||
| 				window.requestAnimationFrame( () => { |  | ||||||
| 					if ( previewEl ) { |  | ||||||
| 						moveElement( element, previewEl ); |  | ||||||
| 						ticking = false; |  | ||||||
| 					} |  | ||||||
| 				} ); |  | ||||||
| 
 |  | ||||||
| 				ticking = true; |  | ||||||
| 			} |  | ||||||
| 		}; |  | ||||||
| 
 |  | ||||||
| 		// Create scroll listener for following
 |  | ||||||
| 		window.addEventListener( 'resize', renderTransformation ); |  | ||||||
| 		window.addEventListener( 'scroll', renderTransformation ); |  | ||||||
| 	} | 	} | ||||||
| 	return previewEl; | 	return previewEl; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| function moveAndScaleElement( parent: HTMLElement, child: HTMLElement ) { |  | ||||||
| 	// Move to right place
 |  | ||||||
| 	moveElement( parent, child ); |  | ||||||
| 
 |  | ||||||
| 	// Scale parent element same as preview
 |  | ||||||
| 	const domRect = child.getBoundingClientRect(); |  | ||||||
| 	parent.style.width = domRect.width + 'px'; |  | ||||||
| 	parent.style.height = domRect.height + 'px'; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function moveElement( parent: HTMLElement, child: HTMLElement ) { |  | ||||||
| 	const domRect = parent.getBoundingClientRect(); |  | ||||||
| 	const left = window.scrollX + domRect.left; |  | ||||||
| 	const top = window.scrollY + domRect.top; |  | ||||||
| 	child.style.position = 'absolute'; |  | ||||||
| 	child.style.left = left + 'px'; |  | ||||||
| 	child.style.top = top + 'px'; |  | ||||||
| 	child.style.zIndex = 'var(--ck-z-panel)'; |  | ||||||
| 	child.style.pointerEvents = 'none'; |  | ||||||
| } |  | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Elian Doran
						Elian Doran