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!( - "{body}", - escape_attr(latex), + "{body}{source}", + source = escape_attr(latex), display = if display { "block" } else { "inline" } ) }