Skip to content

Commit 32d2096

Browse files
committed
katex [nfc]: Clarify structure of "vlist" HTML and our reasoning about it
Mostly this moves and revises comments. Also rename one local to "depthStrut" to match its name in the KaTeX JS, which we also now use in the explanatory comment.
1 parent 7763deb commit 32d2096

File tree

1 file changed

+34
-28
lines changed

1 file changed

+34
-28
lines changed

lib/model/katex.dart

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -258,23 +258,45 @@ class _KatexParser {
258258
if (vlistT.nodes.isEmpty) throw _KatexHtmlParseError();
259259
if (vlistT.attributes.containsKey('style')) throw _KatexHtmlParseError();
260260

261+
// A .vlist-t node is a CSS table; .vlist-r are rows, and
262+
// .vlist and .vlist-s are cells. See the CSS rules:
263+
// https://github.com/KaTeX/KaTeX/blob/9fb63136e/src/styles/katex.scss#L183-L231
264+
//
265+
// The structure of a .vlist-t node is very specific.
266+
// See the code that generates it:
267+
// https://github.com/KaTeX/KaTeX/blob/9fb63136e/src/buildCommon.js#L589-L620
268+
// As seen in that code, there are either two rows totalling three cells
269+
// (namely [[vlist, topStrut], [depthStrut]]), or one row with one cell
270+
// ([[vlist]]), where "vlist" is a .vlist node which contains all the
271+
// real content of the .vlist-t.
272+
//
273+
// The extra cells "topStrut" and "depthStrut" are workarounds for
274+
// Safari circa 2017, as seen in comments in the linked KaTeX code.
275+
// When present, the .vlist-t node has another class `vlist-t2`,
276+
// whose only effect is to cancel out part of the effect of "topStrut"
277+
// (which is a .vlist-s); see the commit that added `vlist-t2`:
278+
// https://github.com/KaTeX/KaTeX/commit/766487bfe
279+
// So we ignore that part of the CSS. We ignore the rest of those
280+
// two "strut" cells too, because they don't seem to have any effect
281+
// in rendering on the web (in e.g. a modern Chrome, in 2025).
282+
261283
final hasVlistT2 = vlistT.className == 'vlist-t vlist-t2';
262284
if (!hasVlistT2 && vlistT.nodes.length != 1) throw _KatexHtmlParseError();
263285
if (hasVlistT2) {
264286
if (vlistT.nodes case [
265-
_,
287+
_, // this row should have two cells [vlist, topStrut]; see details above
266288
dom.Element(localName: 'span', className: 'vlist-r', nodes: [
267289
dom.Element(localName: 'span', className: 'vlist', nodes: [
268290
dom.Element(localName: 'span', className: '', nodes: []),
269-
]) && final vlist,
291+
]) && final depthStrut,
270292
]),
271293
]) {
272-
// In the generated HTML the .vlist in second .vlist-r span will have
273-
// a "height" inline style which we ignore, because it doesn't seem
274-
// to have any effect in rendering on the web.
275-
// But also make sure there aren't any other inline styles present.
276-
final vlistStyles = _parseInlineStyles(vlist);
277-
if (vlistStyles != null && vlistStyles.keys.any((p) => p != 'height')) {
294+
// This "depthStrut" cell will have a "height" inline style,
295+
// which we ignore because it doesn't seem to have any effect in web;
296+
// see detailed comment above.
297+
// Make sure there aren't any other, unexpected, inline styles present.
298+
final styles = _parseInlineStyles(depthStrut);
299+
if (styles != null && styles.keys.any((p) => p != 'height')) {
278300
throw _KatexHtmlParseError();
279301
}
280302
} else {
@@ -290,11 +312,10 @@ class _KatexParser {
290312
if (vlistR.nodes.first
291313
case dom.Element(localName: 'span', className: 'vlist') &&
292314
final vlist) {
293-
// Same as above for the second .vlist-r span, .vlist span in first
294-
// .vlist-r span will have "height" inline style which we ignore,
295-
// because it doesn't seem to have any effect in rendering on
296-
// the web.
297-
// But also make sure there aren't any other inline styles present.
315+
// Same as above for the "depthStrut" node, the main "vlist" cell
316+
// will have a "height" inline style which we ignore
317+
// because it doesn't seem to have any effect in rendering on the web.
318+
// Make sure there aren't any other, unexpected, inline styles present.
298319
final vlistStyles = _parseInlineStyles(vlist);
299320
if (vlistStyles != null && vlistStyles.keys.any((p) => p != 'height')) {
300321
throw _KatexHtmlParseError();
@@ -355,21 +376,6 @@ class _KatexParser {
355376
}
356377
}
357378

358-
// The katex.css has .vlist-t2 and .vlist-s classes for working around
359-
// Safari rendering issues. In HTML the .vlist-t2 class will be present
360-
// along with the .vlist-t class, and .vlist-s class will be present on
361-
// an empty span in the first (of the two) .vlist-r span.
362-
//
363-
// The KaTeX implementation confirms that both classes are always
364-
// present in tandem. And by experimenting via browser devtools and
365-
// the CSS definition and, it can be confirmed that both cancel each
366-
// others effect of the 2px shift.
367-
// See:
368-
// https://github.com/KaTeX/KaTeX/blob/9fb63136e/src/buildCommon.js#L596-L620
369-
// https://github.com/KaTeX/KaTeX/commit/766487bfe
370-
//
371-
// So, we ignore these classes, as those workarounds aren't needed.
372-
373379
return KatexVlistNode(
374380
rows: rows,
375381
debugHtmlNode: kDebugMode ? element : null,

0 commit comments

Comments
 (0)