Showing
6 changed files
with
69 additions
and
25 deletions
| 1 | +## 1.1.1 | ||
| 2 | + | ||
| 3 | +* 🖼️ Fixed issue where images wrapped in links (e.g. `[](url)`) were not rendering properly (#72) | ||
| 4 | +* 🔗 Resolved parsing errors for consecutive inline links without spacing (e.g. `[a](url)[b](url)`) (#34) | ||
| 5 | + | ||
| 1 | ## 1.1.0 | 6 | ## 1.1.0 |
| 2 | 7 | ||
| 3 | * Changed `onLinkTab` to `onLinkTap` fixed issues of newLine issues. | 8 | * Changed `onLinkTab` to `onLinkTap` fixed issues of newLine issues. |
| @@ -75,21 +75,15 @@ class MyHomePage extends StatefulWidget { | @@ -75,21 +75,15 @@ class MyHomePage extends StatefulWidget { | ||
| 75 | class _MyHomePageState extends State<MyHomePage> { | 75 | class _MyHomePageState extends State<MyHomePage> { |
| 76 | TextDirection _direction = TextDirection.ltr; | 76 | TextDirection _direction = TextDirection.ltr; |
| 77 | final TextEditingController _controller = TextEditingController( | 77 | final TextEditingController _controller = TextEditingController( |
| 78 | - text: r''' | ||
| 79 | -decsiob (*) is on the set PQ = {91, 905} jjjzjsx * jjdbhsjsjmamajmsghdhhi msnnsjnskaksjjshahsh | ||
| 80 | - | ||
| 81 | -(*) | ||
| 82 | - | ||
| 83 | -This is a sample markdown document. | ||
| 84 | - | 78 | + text: r'''This is a sample markdown document. |
| 85 | * **bold** | 79 | * **bold** |
| 86 | * *italic* | 80 | * *italic* |
| 87 | * **_bold and italic_** | 81 | * **_bold and italic_** |
| 88 | * ~~strikethrough~~ | 82 | * ~~strikethrough~~ |
| 89 | * `code` | 83 | * `code` |
| 90 | -* [link](https://www.google.com)  | ||
| 91 | - | 84 | +* [link](https://www.google.com) |
| 92 | 85 | ||
| 86 | +[](link_url) | ||
| 93 | ```markdown | 87 | ```markdown |
| 94 | # Complex Markdown Document for Testing | 88 | # Complex Markdown Document for Testing |
| 95 | 89 | ||
| @@ -457,7 +451,8 @@ This document was created to test the robustness of Markdown parsers and to ensu | @@ -457,7 +451,8 @@ This document was created to test the robustness of Markdown parsers and to ensu | ||
| 457 | textAlign: TextAlign.justify, | 451 | textAlign: TextAlign.justify, |
| 458 | textScaler: const TextScaler.linear(1), | 452 | textScaler: const TextScaler.linear(1), |
| 459 | style: const TextStyle( | 453 | style: const TextStyle( |
| 460 | - fontSize: 15, | 454 | + fontFamily: 'monospace', |
| 455 | + fontWeight: FontWeight.bold, | ||
| 461 | ), | 456 | ), |
| 462 | highlightBuilder: (context, text, style) { | 457 | highlightBuilder: (context, text, style) { |
| 463 | return Container( | 458 | return Container( |
| @@ -612,7 +607,7 @@ This document was created to test the robustness of Markdown parsers and to ensu | @@ -612,7 +607,7 @@ This document was created to test the robustness of Markdown parsers and to ensu | ||
| 612 | }, | 607 | }, |
| 613 | linkBuilder: | 608 | linkBuilder: |
| 614 | (context, label, path, style) { | 609 | (context, label, path, style) { |
| 615 | - return Text( | 610 | + return Text.rich( |
| 616 | label, | 611 | label, |
| 617 | style: style.copyWith( | 612 | style: style.copyWith( |
| 618 | color: Colors.blue, | 613 | color: Colors.blue, |
| @@ -13,6 +13,9 @@ class LinkButton extends StatefulWidget { | @@ -13,6 +13,9 @@ class LinkButton extends StatefulWidget { | ||
| 13 | /// The text of the link. | 13 | /// The text of the link. |
| 14 | final String text; | 14 | final String text; |
| 15 | 15 | ||
| 16 | + /// The child of the link. | ||
| 17 | + final Widget? child; | ||
| 18 | + | ||
| 16 | /// The callback function to be called when the link is pressed. | 19 | /// The callback function to be called when the link is pressed. |
| 17 | final VoidCallback? onPressed; | 20 | final VoidCallback? onPressed; |
| 18 | 21 | ||
| @@ -40,6 +43,7 @@ class LinkButton extends StatefulWidget { | @@ -40,6 +43,7 @@ class LinkButton extends StatefulWidget { | ||
| 40 | this.onPressed, | 43 | this.onPressed, |
| 41 | this.textStyle, | 44 | this.textStyle, |
| 42 | this.url, | 45 | this.url, |
| 46 | + this.child, | ||
| 43 | }); | 47 | }); |
| 44 | 48 | ||
| 45 | @override | 49 | @override |
| @@ -65,7 +69,9 @@ class _LinkButtonState extends State<LinkButton> { | @@ -65,7 +69,9 @@ class _LinkButtonState extends State<LinkButton> { | ||
| 65 | onTapUp: (_) => _handlePress(false), | 69 | onTapUp: (_) => _handlePress(false), |
| 66 | onTapCancel: () => _handlePress(false), | 70 | onTapCancel: () => _handlePress(false), |
| 67 | onTap: widget.onPressed, | 71 | onTap: widget.onPressed, |
| 68 | - child: widget.config.getRich(TextSpan(text: widget.text, style: style)), | 72 | + child: |
| 73 | + widget.child ?? | ||
| 74 | + widget.config.getRich(TextSpan(text: widget.text, style: style)), | ||
| 69 | ), | 75 | ), |
| 70 | ); | 76 | ); |
| 71 | } | 77 | } |
| @@ -44,7 +44,7 @@ typedef LatexBuilder = | @@ -44,7 +44,7 @@ typedef LatexBuilder = | ||
| 44 | typedef LinkBuilder = | 44 | typedef LinkBuilder = |
| 45 | Widget Function( | 45 | Widget Function( |
| 46 | BuildContext context, | 46 | BuildContext context, |
| 47 | - String text, | 47 | + InlineSpan text, |
| 48 | String url, | 48 | String url, |
| 49 | TextStyle style, | 49 | TextStyle style, |
| 50 | ); | 50 | ); |
| @@ -18,8 +18,8 @@ abstract class MarkdownComponent { | @@ -18,8 +18,8 @@ abstract class MarkdownComponent { | ||
| 18 | ]; | 18 | ]; |
| 19 | 19 | ||
| 20 | static final List<MarkdownComponent> inlineComponents = [ | 20 | static final List<MarkdownComponent> inlineComponents = [ |
| 21 | - ImageMd(), | ||
| 22 | ATagMd(), | 21 | ATagMd(), |
| 22 | + ImageMd(), | ||
| 23 | TableMd(), | 23 | TableMd(), |
| 24 | StrikeMd(), | 24 | StrikeMd(), |
| 25 | BoldMd(), | 25 | BoldMd(), |
| @@ -780,7 +780,7 @@ class SourceTag extends InlineMd { | @@ -780,7 +780,7 @@ class SourceTag extends InlineMd { | ||
| 780 | /// Link text component | 780 | /// Link text component |
| 781 | class ATagMd extends InlineMd { | 781 | class ATagMd extends InlineMd { |
| 782 | @override | 782 | @override |
| 783 | - RegExp get exp => RegExp(r"\[[^\[\]]*\]\([^\s]*\)"); | 783 | + RegExp get exp => RegExp(r"(?<!\!)\[.*\]\([^\s]*\)"); |
| 784 | 784 | ||
| 785 | @override | 785 | @override |
| 786 | InlineSpan span( | 786 | InlineSpan span( |
| @@ -788,14 +788,33 @@ class ATagMd extends InlineMd { | @@ -788,14 +788,33 @@ class ATagMd extends InlineMd { | ||
| 788 | String text, | 788 | String text, |
| 789 | final GptMarkdownConfig config, | 789 | final GptMarkdownConfig config, |
| 790 | ) { | 790 | ) { |
| 791 | - // First try to find the basic pattern | ||
| 792 | - final basicMatch = RegExp(r'\[([^\[\]]*)\]\(').firstMatch(text.trim()); | ||
| 793 | - if (basicMatch == null) { | 791 | + var bracketCount = 0; |
| 792 | + var start = 1; | ||
| 793 | + var end = 0; | ||
| 794 | + for (var i = 0; i < text.length; i++) { | ||
| 795 | + if (text[i] == '[') { | ||
| 796 | + bracketCount++; | ||
| 797 | + } else if (text[i] == ']') { | ||
| 798 | + bracketCount--; | ||
| 799 | + if (bracketCount == 0) { | ||
| 800 | + end = i; | ||
| 801 | + break; | ||
| 802 | + } | ||
| 803 | + } | ||
| 804 | + } | ||
| 805 | + | ||
| 806 | + if (text[end + 1] != '(') { | ||
| 794 | return const TextSpan(); | 807 | return const TextSpan(); |
| 795 | } | 808 | } |
| 796 | 809 | ||
| 797 | - final linkText = basicMatch.group(1) ?? ''; | ||
| 798 | - final urlStart = basicMatch.end; | 810 | + // First try to find the basic pattern |
| 811 | + // final basicMatch = RegExp(r'(?<!\!)\[(.*)\]\(').firstMatch(text.trim()); | ||
| 812 | + // if (basicMatch == null) { | ||
| 813 | + // return const TextSpan(); | ||
| 814 | + // } | ||
| 815 | + | ||
| 816 | + final linkText = text.substring(start, end); | ||
| 817 | + final urlStart = end + 2; | ||
| 799 | 818 | ||
| 800 | // Now find the balanced closing parenthesis | 819 | // Now find the balanced closing parenthesis |
| 801 | int parenCount = 0; | 820 | int parenCount = 0; |
| @@ -826,14 +845,29 @@ class ATagMd extends InlineMd { | @@ -826,14 +845,29 @@ class ATagMd extends InlineMd { | ||
| 826 | 845 | ||
| 827 | var builder = config.linkBuilder; | 846 | var builder = config.linkBuilder; |
| 828 | 847 | ||
| 848 | + var ending = text.substring(urlEnd + 1); | ||
| 849 | + | ||
| 850 | + var endingSpans = MarkdownComponent.generate( | ||
| 851 | + context, | ||
| 852 | + ending, | ||
| 853 | + config, | ||
| 854 | + false, | ||
| 855 | + ); | ||
| 856 | + var theme = GptMarkdownTheme.of(context); | ||
| 857 | + var linkTextSpan = TextSpan( | ||
| 858 | + children: MarkdownComponent.generate(context, linkText, config, false), | ||
| 859 | + style: config.style?.copyWith(color: theme.linkColor), | ||
| 860 | + ); | ||
| 861 | + | ||
| 829 | // Use custom builder if provided | 862 | // Use custom builder if provided |
| 863 | + WidgetSpan? child; | ||
| 830 | if (builder != null) { | 864 | if (builder != null) { |
| 831 | - return WidgetSpan( | 865 | + child = WidgetSpan( |
| 832 | child: GestureDetector( | 866 | child: GestureDetector( |
| 833 | onTap: () => config.onLinkTap?.call(url, linkText), | 867 | onTap: () => config.onLinkTap?.call(url, linkText), |
| 834 | child: builder( | 868 | child: builder( |
| 835 | context, | 869 | context, |
| 836 | - linkText, | 870 | + linkTextSpan, |
| 837 | url, | 871 | url, |
| 838 | config.style ?? const TextStyle(), | 872 | config.style ?? const TextStyle(), |
| 839 | ), | 873 | ), |
| @@ -842,8 +876,9 @@ class ATagMd extends InlineMd { | @@ -842,8 +876,9 @@ class ATagMd extends InlineMd { | ||
| 842 | } | 876 | } |
| 843 | 877 | ||
| 844 | // Default rendering | 878 | // Default rendering |
| 845 | - var theme = GptMarkdownTheme.of(context); | ||
| 846 | - return WidgetSpan( | 879 | + child ??= WidgetSpan( |
| 880 | + alignment: PlaceholderAlignment.baseline, | ||
| 881 | + baseline: TextBaseline.alphabetic, | ||
| 847 | child: LinkButton( | 882 | child: LinkButton( |
| 848 | hoverColor: theme.linkHoverColor, | 883 | hoverColor: theme.linkHoverColor, |
| 849 | color: theme.linkColor, | 884 | color: theme.linkColor, |
| @@ -852,8 +887,11 @@ class ATagMd extends InlineMd { | @@ -852,8 +887,11 @@ class ATagMd extends InlineMd { | ||
| 852 | }, | 887 | }, |
| 853 | text: linkText, | 888 | text: linkText, |
| 854 | config: config, | 889 | config: config, |
| 890 | + child: config.getRich(linkTextSpan), | ||
| 855 | ), | 891 | ), |
| 856 | ); | 892 | ); |
| 893 | + var textSpan = TextSpan(children: [child, ...endingSpans]); | ||
| 894 | + return textSpan; | ||
| 857 | } | 895 | } |
| 858 | } | 896 | } |
| 859 | 897 |
| 1 | name: gpt_markdown | 1 | name: gpt_markdown |
| 2 | description: "Powerful Flutter Markdown & LaTeX Renderer: Rich Text, Math, Tables, Links, and Text Selection. Ideal for ChatGPT, Gemini, and more." | 2 | description: "Powerful Flutter Markdown & LaTeX Renderer: Rich Text, Math, Tables, Links, and Text Selection. Ideal for ChatGPT, Gemini, and more." |
| 3 | -version: 1.1.0 | 3 | +version: 1.1.1 |
| 4 | homepage: https://github.com/Infinitix-LLC/gpt_markdown | 4 | homepage: https://github.com/Infinitix-LLC/gpt_markdown |
| 5 | 5 | ||
| 6 | environment: | 6 | environment: |
-
Please register or login to post a comment