saminsohag

fixed issues 34 and 72

## 1.1.1
* 🖼️ Fixed issue where images wrapped in links (e.g. `[![](img)](url)`) were not rendering properly (#72)
* 🔗 Resolved parsing errors for consecutive inline links without spacing (e.g. `[a](url)[b](url)`) (#34)
## 1.1.0
* Changed `onLinkTab` to `onLinkTap` fixed issues of newLine issues.
... ...
... ... @@ -75,21 +75,15 @@ class MyHomePage extends StatefulWidget {
class _MyHomePageState extends State<MyHomePage> {
TextDirection _direction = TextDirection.ltr;
final TextEditingController _controller = TextEditingController(
text: r'''
decsiob (*) is on the set PQ = {91, 905} jjjzjsx * jjdbhsjsjmamajmsghdhhi msnnsjnskaksjjshahsh
(*)
This is a sample markdown document.
text: r'''This is a sample markdown document.
* **bold**
* *italic*
* **_bold and italic_**
* ~~strikethrough~~
* `code`
* [link](https://www.google.com) ![image](https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png)
* [link](https://www.google.com)
[![alt text](https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png)](link_url)
```markdown
# Complex Markdown Document for Testing
... ... @@ -457,7 +451,8 @@ This document was created to test the robustness of Markdown parsers and to ensu
textAlign: TextAlign.justify,
textScaler: const TextScaler.linear(1),
style: const TextStyle(
fontSize: 15,
fontFamily: 'monospace',
fontWeight: FontWeight.bold,
),
highlightBuilder: (context, text, style) {
return Container(
... ... @@ -612,7 +607,7 @@ This document was created to test the robustness of Markdown parsers and to ensu
},
linkBuilder:
(context, label, path, style) {
return Text(
return Text.rich(
label,
style: style.copyWith(
color: Colors.blue,
... ...
... ... @@ -13,6 +13,9 @@ class LinkButton extends StatefulWidget {
/// The text of the link.
final String text;
/// The child of the link.
final Widget? child;
/// The callback function to be called when the link is pressed.
final VoidCallback? onPressed;
... ... @@ -40,6 +43,7 @@ class LinkButton extends StatefulWidget {
this.onPressed,
this.textStyle,
this.url,
this.child,
});
@override
... ... @@ -65,7 +69,9 @@ class _LinkButtonState extends State<LinkButton> {
onTapUp: (_) => _handlePress(false),
onTapCancel: () => _handlePress(false),
onTap: widget.onPressed,
child: widget.config.getRich(TextSpan(text: widget.text, style: style)),
child:
widget.child ??
widget.config.getRich(TextSpan(text: widget.text, style: style)),
),
);
}
... ...
... ... @@ -44,7 +44,7 @@ typedef LatexBuilder =
typedef LinkBuilder =
Widget Function(
BuildContext context,
String text,
InlineSpan text,
String url,
TextStyle style,
);
... ...
... ... @@ -18,8 +18,8 @@ abstract class MarkdownComponent {
];
static final List<MarkdownComponent> inlineComponents = [
ImageMd(),
ATagMd(),
ImageMd(),
TableMd(),
StrikeMd(),
BoldMd(),
... ... @@ -780,7 +780,7 @@ class SourceTag extends InlineMd {
/// Link text component
class ATagMd extends InlineMd {
@override
RegExp get exp => RegExp(r"\[[^\[\]]*\]\([^\s]*\)");
RegExp get exp => RegExp(r"(?<!\!)\[.*\]\([^\s]*\)");
@override
InlineSpan span(
... ... @@ -788,14 +788,33 @@ class ATagMd extends InlineMd {
String text,
final GptMarkdownConfig config,
) {
// First try to find the basic pattern
final basicMatch = RegExp(r'\[([^\[\]]*)\]\(').firstMatch(text.trim());
if (basicMatch == null) {
var bracketCount = 0;
var start = 1;
var end = 0;
for (var i = 0; i < text.length; i++) {
if (text[i] == '[') {
bracketCount++;
} else if (text[i] == ']') {
bracketCount--;
if (bracketCount == 0) {
end = i;
break;
}
}
}
if (text[end + 1] != '(') {
return const TextSpan();
}
final linkText = basicMatch.group(1) ?? '';
final urlStart = basicMatch.end;
// First try to find the basic pattern
// final basicMatch = RegExp(r'(?<!\!)\[(.*)\]\(').firstMatch(text.trim());
// if (basicMatch == null) {
// return const TextSpan();
// }
final linkText = text.substring(start, end);
final urlStart = end + 2;
// Now find the balanced closing parenthesis
int parenCount = 0;
... ... @@ -826,14 +845,29 @@ class ATagMd extends InlineMd {
var builder = config.linkBuilder;
var ending = text.substring(urlEnd + 1);
var endingSpans = MarkdownComponent.generate(
context,
ending,
config,
false,
);
var theme = GptMarkdownTheme.of(context);
var linkTextSpan = TextSpan(
children: MarkdownComponent.generate(context, linkText, config, false),
style: config.style?.copyWith(color: theme.linkColor),
);
// Use custom builder if provided
WidgetSpan? child;
if (builder != null) {
return WidgetSpan(
child = WidgetSpan(
child: GestureDetector(
onTap: () => config.onLinkTap?.call(url, linkText),
child: builder(
context,
linkText,
linkTextSpan,
url,
config.style ?? const TextStyle(),
),
... ... @@ -842,8 +876,9 @@ class ATagMd extends InlineMd {
}
// Default rendering
var theme = GptMarkdownTheme.of(context);
return WidgetSpan(
child ??= WidgetSpan(
alignment: PlaceholderAlignment.baseline,
baseline: TextBaseline.alphabetic,
child: LinkButton(
hoverColor: theme.linkHoverColor,
color: theme.linkColor,
... ... @@ -852,8 +887,11 @@ class ATagMd extends InlineMd {
},
text: linkText,
config: config,
child: config.getRich(linkTextSpan),
),
);
var textSpan = TextSpan(children: [child, ...endingSpans]);
return textSpan;
}
}
... ...
name: gpt_markdown
description: "Powerful Flutter Markdown & LaTeX Renderer: Rich Text, Math, Tables, Links, and Text Selection. Ideal for ChatGPT, Gemini, and more."
version: 1.1.0
version: 1.1.1
homepage: https://github.com/Infinitix-LLC/gpt_markdown
environment:
... ...