- Replaced flutter_markdown_plus with flutter_math_fork to improve mathematical rendering within markdown content. - Integrated markdown_widget for better markdown processing and rendering, enhancing overall user experience. - Updated the markdown configuration to utilize the new packages, ensuring maintainability and adaptability of markdown features. - Refactored the streaming markdown widget to accommodate the new markdown processing logic, improving code clarity and performance.
102 lines
3.0 KiB
Dart
102 lines
3.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_math_fork/flutter_math.dart';
|
|
import 'package:markdown/markdown.dart' as m;
|
|
import 'package:markdown_widget/markdown_widget.dart';
|
|
|
|
import '../../theme/theme_extensions.dart';
|
|
|
|
const String _latexTag = 'latex';
|
|
|
|
/// Provides LaTeX parsing support for markdown_widget.
|
|
class ConduitLatex {
|
|
const ConduitLatex();
|
|
|
|
/// Returns the inline syntax used to identify LaTeX segments.
|
|
m.InlineSyntax syntax() => _LatexSyntax();
|
|
|
|
/// Returns the span generator that renders LaTeX expressions.
|
|
SpanNodeGeneratorWithTag generator({required bool isDark}) {
|
|
return SpanNodeGeneratorWithTag(
|
|
tag: _latexTag,
|
|
generator: (element, config, visitor) {
|
|
return _LatexNode(
|
|
attributes: element.attributes,
|
|
rawText: element.textContent,
|
|
config: config,
|
|
isDark: isDark,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class _LatexSyntax extends m.InlineSyntax {
|
|
_LatexSyntax() : super(r'(\$\$[\s\S]+?\$\$)|(\$[^\n]+?\$)');
|
|
|
|
@override
|
|
bool onMatch(m.InlineParser parser, Match match) {
|
|
final raw = match.input.substring(match.start, match.end);
|
|
final element = m.Element.text(_latexTag, raw);
|
|
if (raw.startsWith(r'$$') && raw.endsWith(r'$$') && raw.length > 4) {
|
|
element.attributes['content'] = raw.substring(2, raw.length - 2);
|
|
element.attributes['isInline'] = 'false';
|
|
} else if (raw.startsWith(r'$') && raw.endsWith(r'$') && raw.length > 2) {
|
|
element.attributes['content'] = raw.substring(1, raw.length - 1);
|
|
element.attributes['isInline'] = 'true';
|
|
} else {
|
|
element.attributes['content'] = raw;
|
|
element.attributes['isInline'] = 'true';
|
|
}
|
|
parser.addNode(element);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class _LatexNode extends SpanNode {
|
|
_LatexNode({
|
|
required this.attributes,
|
|
required this.rawText,
|
|
required this.config,
|
|
required this.isDark,
|
|
});
|
|
|
|
final Map<String, String> attributes;
|
|
final String rawText;
|
|
final MarkdownConfig config;
|
|
final bool isDark;
|
|
|
|
@override
|
|
InlineSpan build() {
|
|
final content = attributes['content']?.trim();
|
|
final isInline = attributes['isInline'] == 'true';
|
|
final baseStyle = (parentStyle ?? config.p.textStyle).copyWith(
|
|
color:
|
|
(parentStyle ?? config.p.textStyle).color ??
|
|
(isDark ? Colors.white : Colors.black),
|
|
);
|
|
|
|
if (content == null || content.isEmpty) {
|
|
return TextSpan(text: rawText, style: baseStyle);
|
|
}
|
|
|
|
final latexWidget = Math.tex(
|
|
content,
|
|
mathStyle: MathStyle.text,
|
|
textStyle: baseStyle,
|
|
textScaleFactor: 1,
|
|
onErrorFallback: (error) {
|
|
return Text(rawText, style: baseStyle.copyWith(color: Colors.red));
|
|
},
|
|
);
|
|
|
|
final widget = isInline
|
|
? latexWidget
|
|
: Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: Spacing.xs),
|
|
child: Center(child: latexWidget),
|
|
);
|
|
|
|
return WidgetSpan(alignment: PlaceholderAlignment.middle, child: widget);
|
|
}
|
|
}
|