diff --git a/frontend/src/app.css b/frontend/src/app.css
index 8221411..f88fbce 100644
--- a/frontend/src/app.css
+++ b/frontend/src/app.css
@@ -1542,6 +1542,31 @@ tbody tr:hover {
line-height: 1.5;
}
+/*
+ * The MathML is taken out of text selection and the source is offered in its
+ * place.
+ *
+ * A formula is one element per token, and a browser copying a selection walks
+ * those elements one at a time: copying `a_1 = (x_ke / n_0)^{2/3}` from the
+ * page gave one character per line, 132 lines for 132 characters. What a
+ * reader wants on the clipboard is the formula, so the source is what can be
+ * selected and the markup cannot.
+ */
+.math math {
+ user-select: none;
+}
+
+/* Kept out of the layout but not out of a selection, so that a copy finds it
+ where the formula is. */
+.math-source {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
+ clip-path: inset(50%);
+ white-space: nowrap;
+}
+
.math-display {
display: block;
margin: 1.1rem 0;
diff --git a/src/docs/mathml.rs b/src/docs/mathml.rs
index 597a92b..2de2bce 100644
--- a/src/docs/mathml.rs
+++ b/src/docs/mathml.rs
@@ -23,14 +23,21 @@ use crate::docs::markdown::{escape, escape_attr};
/// `display` is set when the formula stands on its own line, which makes it
/// larger and centred.
pub fn render(latex: &str, display: bool) -> String {
+ // The delimiters of a display formula are on their own lines, so the
+ // source arrives with the newline that followed the opening pair.
+ let latex = latex.trim();
let body = to_mathml(&normalize(latex));
let class = if display { "math math-display" } else { "math" };
// The original source is kept in a data attribute so that the formula
// can be recovered from the page, for copying and for the tests.
+ // The MathML is one element per token, and a browser copying a selection
+ // walks them one at a time: a formula copied from the page arrives as one
+ // character per line. The source is therefore carried beside it in an
+ // element a reader can select, and the MathML is taken out of selection
+ // so that what a copy yields is the formula as it was written.
format!(
- "",
- escape_attr(latex),
+ "{source}",
+ source = escape_attr(latex),
display = if display { "block" } else { "inline" }
)
}