Samin yeasar Sohag
Committed by GitHub

Merge pull request #26 from DvdNss/main

+ highlightBuilder && - default padding on code block
@@ -99,6 +99,7 @@ class MarkdownHelper { @@ -99,6 +99,7 @@ class MarkdownHelper {
99 99
100 You can use Markdown to format text easily. Here are some examples: 100 You can use Markdown to format text easily. Here are some examples:
101 101
  102 +- `Highlighted Text`: `This text is highlighted`
102 - **Bold Text**: **This text is bold** 103 - **Bold Text**: **This text is bold**
103 - *Italic Text*: *This text is italicized* 104 - *Italic Text*: *This text is italicized*
104 - [Link](https://www.example.com): [This is a link](https://www.example.com) 105 - [Link](https://www.example.com): [This is a link](https://www.example.com)
@@ -266,6 +267,29 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex @@ -266,6 +267,29 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex
266 style: const TextStyle( 267 style: const TextStyle(
267 fontSize: 15, 268 fontSize: 15,
268 ), 269 ),
  270 + highlightBuilder: (context, text, style) {
  271 + return Container(
  272 + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
  273 + decoration: BoxDecoration(
  274 + color: Theme.of(context).colorScheme.secondaryContainer,
  275 + borderRadius: BorderRadius.circular(4),
  276 + border: Border.all(
  277 + color: Theme.of(context).colorScheme.secondary.withOpacity(0.5),
  278 + width: 1,
  279 + ),
  280 + ),
  281 + child: Text(
  282 + text,
  283 + style: TextStyle(
  284 + color: Theme.of(context).colorScheme.onSecondaryContainer,
  285 + fontFamily: 'monospace',
  286 + fontWeight: FontWeight.bold,
  287 + fontSize: style.fontSize != null ? style.fontSize! * 0.9 : 13.5,
  288 + height: style.height,
  289 + ),
  290 + ),
  291 + );
  292 + },
269 latexWorkaround: (tex) { 293 latexWorkaround: (tex) {
270 List<String> stack = []; 294 List<String> stack = [];
271 tex = tex.splitMapJoin( 295 tex = tex.splitMapJoin(
@@ -377,6 +401,20 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex @@ -377,6 +401,20 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex
377 ); 401 );
378 }, 402 },
379 ); 403 );
  404 + codeBuilder: (context, name, code) {
  405 + return Padding(
  406 + padding: const EdgeInsets.symmetric(horizontal: 16),
  407 + child: Text(
  408 + code.trim(),
  409 + style: TextStyle(
  410 + fontFamily: 'JetBrains Mono',
  411 + fontSize: 14,
  412 + height: 1.5,
  413 + color: Theme.of(context).colorScheme.onSurface,
  414 + ),
  415 + ),
  416 + );
  417 + };
380 if (selectable) { 418 if (selectable) {
381 child = SelectionArea( 419 child = SelectionArea(
382 child: child, 420 child: child,
@@ -12,6 +12,8 @@ class GptMarkdownConfig { @@ -12,6 +12,8 @@ class GptMarkdownConfig {
12 this.followLinkColor = false, 12 this.followLinkColor = false,
13 this.codeBuilder, 13 this.codeBuilder,
14 this.sourceTagBuilder, 14 this.sourceTagBuilder,
  15 + this.highlightBuilder,
  16 + this.linkBuilder,
15 this.maxLines, 17 this.maxLines,
16 this.overflow, 18 this.overflow,
17 }); 19 });
@@ -32,6 +34,8 @@ class GptMarkdownConfig { @@ -32,6 +34,8 @@ class GptMarkdownConfig {
32 codeBuilder; 34 codeBuilder;
33 final int? maxLines; 35 final int? maxLines;
34 final TextOverflow? overflow; 36 final TextOverflow? overflow;
  37 + final Widget Function(BuildContext context, String text, TextStyle style)? highlightBuilder;
  38 + final Widget Function(BuildContext context, String text, String url, TextStyle style)? linkBuilder;
35 39
36 GptMarkdownConfig copyWith({ 40 GptMarkdownConfig copyWith({
37 TextStyle? style, 41 TextStyle? style,
@@ -51,6 +55,8 @@ class GptMarkdownConfig { @@ -51,6 +55,8 @@ class GptMarkdownConfig {
51 codeBuilder, 55 codeBuilder,
52 final int? maxLines, 56 final int? maxLines,
53 final TextOverflow? overflow, 57 final TextOverflow? overflow,
  58 + final Widget Function(BuildContext context, String text, TextStyle style)? highlightBuilder,
  59 + final Widget Function(BuildContext context, String text, String url, TextStyle style)? linkBuilder,
54 }) { 60 }) {
55 return GptMarkdownConfig( 61 return GptMarkdownConfig(
56 style: style ?? this.style, 62 style: style ?? this.style,
@@ -65,6 +71,8 @@ class GptMarkdownConfig { @@ -65,6 +71,8 @@ class GptMarkdownConfig {
65 sourceTagBuilder: sourceTagBuilder ?? this.sourceTagBuilder, 71 sourceTagBuilder: sourceTagBuilder ?? this.sourceTagBuilder,
66 maxLines: maxLines ?? this.maxLines, 72 maxLines: maxLines ?? this.maxLines,
67 overflow: overflow ?? this.overflow, 73 overflow: overflow ?? this.overflow,
  74 + highlightBuilder: highlightBuilder ?? this.highlightBuilder,
  75 + linkBuilder: linkBuilder ?? this.linkBuilder,
68 ); 76 );
69 } 77 }
70 78
@@ -34,6 +34,8 @@ class GptMarkdown extends StatelessWidget { @@ -34,6 +34,8 @@ class GptMarkdown extends StatelessWidget {
34 this.latexBuilder, 34 this.latexBuilder,
35 this.codeBuilder, 35 this.codeBuilder,
36 this.sourceTagBuilder, 36 this.sourceTagBuilder,
  37 + this.highlightBuilder,
  38 + this.linkBuilder,
37 this.maxLines, 39 this.maxLines,
38 this.overflow, 40 this.overflow,
39 }); 41 });
@@ -53,6 +55,8 @@ class GptMarkdown extends StatelessWidget { @@ -53,6 +55,8 @@ class GptMarkdown extends StatelessWidget {
53 final Widget Function(BuildContext context, String name, String code)? 55 final Widget Function(BuildContext context, String name, String code)?
54 codeBuilder; 56 codeBuilder;
55 final Widget Function(BuildContext, String, TextStyle)? sourceTagBuilder; 57 final Widget Function(BuildContext, String, TextStyle)? sourceTagBuilder;
  58 + final Widget Function(BuildContext context, String text, TextStyle style)? highlightBuilder;
  59 + final Widget Function(BuildContext context, String text, String url, TextStyle style)? linkBuilder;
56 60
57 @override 61 @override
58 Widget build(BuildContext context) { 62 Widget build(BuildContext context) {
@@ -93,6 +97,8 @@ class GptMarkdown extends StatelessWidget { @@ -93,6 +97,8 @@ class GptMarkdown extends StatelessWidget {
93 maxLines: maxLines, 97 maxLines: maxLines,
94 overflow: overflow, 98 overflow: overflow,
95 sourceTagBuilder: sourceTagBuilder, 99 sourceTagBuilder: sourceTagBuilder,
  100 + highlightBuilder: highlightBuilder,
  101 + linkBuilder: linkBuilder,
96 ), 102 ),
97 )); 103 ));
98 } 104 }
@@ -422,25 +422,37 @@ class HighlightedText extends InlineMd { @@ -422,25 +422,37 @@ class HighlightedText extends InlineMd {
422 final GptMarkdownConfig config, 422 final GptMarkdownConfig config,
423 ) { 423 ) {
424 var match = exp.firstMatch(text.trim()); 424 var match = exp.firstMatch(text.trim());
425 - var conf = config.copyWith(  
426 - style: config.style?.copyWith(  
427 - fontWeight: FontWeight.bold,  
428 - background: Paint()  
429 - ..color = GptMarkdownTheme.of(context).highlightColor  
430 - ..strokeCap = StrokeCap.round  
431 - ..strokeJoin = StrokeJoin.round,  
432 - ) ??  
433 - TextStyle(  
434 - fontWeight: FontWeight.bold,  
435 - background: Paint()  
436 - ..color = GptMarkdownTheme.of(context).highlightColor  
437 - ..strokeCap = StrokeCap.round  
438 - ..strokeJoin = StrokeJoin.round,  
439 - ),  
440 - ); 425 + var highlightedText = match?[1] ?? "";
  426 +
  427 + if (config.highlightBuilder != null) {
  428 + return WidgetSpan(
  429 + alignment: PlaceholderAlignment.middle,
  430 + child: config.highlightBuilder!(
  431 + context,
  432 + highlightedText,
  433 + config.style ?? const TextStyle(),
  434 + ),
  435 + );
  436 + }
  437 +
  438 + var style = config.style?.copyWith(
  439 + fontWeight: FontWeight.bold,
  440 + background: Paint()
  441 + ..color = GptMarkdownTheme.of(context).highlightColor
  442 + ..strokeCap = StrokeCap.round
  443 + ..strokeJoin = StrokeJoin.round,
  444 + ) ??
  445 + TextStyle(
  446 + fontWeight: FontWeight.bold,
  447 + background: Paint()
  448 + ..color = GptMarkdownTheme.of(context).highlightColor
  449 + ..strokeCap = StrokeCap.round
  450 + ..strokeJoin = StrokeJoin.round,
  451 + );
  452 +
441 return TextSpan( 453 return TextSpan(
442 - text: match?[1],  
443 - style: conf.style, 454 + text: highlightedText,
  455 + style: style,
444 ); 456 );
445 } 457 }
446 } 458 }
@@ -721,15 +733,35 @@ class ATagMd extends InlineMd { @@ -721,15 +733,35 @@ class ATagMd extends InlineMd {
721 if (match?[1] == null && match?[2] == null) { 733 if (match?[1] == null && match?[2] == null) {
722 return const TextSpan(); 734 return const TextSpan();
723 } 735 }
  736 +
  737 + final linkText = match?[1] ?? "";
  738 + final url = match?[2] ?? "";
  739 +
  740 + // Use custom builder if provided
  741 + if (config.linkBuilder != null) {
  742 + return WidgetSpan(
  743 + child: GestureDetector(
  744 + onTap: () => config.onLinkTab?.call(url, linkText),
  745 + child: config.linkBuilder!(
  746 + context,
  747 + linkText,
  748 + url,
  749 + config.style ?? const TextStyle(),
  750 + ),
  751 + ),
  752 + );
  753 + }
  754 +
  755 + // Default rendering
724 var theme = GptMarkdownTheme.of(context); 756 var theme = GptMarkdownTheme.of(context);
725 return WidgetSpan( 757 return WidgetSpan(
726 child: LinkButton( 758 child: LinkButton(
727 hoverColor: theme.linkHoverColor, 759 hoverColor: theme.linkHoverColor,
728 color: theme.linkColor, 760 color: theme.linkColor,
729 onPressed: () { 761 onPressed: () {
730 - config.onLinkTab?.call("${match?[2]}", "${match?[1]}"); 762 + config.onLinkTab?.call(url, linkText);
731 }, 763 },
732 - text: match?[1] ?? "", 764 + text: linkText,
733 config: config, 765 config: config,
734 ), 766 ),
735 ); 767 );
@@ -892,11 +924,9 @@ class CodeBlockMd extends BlockMd { @@ -892,11 +924,9 @@ class CodeBlockMd extends BlockMd {
892 String codes = this.exp.firstMatch(text)?[2] ?? ""; 924 String codes = this.exp.firstMatch(text)?[2] ?? "";
893 String name = this.exp.firstMatch(text)?[1] ?? ""; 925 String name = this.exp.firstMatch(text)?[1] ?? "";
894 codes = codes.replaceAll(r"```", "").trim(); 926 codes = codes.replaceAll(r"```", "").trim();
895 - return Padding(  
896 - padding: const EdgeInsets.all(16.0),  
897 - child: config.codeBuilder != null  
898 - ? config.codeBuilder?.call(context, name, codes)  
899 - : CodeField(name: name, codes: codes),  
900 - ); 927 +
  928 + return config.codeBuilder != null
  929 + ? config.codeBuilder!(context, name, codes)
  930 + : CodeField(name: name, codes: codes);
901 } 931 }
902 } 932 }