From f2f79fc73e6c5b28f5dadb42ed47f76e1fc134c4 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 26 Dec 2024 18:59:17 +0200 Subject: [PATCH] feat(mdx-editor): math plugin based on katex --- front_end/messages/cs.json | 3 +- front_end/messages/en.json | 3 +- front_end/messages/es.json | 3 +- front_end/messages/pt.json | 3 +- front_end/messages/zh.json | 3 +- front_end/package-lock.json | 72 +++++++ front_end/package.json | 3 + .../questions/components/notebook_form.tsx | 10 +- front_end/src/app/globals.css | 9 + .../embedded_math_jax/index.tsx | 35 +--- .../markdown_editor/initialized_editor.tsx | 10 +- .../plugins/equation/EquationNode.tsx | 180 ++++++++++++++++++ .../equation/LexicalEquationVisitor.ts | 12 ++ .../plugins/equation/MdastEquationVisitor.ts | 18 ++ .../equation/components/AddEquationAction.tsx | 29 +++ .../equation/components/AddEquationModal.tsx | 118 ++++++++++++ .../equation/components/EquationComponent.tsx | 146 ++++++++++++++ .../equation/components/EquationEditor.tsx | 62 ++++++ .../equation/components/KatexRenderer.tsx | 47 +++++ .../plugins/equation/index.tsx | 51 +++++ 20 files changed, 774 insertions(+), 43 deletions(-) create mode 100644 front_end/src/components/markdown_editor/plugins/equation/EquationNode.tsx create mode 100644 front_end/src/components/markdown_editor/plugins/equation/LexicalEquationVisitor.ts create mode 100644 front_end/src/components/markdown_editor/plugins/equation/MdastEquationVisitor.ts create mode 100644 front_end/src/components/markdown_editor/plugins/equation/components/AddEquationAction.tsx create mode 100644 front_end/src/components/markdown_editor/plugins/equation/components/AddEquationModal.tsx create mode 100644 front_end/src/components/markdown_editor/plugins/equation/components/EquationComponent.tsx create mode 100644 front_end/src/components/markdown_editor/plugins/equation/components/EquationEditor.tsx create mode 100644 front_end/src/components/markdown_editor/plugins/equation/components/KatexRenderer.tsx create mode 100644 front_end/src/components/markdown_editor/plugins/equation/index.tsx diff --git a/front_end/messages/cs.json b/front_end/messages/cs.json index b0e42cf16..86065cd95 100644 --- a/front_end/messages/cs.json +++ b/front_end/messages/cs.json @@ -814,5 +814,6 @@ "onboardingStep5SavePredictionsParagraph": "Chcete uložit provedené předpovědi?", "onboardingYouPredicted": "Předpověděli jste", "next": "Další", - "postCreateErrorMinSubquestions": "Skupina otázek musí obsahovat alespoň jednu podotázku" + "postCreateErrorMinSubquestions": "Skupina otázek musí obsahovat alespoň jednu podotázku", + "insertEquation": "Vložit rovnici" } diff --git a/front_end/messages/en.json b/front_end/messages/en.json index dec5f29ae..4059ba539 100644 --- a/front_end/messages/en.json +++ b/front_end/messages/en.json @@ -1026,5 +1026,6 @@ "clearCommentsAndCommunicationInfo": "Our Pros work on projects for external partners who value clear reasoning to better interpret the forecasts. We select Pros who have a history of making clear and insightful comments, and who are willing to disagree with their peers, but in a polite and respectful manner.", "expressionOfInterestFormMessage": "We sometimes recruit upstanding members of the community who are great at providing constructive feedback on submitted questions to become paid moderators. Fill out our expression of interest form if you would like to be considered.", "dateInputDetails": "Date and time is in your local timezone [{timezone}]", - "postCreateErrorMinSubquestions": "A question group must have at least one subquestion" + "postCreateErrorMinSubquestions": "A question group must have at least one subquestion", + "insertEquation": "Insert Equation" } diff --git a/front_end/messages/es.json b/front_end/messages/es.json index fe1a95c5d..c38f91403 100644 --- a/front_end/messages/es.json +++ b/front_end/messages/es.json @@ -827,5 +827,6 @@ "onboardingStep5SavePredictionsParagraph": "¿Te gustaría guardar las predicciones que has realizado?", "onboardingYouPredicted": "Has predicho", "next": "Siguiente", - "postCreateErrorMinSubquestions": "Un grupo de preguntas debe tener al menos una subpregunta" + "postCreateErrorMinSubquestions": "Un grupo de preguntas debe tener al menos una subpregunta", + "insertEquation": "Insertar ecuación" } diff --git a/front_end/messages/pt.json b/front_end/messages/pt.json index 695c057ce..46b3df840 100644 --- a/front_end/messages/pt.json +++ b/front_end/messages/pt.json @@ -943,5 +943,6 @@ "onboardingStep5SavePredictionsParagraph": "Gostaria de salvar as previsões que você fez?", "onboardingYouPredicted": "Você previu", "next": "Próximo", - "postCreateErrorMinSubquestions": "Um grupo de perguntas deve ter pelo menos uma subpergunta" + "postCreateErrorMinSubquestions": "Um grupo de perguntas deve ter pelo menos uma subpergunta", + "insertEquation": "Inserir equação" } diff --git a/front_end/messages/zh.json b/front_end/messages/zh.json index f7ab24e85..4cf53400f 100644 --- a/front_end/messages/zh.json +++ b/front_end/messages/zh.json @@ -805,5 +805,6 @@ "onboardingStep5SavePredictionsParagraph": "您想保存所做的预测吗?", "onboardingYouPredicted": "您预测了", "next": "下一步", - "postCreateErrorMinSubquestions": "一个问题组必须至少包含一个子问题" + "postCreateErrorMinSubquestions": "一个问题组必须至少包含一个子问题", + "insertEquation": "插入公式" } diff --git a/front_end/package-lock.json b/front_end/package-lock.json index 9b6e416c4..c44d04d6c 100644 --- a/front_end/package-lock.json +++ b/front_end/package-lock.json @@ -39,9 +39,12 @@ "date-fns-tz": "^3.1.3", "file-saver": "^2.0.5", "html-react-parser": "^5.1.18", + "katex": "^0.16.18", "lexical-beautiful-mentions": "^0.1.44", "lodash": "^4.17.21", "mathjs": "^12.4.2", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.1.0", "negotiator": "^0.6.3", "next": "^14.2.15", "next-intl": "^3.14.1", @@ -4930,6 +4933,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", + "license": "MIT" + }, "node_modules/@types/lodash": { "version": "4.17.7", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", @@ -9970,6 +9979,31 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/katex": { + "version": "0.16.18", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.18.tgz", + "integrity": "sha512-LRuk0rPdXrecAFwQucYjMiIs0JFefk6N1q/04mlw14aVIVgxq1FO0MA9RiIIGVaKOB5GIP5GH4aBBNraZERmaQ==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -10599,6 +10633,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-mdx": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", @@ -10893,6 +10946,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/micromark-extension-mdx-expression": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz", diff --git a/front_end/package.json b/front_end/package.json index 2d1d9af5c..afb35ee26 100644 --- a/front_end/package.json +++ b/front_end/package.json @@ -44,9 +44,12 @@ "date-fns-tz": "^3.1.3", "file-saver": "^2.0.5", "html-react-parser": "^5.1.18", + "katex": "^0.16.18", "lexical-beautiful-mentions": "^0.1.44", "lodash": "^4.17.21", "mathjs": "^12.4.2", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.1.0", "negotiator": "^0.6.3", "next": "^14.2.15", "next-intl": "^3.14.1", diff --git a/front_end/src/app/(main)/questions/components/notebook_form.tsx b/front_end/src/app/(main)/questions/components/notebook_form.tsx index 02a5ff3b5..c97edab0b 100644 --- a/front_end/src/app/(main)/questions/components/notebook_form.tsx +++ b/front_end/src/app/(main)/questions/components/notebook_form.tsx @@ -32,6 +32,12 @@ import BacktoCreate from "./back_to_create"; import CategoryPicker from "./category_picker"; import { createQuestionPost, updatePost } from "../actions"; +const TEST_MDX = `Lift($L$) can be determined by Lift Coefficient ($C_L$) like the following equation. + +$$ +L = \\frac{1}{2} \\rho v^2 S C_L +$$`; + const createNotebookSchema = (t: ReturnType) => { return z.object({ title: z @@ -91,6 +97,8 @@ const NotebookForm: React.FC = ({ resolver: zodResolver(notebookSchema), }); + console.log("markdown", form.watch("markdown")); + // TODO: consider refactoring this field to be part of zod schema const [categoriesList, setCategoriesList] = useState( post?.projects.category ? post?.projects.category : ([] as Category[]) @@ -210,7 +218,7 @@ const NotebookForm: React.FC = ({ diff --git a/front_end/src/app/globals.css b/front_end/src/app/globals.css index d74605aa5..f66144498 100644 --- a/front_end/src/app/globals.css +++ b/front_end/src/app/globals.css @@ -203,3 +203,12 @@ a { padding: 0; border: 0; } + +/*.editor-equation {*/ +/* cursor: default;*/ +/* user-select: none;*/ +/*}*/ + +/*.editor-equation.focused {*/ +/* outline: 2px solid rgb(60, 132, 244);*/ +/*}*/ diff --git a/front_end/src/components/markdown_editor/embedded_math_jax/index.tsx b/front_end/src/components/markdown_editor/embedded_math_jax/index.tsx index aca7e86c2..c31e378e1 100644 --- a/front_end/src/components/markdown_editor/embedded_math_jax/index.tsx +++ b/front_end/src/components/markdown_editor/embedded_math_jax/index.tsx @@ -1,17 +1,12 @@ -import { - faSquareRootVariable, - faXmark, -} from "@fortawesome/free-solid-svg-icons"; +import { faXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { - insertJsx$, JsxComponentDescriptor, readOnly$, useCellValue, useLexicalNodeRemove, - usePublisher, } from "@mdxeditor/editor"; -import React, { FC } from "react"; +import React from "react"; import createEditorComponent from "@/components/markdown_editor/createJsxComponent"; import MathJaxContent from "@/components/math_jax_content"; @@ -54,31 +49,7 @@ const EmbeddedMathJax: React.FC = ({ content }) => { ); }; -export const EmbedMathJaxAction: FC = () => { - const insertJsx = usePublisher(insertJsx$); - - return ( - - ); -}; - +// support legacy math rendering via embedded JSX export const mathJaxDescriptor: JsxComponentDescriptor = { name: COMPONENT_NAME, props: [{ name: "content", type: "string", required: true }], diff --git a/front_end/src/components/markdown_editor/initialized_editor.tsx b/front_end/src/components/markdown_editor/initialized_editor.tsx index 12e87e46e..29869c196 100644 --- a/front_end/src/components/markdown_editor/initialized_editor.tsx +++ b/front_end/src/components/markdown_editor/initialized_editor.tsx @@ -40,16 +40,15 @@ import React, { import { mergeRefs } from "react-merge-refs"; import { uploadImage } from "@/app/(main)/questions/actions"; -import { - EmbedMathJaxAction, - mathJaxDescriptor, -} from "@/components/markdown_editor/embedded_math_jax"; +import { mathJaxDescriptor } from "@/components/markdown_editor/embedded_math_jax"; import { embeddedQuestionDescriptor, EmbedQuestionAction, } from "@/components/markdown_editor/embedded_question"; import { tweetDescriptor } from "@/components/markdown_editor/embedded_twitter"; import { processMarkdown } from "@/components/markdown_editor/helpers"; +import { equationPlugin } from "@/components/markdown_editor/plugins/equation"; +import AddEquationAction from "@/components/markdown_editor/plugins/equation/components/AddEquationAction"; import { linkPlugin } from "@/components/markdown_editor/plugins/link"; import { mentionsPlugin } from "@/components/markdown_editor/plugins/mentions"; import { useAuth } from "@/contexts/auth_context"; @@ -180,6 +179,7 @@ const InitializedMarkdownEditor: FC< disableImageResize: true, imageUploadHandler, }), + equationPlugin(), ]; const editorDiffSourcePlugin = useMemo(() => { @@ -205,7 +205,7 @@ const InitializedMarkdownEditor: FC< - + diff --git a/front_end/src/components/markdown_editor/plugins/equation/EquationNode.tsx b/front_end/src/components/markdown_editor/plugins/equation/EquationNode.tsx new file mode 100644 index 000000000..1feae0217 --- /dev/null +++ b/front_end/src/components/markdown_editor/plugins/equation/EquationNode.tsx @@ -0,0 +1,180 @@ +import { voidEmitter } from "@mdxeditor/editor"; +import katex from "katex"; +import type { + DOMConversionMap, + DOMConversionOutput, + EditorConfig, + LexicalEditor, + LexicalNode, + NodeKey, + SerializedLexicalNode, + Spread, +} from "lexical"; +import { $applyNodeReplacement, DecoratorNode, DOMExportOutput } from "lexical"; +import { InlineMath, Math } from "mdast-util-math"; +import * as React from "react"; + +import EquationComponent from "./components/EquationComponent"; + +export type SerializedEquationNode = Spread< + { + equation: string; + inline: boolean; + }, + SerializedLexicalNode +>; + +function $convertEquationElement( + domNode: HTMLElement +): null | DOMConversionOutput { + let equation = domNode.getAttribute("data-lexical-equation"); + const inline = domNode.getAttribute("data-lexical-inline") === "true"; + // Decode the equation from base64 + equation = atob(equation || ""); + if (equation) { + const node = $createEquationNode(equation, inline); + return { node }; + } + + return null; +} + +export class EquationNode extends DecoratorNode { + __equation: string; + __inline: boolean; + + __focusEmitter = voidEmitter(); + + static getType(): string { + return "equation"; + } + + static clone(node: EquationNode): EquationNode { + return new EquationNode(node.__equation, node.__inline, node.__key); + } + + constructor(equation: string, inline?: boolean, key?: NodeKey) { + super(key); + this.__equation = equation; + this.__inline = inline ?? false; + } + + static importJSON(serializedNode: SerializedEquationNode): EquationNode { + const node = $createEquationNode( + serializedNode.equation, + serializedNode.inline + ); + return node; + } + + exportJSON(): SerializedEquationNode { + return { + ...super.exportJSON(), + equation: this.getEquation(), + inline: this.__inline, + }; + } + + getMdastNode(): Math | InlineMath { + if (this.__inline) { + return { type: "inlineMath", value: this.__equation }; + } + + return { type: "math", value: this.__equation }; + } + + createDOM(_config: EditorConfig): HTMLElement { + const element = document.createElement(this.__inline ? "span" : "div"); + // EquationNodes should implement `user-action:none` in their CSS to avoid issues with deletion on Android. + element.className = "editor-equation"; + return element; + } + + exportDOM(): DOMExportOutput { + const element = document.createElement(this.__inline ? "span" : "div"); + // Encode the equation as base64 to avoid issues with special characters + const equation = btoa(this.__equation); + element.setAttribute("data-lexical-equation", equation); + element.setAttribute("data-lexical-inline", `${this.__inline}`); + katex.render(this.__equation, element, { + displayMode: !this.__inline, // true === block display // + errorColor: "#cc0000", + output: "html", + strict: "warn", + throwOnError: false, + trust: false, + }); + return { element }; + } + + static importDOM(): DOMConversionMap | null { + return { + div: (domNode: HTMLElement) => { + if (!domNode.hasAttribute("data-lexical-equation")) { + return null; + } + return { + conversion: $convertEquationElement, + priority: 2, + }; + }, + span: (domNode: HTMLElement) => { + if (!domNode.hasAttribute("data-lexical-equation")) { + return null; + } + return { + conversion: $convertEquationElement, + priority: 1, + }; + }, + }; + } + + updateDOM(prevNode: this): boolean { + // If the inline property changes, replace the element + return this.__inline !== prevNode.__inline; + } + + getTextContent(): string { + return this.__equation; + } + + getEquation(): string { + return this.__equation; + } + + setEquation(equation: string): void { + const writable = this.getWritable(); + writable.__equation = equation; + } + + decorate(parentEditor: LexicalEditor): JSX.Element { + return ( + + ); + } + + select(): void { + console.log("SELECT!!"); + this.__focusEmitter.publish(); + } +} + +export function $createEquationNode( + equation = "", + inline = false +): EquationNode { + const equationNode = new EquationNode(equation, inline); + return $applyNodeReplacement(equationNode); +} + +export function $isEquationNode( + node: LexicalNode | null | undefined +): node is EquationNode { + return node instanceof EquationNode; +} diff --git a/front_end/src/components/markdown_editor/plugins/equation/LexicalEquationVisitor.ts b/front_end/src/components/markdown_editor/plugins/equation/LexicalEquationVisitor.ts new file mode 100644 index 000000000..813a59bed --- /dev/null +++ b/front_end/src/components/markdown_editor/plugins/equation/LexicalEquationVisitor.ts @@ -0,0 +1,12 @@ +import { LexicalExportVisitor } from "@mdxeditor/editor"; +import { Math } from "mdast-util-math"; + +import { $isEquationNode, EquationNode } from "./EquationNode"; + +export const LexicalEquationVisitor: LexicalExportVisitor = + { + testLexicalNode: $isEquationNode, + visitLexicalNode({ actions, mdastParent, lexicalNode }) { + actions.appendToParent(mdastParent, lexicalNode.getMdastNode()); + }, + }; diff --git a/front_end/src/components/markdown_editor/plugins/equation/MdastEquationVisitor.ts b/front_end/src/components/markdown_editor/plugins/equation/MdastEquationVisitor.ts new file mode 100644 index 000000000..13cd7f857 --- /dev/null +++ b/front_end/src/components/markdown_editor/plugins/equation/MdastEquationVisitor.ts @@ -0,0 +1,18 @@ +import { MdastImportVisitor } from "@mdxeditor/editor"; +import { InlineMath, Math } from "mdast-util-math"; + +import { $createEquationNode } from "./EquationNode"; + +export const MdastEquationVisitor: MdastImportVisitor = { + testNode: "math", + visitNode({ mdastNode, actions }) { + actions.addAndStepInto($createEquationNode(mdastNode.value, false)); + }, +}; + +export const MdastInlineEquationVisitor: MdastImportVisitor = { + testNode: "inlineMath", + visitNode({ mdastNode, actions }) { + actions.addAndStepInto($createEquationNode(mdastNode.value, true)); + }, +}; diff --git a/front_end/src/components/markdown_editor/plugins/equation/components/AddEquationAction.tsx b/front_end/src/components/markdown_editor/plugins/equation/components/AddEquationAction.tsx new file mode 100644 index 000000000..8317e56ef --- /dev/null +++ b/front_end/src/components/markdown_editor/plugins/equation/components/AddEquationAction.tsx @@ -0,0 +1,29 @@ +import { faSquareRootVariable } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React, { FC, useState } from "react"; + +import Button from "@/components/ui/button"; + +import AddEquationModal from "./AddEquationModal"; + +const AddEquationAction: FC = () => { + const [isModalOpen, setIsModalOpen] = useState(false); + + return ( + <> + + setIsModalOpen(false)} + /> + + ); +}; + +export default AddEquationAction; diff --git a/front_end/src/components/markdown_editor/plugins/equation/components/AddEquationModal.tsx b/front_end/src/components/markdown_editor/plugins/equation/components/AddEquationModal.tsx new file mode 100644 index 000000000..909c42272 --- /dev/null +++ b/front_end/src/components/markdown_editor/plugins/equation/components/AddEquationModal.tsx @@ -0,0 +1,118 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { activeEditor$, useCellValue, usePublisher } from "@mdxeditor/editor"; +import { useTranslations } from "next-intl"; +import { FC } from "react"; +import { ErrorBoundary } from "react-error-boundary"; +import { Controller, useForm } from "react-hook-form"; +import { z } from "zod"; + +import BaseModal from "@/components/base_modal"; +import Button from "@/components/ui/button"; +import Checkbox from "@/components/ui/checkbox"; +import { Input, Textarea } from "@/components/ui/form_field"; +import { InputContainer } from "@/components/ui/input_container"; + +import KatexRenderer from "./KatexRenderer"; +import { insertEquation$ } from "../index"; + +const createSchema = (t: ReturnType) => + z.object({ + inline: z.boolean(), + equation: z.string().min(1, { + message: t("errorMinLength", { field: "String", minLength: 1 }), + }), + }); +type FormValues = z.infer>; + +type Props = { + isOpen: boolean; + onClose: () => void; +}; + +const AddEquationModal: FC = ({ isOpen, onClose }) => { + const t = useTranslations(); + + const editor = useCellValue(activeEditor$); + const insertEquation = usePublisher(insertEquation$); + + const { register, control, watch, formState, reset, handleSubmit } = + useForm({ + mode: "all", + resolver: zodResolver(createSchema(t)), + defaultValues: { + inline: true, + equation: "", + }, + }); + + const equation = watch("equation"); + const inline = watch("inline"); + + const closeModal = () => { + reset(); + onClose(); + }; + + const handleValidSubmit = (data: FormValues) => { + insertEquation(data); + closeModal(); + }; + + return ( + +
+ ( + + )} + /> + + {inline ? ( + + ) : ( +