Skip to content

Commit 5164619

Browse files
content: Add initial support for displaying some KaTeX content
This initial implementation include: - Displaying the characters in their corresponding custom text styles (fonts, font weight, font style). - Character and symbol sizing. - And some subset of inline styles. This results in support for displaying some simple KaTeX functions.
1 parent 01b45c5 commit 5164619

File tree

4 files changed

+953
-27
lines changed

4 files changed

+953
-27
lines changed

lib/model/content.dart

+81-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import 'package:html/parser.dart';
55

66
import '../api/model/model.dart';
77
import '../api/model/submessage.dart';
8+
import '../log.dart';
89
import 'code_block.dart';
10+
import 'katex.dart';
911

1012
/// A node in a parse tree for Zulip message-style content.
1113
///
@@ -340,24 +342,83 @@ class CodeBlockSpanNode extends ContentNode {
340342
}
341343
}
342344

343-
class MathBlockNode extends BlockContentNode {
344-
const MathBlockNode({super.debugHtmlNode, required this.texSource});
345+
sealed class BaseKatexSpan extends ContentNode {
346+
const BaseKatexSpan({super.debugHtmlNode});
347+
}
345348

346-
final String texSource;
349+
class KatexNegativeRightMarginSpans extends BaseKatexSpan {
350+
const KatexNegativeRightMarginSpans({
351+
required this.marginRightEm,
352+
this.spans = const [],
353+
super.debugHtmlNode,
354+
});
355+
356+
final double marginRightEm;
357+
final List<KatexSpan> spans;
347358

348359
@override
349-
bool operator ==(Object other) {
350-
return other is MathBlockNode && other.texSource == texSource;
360+
List<DiagnosticsNode> debugDescribeChildren() {
361+
return spans.map((node) => node.toDiagnosticsNode()).toList();
351362
}
363+
}
364+
365+
class KatexSpan extends BaseKatexSpan {
366+
const KatexSpan({
367+
required this.text,
368+
required this.styles,
369+
370+
required this.classes,
371+
required this.ancestorClasses,
372+
373+
required this.spans,
374+
375+
super.debugHtmlNode,
376+
});
377+
378+
final String? text;
379+
final KatexSpanStyles styles;
380+
381+
final List<String> classes;
382+
final List<List<String>> ancestorClasses;
383+
384+
final List<BaseKatexSpan> spans;
352385

353386
@override
354-
int get hashCode => Object.hash('MathBlockNode', texSource);
387+
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
388+
super.debugFillProperties(properties);
389+
properties.add(StringProperty('text', text));
390+
properties.add(KatexSpanStylesProperty('styles', styles));
391+
properties.add(StringProperty('classes', classes.join(', ')));
392+
properties.add(StringProperty('ancestorClasses', ancestorClasses.map(
393+
(e) => '[${e.join(', ')}]').join(', ')));
394+
}
395+
396+
@override
397+
List<DiagnosticsNode> debugDescribeChildren() {
398+
return spans.map((node) => node.toDiagnosticsNode()).toList();
399+
}
400+
}
401+
402+
class MathBlockNode extends BlockContentNode {
403+
const MathBlockNode({
404+
super.debugHtmlNode,
405+
required this.texSource,
406+
required this.spans,
407+
});
408+
409+
final String texSource;
410+
final List<BaseKatexSpan>? spans;
355411

356412
@override
357413
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
358414
super.debugFillProperties(properties);
359415
properties.add(StringProperty('texSource', texSource));
360416
}
417+
418+
@override
419+
List<DiagnosticsNode> debugDescribeChildren() {
420+
return spans?.map((node) => node.toDiagnosticsNode()).toList() ?? const [];
421+
}
361422
}
362423

363424
class ImageNodeList extends BlockContentNode {
@@ -1626,8 +1687,15 @@ class _ZulipContentParser {
16261687
final firstChild = nodes.first as dom.Element;
16271688
final texSource = _parseMath(firstChild, block: true);
16281689
if (texSource != null) {
1690+
List<BaseKatexSpan>? katexSpans;
1691+
try {
1692+
katexSpans = KatexParser().parseKatexBlock(firstChild);
1693+
} on KatexHtmlParseError catch (e, st) {
1694+
assert(debugLog('$e\n$st'));
1695+
}
16291696
result.add(MathBlockNode(
16301697
texSource: texSource,
1698+
spans: katexSpans,
16311699
debugHtmlNode: kDebugMode ? firstChild : null));
16321700
} else {
16331701
result.add(UnimplementedBlockContentNode(htmlNode: firstChild));
@@ -1661,8 +1729,15 @@ class _ZulipContentParser {
16611729
if (child case dom.Element(localName: 'span', className: 'katex-display')) {
16621730
final texSource = _parseMath(child, block: true);
16631731
if (texSource != null) {
1732+
List<BaseKatexSpan>? katexSpans;
1733+
try {
1734+
katexSpans = KatexParser().parseKatexBlock(child);
1735+
} on KatexHtmlParseError catch (e, st) {
1736+
assert(debugLog('$e\n$st'));
1737+
}
16641738
result.add(MathBlockNode(
16651739
texSource: texSource,
1740+
spans: katexSpans,
16661741
debugHtmlNode: debugHtmlNode));
16671742
continue;
16681743
}

0 commit comments

Comments
 (0)