From 8a4f6be5ebdb4bada5413f7f18249d58b40c120c Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Thu, 7 May 2026 14:52:15 +0100 Subject: [PATCH 1/3] fix(note-editor): MathJax Block Was: `\[\\]` Now: `\[\]` Fixes 20961 --- AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt index ec980bd9a621..aba78d093259 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt @@ -332,7 +332,7 @@ class Toolbar : FrameLayout { val mathjaxOptions = arrayOf( - MathJaxOption(TR.editingMathjaxBlock(), prefix = "\\[\\", suffix = "\\]"), + MathJaxOption(TR.editingMathjaxBlock(), prefix = "\\[", suffix = "\\]"), MathJaxOption(TR.editingMathjaxChemistry(), prefix = "\\( \\ce{", suffix = "} \\)"), ) AlertDialog.Builder(context).show { From 7dc3a6aef9393d2c971a883a2f9c255b51c3863e Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Thu, 7 May 2026 15:10:35 +0100 Subject: [PATCH 2/3] docs(note-editor): improve TextWrapper docs --- AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt index aba78d093259..2dfd9770a914 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt @@ -429,8 +429,8 @@ class Toolbar : FrameLayout { /** * A [TextFormatter] which wraps the selected string with [prefix] and [suffix] - * If there's no selected, the cursor is in the middle of the prefix and suffix - * If there is text selected, the whole string is selected + * If there's no selection, the cursor is placed in the middle of the prefix and suffix + * If text is selected and wrapped, the whole string is selected */ class TextWrapper( private val prefix: String, From 2b6437cd25301e1ad2e74bd76b2d456c65f3ff78 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Thu, 7 May 2026 15:13:37 +0100 Subject: [PATCH 3/3] refactor(note-editor): make MathJax testable Issue 20961 Assisted-by: Claude Opus 4.7 - most of the code --- .../ichi2/anki/noteeditor/MathJaxFormat.kt | 28 +++++++++++++++++ .../java/com/ichi2/anki/noteeditor/Toolbar.kt | 20 +++--------- .../anki/noteeditor/MathJaxFormatTest.kt | 31 +++++++++++++++++++ 3 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/MathJaxFormat.kt create mode 100644 AnkiDroid/src/test/java/com/ichi2/anki/noteeditor/MathJaxFormatTest.kt diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/MathJaxFormat.kt b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/MathJaxFormat.kt new file mode 100644 index 000000000000..2f543223c9fe --- /dev/null +++ b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/MathJaxFormat.kt @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki.noteeditor + +import com.ichi2.anki.CollectionManager.TR + +/** Options for inserting MathJax equation types via the toolbar */ +enum class MathJaxFormat( + val prefix: String, + val suffix: String, +) { + /** Display-math block: `\[ E=mc^2 \]` */ + BLOCK(prefix = "\\[", suffix = "\\]"), + + /** + * [mhchem](https://mhchem.github.io/MathJax-mhchem/) chemistry equation: `\( \ce{ H2O } \)` + */ + CHEMISTRY(prefix = "\\( \\ce{", suffix = "} \\)"), + ; + + fun toTextWrapper() = Toolbar.TextWrapper(prefix = prefix, suffix = suffix) + + fun label(): String = + when (this) { + BLOCK -> TR.editingMathjaxBlock() + CHEMISTRY -> TR.editingMathjaxChemistry() + } +} diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt index 2dfd9770a914..379afa213d9e 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt @@ -48,7 +48,6 @@ import androidx.core.view.children import androidx.core.view.isVisible import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import com.ichi2.anki.AnkiDroidApp -import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.NoteEditorFragment import com.ichi2.anki.R import com.ichi2.anki.compat.CompatHelper @@ -322,22 +321,11 @@ class Toolbar : FrameLayout { * Displays a dialog that allows the user to insert a MathJax equation in different formats. */ private fun displayInsertMathJaxEquationsDialog() { - data class MathJaxOption( - val label: String, - val prefix: String, - val suffix: String, - ) { - fun toTextWrapper() = TextWrapper(prefix = this.prefix, suffix = this.suffix) - } - - val mathjaxOptions = - arrayOf( - MathJaxOption(TR.editingMathjaxBlock(), prefix = "\\[", suffix = "\\]"), - MathJaxOption(TR.editingMathjaxChemistry(), prefix = "\\( \\ce{", suffix = "} \\)"), - ) + val options = MathJaxFormat.entries + val labels = options.map { it.label() }.toTypedArray() AlertDialog.Builder(context).show { - setItems(mathjaxOptions.map(MathJaxOption::label).toTypedArray()) { _, index -> - onFormat(mathjaxOptions[index].toTextWrapper()) + setItems(labels) { _, index -> + onFormat(options[index].toTextWrapper()) } title(R.string.insert_mathjax) } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/noteeditor/MathJaxFormatTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/noteeditor/MathJaxFormatTest.kt new file mode 100644 index 000000000000..9bbbd982fe98 --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/anki/noteeditor/MathJaxFormatTest.kt @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki.noteeditor + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.Test + +class MathJaxFormatTest { + @Test + fun `block wraps text in display-math delimiters`() { + assertThat( + MathJaxFormat.BLOCK + .toTextWrapper() + .format("E=mc^2") + .result, + equalTo("""\[E=mc^2\]"""), + ) + } + + @Test + fun `chemistry wraps text in mhchem ce delimiters`() { + assertThat( + MathJaxFormat.CHEMISTRY + .toTextWrapper() + .format("H2O") + .result, + equalTo("""\( \ce{H2O} \)"""), + ) + } +}