Showing
8 changed files
with
146 additions
and
74 deletions
1 | +## 1.0.6 | ||
2 | + | ||
3 | +* `_italic_` and `>Indentation` syntax added. | ||
4 | +* `linkBuilder` and `highlightBuilder` added `[f45132b](https://github.com/Infinitix-LLC/gpt_markdown/commit/f45132b2cd4b069d3e5703561deb5c7e51d3c560)`. | ||
5 | + | ||
1 | ## 1.0.5 | 6 | ## 1.0.5 |
2 | 7 | ||
3 | * Fixed the order of inline and block latex in markdown. | 8 | * Fixed the order of inline and block latex in markdown. |
@@ -269,22 +269,33 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex | @@ -269,22 +269,33 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex | ||
269 | ), | 269 | ), |
270 | highlightBuilder: (context, text, style) { | 270 | highlightBuilder: (context, text, style) { |
271 | return Container( | 271 | return Container( |
272 | - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), | 272 | + padding: const EdgeInsets.symmetric( |
273 | + horizontal: 4, vertical: 2), | ||
273 | decoration: BoxDecoration( | 274 | decoration: BoxDecoration( |
274 | - color: Theme.of(context).colorScheme.secondaryContainer, | ||
275 | - borderRadius: BorderRadius.circular(4), | 275 | + color: Theme.of(context) |
276 | + .colorScheme | ||
277 | + .secondaryContainer, | ||
278 | + borderRadius: | ||
279 | + BorderRadius.circular(4), | ||
276 | border: Border.all( | 280 | border: Border.all( |
277 | - color: Theme.of(context).colorScheme.secondary.withOpacity(0.5), | 281 | + color: Theme.of(context) |
282 | + .colorScheme | ||
283 | + .secondary | ||
284 | + .withValues(alpha: 0.5), | ||
278 | width: 1, | 285 | width: 1, |
279 | ), | 286 | ), |
280 | ), | 287 | ), |
281 | child: Text( | 288 | child: Text( |
282 | text, | 289 | text, |
283 | style: TextStyle( | 290 | style: TextStyle( |
284 | - color: Theme.of(context).colorScheme.onSecondaryContainer, | 291 | + color: Theme.of(context) |
292 | + .colorScheme | ||
293 | + .onSecondaryContainer, | ||
285 | fontFamily: 'monospace', | 294 | fontFamily: 'monospace', |
286 | fontWeight: FontWeight.bold, | 295 | fontWeight: FontWeight.bold, |
287 | - fontSize: style.fontSize != null ? style.fontSize! * 0.9 : 13.5, | 296 | + fontSize: style.fontSize != null |
297 | + ? style.fontSize! * 0.9 | ||
298 | + : 13.5, | ||
288 | height: style.height, | 299 | height: style.height, |
289 | ), | 300 | ), |
290 | ), | 301 | ), |
@@ -400,21 +411,29 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex | @@ -400,21 +411,29 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex | ||
400 | ), | 411 | ), |
401 | ); | 412 | ); |
402 | }, | 413 | }, |
414 | + linkBuilder: | ||
415 | + (context, label, path, style) { | ||
416 | + // | ||
417 | + return Text(path); | ||
418 | + }, | ||
419 | + // codeBuilder: (context, name, code) { | ||
420 | + // return Padding( | ||
421 | + // padding: const EdgeInsets.symmetric( | ||
422 | + // horizontal: 16), | ||
423 | + // child: Text( | ||
424 | + // code.trim(), | ||
425 | + // style: TextStyle( | ||
426 | + // fontFamily: 'JetBrains Mono', | ||
427 | + // fontSize: 14, | ||
428 | + // height: 1.5, | ||
429 | + // color: Theme.of(context) | ||
430 | + // .colorScheme | ||
431 | + // .onSurface, | ||
432 | + // ), | ||
433 | + // ), | ||
434 | + // ); | ||
435 | + // } | ||
403 | ); | 436 | ); |
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 | - }; | ||
418 | if (selectable) { | 437 | if (selectable) { |
419 | child = SelectionArea( | 438 | child = SelectionArea( |
420 | child: child, | 439 | child: child, |
@@ -126,7 +126,7 @@ packages: | @@ -126,7 +126,7 @@ packages: | ||
126 | path: ".." | 126 | path: ".." |
127 | relative: true | 127 | relative: true |
128 | source: path | 128 | source: path |
129 | - version: "1.0.5" | 129 | + version: "1.0.6" |
130 | http: | 130 | http: |
131 | dependency: transitive | 131 | dependency: transitive |
132 | description: | 132 | description: |
lib/custom_widgets/indent_widget.dart
0 → 100644
1 | +import 'package:flutter/material.dart'; | ||
2 | + | ||
3 | +class IndentWidget extends StatelessWidget { | ||
4 | + const IndentWidget({ | ||
5 | + super.key, | ||
6 | + required this.child, | ||
7 | + required this.direction, | ||
8 | + required this.color, | ||
9 | + }); | ||
10 | + final Widget child; | ||
11 | + final TextDirection direction; | ||
12 | + final Color color; | ||
13 | + | ||
14 | + @override | ||
15 | + Widget build(BuildContext context) { | ||
16 | + return CustomPaint( | ||
17 | + foregroundPainter: IndentPainter(color, direction), | ||
18 | + child: child, | ||
19 | + ); | ||
20 | + } | ||
21 | +} | ||
22 | + | ||
23 | +class IndentPainter extends CustomPainter { | ||
24 | + IndentPainter(this.color, this.direction); | ||
25 | + final Color color; | ||
26 | + final TextDirection direction; | ||
27 | + @override | ||
28 | + void paint(Canvas canvas, Size size) { | ||
29 | + var left = direction == TextDirection.ltr; | ||
30 | + var start = left ? 0.0 : size.width - 4; | ||
31 | + var rect = Rect.fromLTWH(start, 0, 4, size.height); | ||
32 | + var paint = Paint()..color = color; | ||
33 | + canvas.drawRect(rect, paint); | ||
34 | + } | ||
35 | + | ||
36 | + @override | ||
37 | + bool shouldRepaint(covariant CustomPainter oldDelegate) { | ||
38 | + return true; | ||
39 | + } | ||
40 | +} |
@@ -34,8 +34,11 @@ class GptMarkdownConfig { | @@ -34,8 +34,11 @@ class GptMarkdownConfig { | ||
34 | codeBuilder; | 34 | codeBuilder; |
35 | final int? maxLines; | 35 | final int? maxLines; |
36 | 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; | 37 | + final Widget Function(BuildContext context, String text, TextStyle style)? |
38 | + highlightBuilder; | ||
39 | + final Widget Function( | ||
40 | + BuildContext context, String text, String url, TextStyle style)? | ||
41 | + linkBuilder; | ||
39 | 42 | ||
40 | GptMarkdownConfig copyWith({ | 43 | GptMarkdownConfig copyWith({ |
41 | TextStyle? style, | 44 | TextStyle? style, |
@@ -55,8 +58,11 @@ class GptMarkdownConfig { | @@ -55,8 +58,11 @@ class GptMarkdownConfig { | ||
55 | codeBuilder, | 58 | codeBuilder, |
56 | final int? maxLines, | 59 | final int? maxLines, |
57 | final TextOverflow? overflow, | 60 | 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, | 61 | + final Widget Function(BuildContext context, String text, TextStyle style)? |
62 | + highlightBuilder, | ||
63 | + final Widget Function( | ||
64 | + BuildContext context, String text, String url, TextStyle style)? | ||
65 | + linkBuilder, | ||
60 | }) { | 66 | }) { |
61 | return GptMarkdownConfig( | 67 | return GptMarkdownConfig( |
62 | style: style ?? this.style, | 68 | style: style ?? this.style, |
@@ -13,6 +13,7 @@ import 'package:gpt_markdown/custom_widgets/unordered_ordered_list.dart'; | @@ -13,6 +13,7 @@ import 'package:gpt_markdown/custom_widgets/unordered_ordered_list.dart'; | ||
13 | import 'dart:math'; | 13 | import 'dart:math'; |
14 | 14 | ||
15 | import 'custom_widgets/code_field.dart'; | 15 | import 'custom_widgets/code_field.dart'; |
16 | +import 'custom_widgets/indent_widget.dart'; | ||
16 | import 'custom_widgets/link_button.dart'; | 17 | import 'custom_widgets/link_button.dart'; |
17 | 18 | ||
18 | part 'theme.dart'; | 19 | part 'theme.dart'; |
@@ -55,8 +56,11 @@ class GptMarkdown extends StatelessWidget { | @@ -55,8 +56,11 @@ class GptMarkdown extends StatelessWidget { | ||
55 | final Widget Function(BuildContext context, String name, String code)? | 56 | final Widget Function(BuildContext context, String name, String code)? |
56 | codeBuilder; | 57 | codeBuilder; |
57 | final Widget Function(BuildContext, String, TextStyle)? sourceTagBuilder; | 58 | 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; | 59 | + final Widget Function(BuildContext context, String text, TextStyle style)? |
60 | + highlightBuilder; | ||
61 | + final Widget Function( | ||
62 | + BuildContext context, String text, String url, TextStyle style)? | ||
63 | + linkBuilder; | ||
60 | 64 | ||
61 | @override | 65 | @override |
62 | Widget build(BuildContext context) { | 66 | Widget build(BuildContext context) { |
@@ -2,28 +2,28 @@ part of 'gpt_markdown.dart'; | @@ -2,28 +2,28 @@ part of 'gpt_markdown.dart'; | ||
2 | 2 | ||
3 | /// Markdown components | 3 | /// Markdown components |
4 | abstract class MarkdownComponent { | 4 | abstract class MarkdownComponent { |
5 | - static final List<MarkdownComponent> components = [ | ||
6 | - CodeBlockMd(), | ||
7 | - NewLines(), | ||
8 | - // IndentMd(), | ||
9 | - ImageMd(), | ||
10 | - TableMd(), | ||
11 | - HTag(), | ||
12 | - UnOrderedList(), | ||
13 | - OrderedList(), | ||
14 | - RadioButtonMd(), | ||
15 | - CheckBoxMd(), | ||
16 | - HrLine(), | ||
17 | - LatexMath(), | ||
18 | - LatexMathMultiLine(), | ||
19 | - ImageMd(), | ||
20 | - HighlightedText(), | ||
21 | - StrikeMd(), | ||
22 | - BoldMd(), | ||
23 | - ItalicMd(), | ||
24 | - ATagMd(), | ||
25 | - SourceTag(), | ||
26 | - ]; | 5 | + static List<MarkdownComponent> get components => [ |
6 | + CodeBlockMd(), | ||
7 | + NewLines(), | ||
8 | + IndentMd(), | ||
9 | + ImageMd(), | ||
10 | + TableMd(), | ||
11 | + HTag(), | ||
12 | + UnOrderedList(), | ||
13 | + OrderedList(), | ||
14 | + RadioButtonMd(), | ||
15 | + CheckBoxMd(), | ||
16 | + HrLine(), | ||
17 | + LatexMath(), | ||
18 | + LatexMathMultiLine(), | ||
19 | + ImageMd(), | ||
20 | + HighlightedText(), | ||
21 | + StrikeMd(), | ||
22 | + BoldMd(), | ||
23 | + ItalicMd(), | ||
24 | + ATagMd(), | ||
25 | + SourceTag(), | ||
26 | + ]; | ||
27 | 27 | ||
28 | /// Generate widget for markdown widget | 28 | /// Generate widget for markdown widget |
29 | static List<InlineSpan> generate( | 29 | static List<InlineSpan> generate( |
@@ -311,7 +311,7 @@ class IndentMd extends InlineMd { | @@ -311,7 +311,7 @@ class IndentMd extends InlineMd { | ||
311 | @override | 311 | @override |
312 | RegExp get exp => | 312 | RegExp get exp => |
313 | // RegExp(r"(?<=\n\n)(\ +)(.+?)(?=\n\n)", dotAll: true, multiLine: true); | 313 | // RegExp(r"(?<=\n\n)(\ +)(.+?)(?=\n\n)", dotAll: true, multiLine: true); |
314 | - RegExp(r"(?:\n\n+)^(\ *)(.+?)$(?:\n\n+)", dotAll: true, multiLine: true); | 314 | + RegExp(r"^>([^\n]+)$", dotAll: true, multiLine: true); |
315 | 315 | ||
316 | @override | 316 | @override |
317 | InlineSpan span( | 317 | InlineSpan span( |
@@ -320,8 +320,7 @@ class IndentMd extends InlineMd { | @@ -320,8 +320,7 @@ class IndentMd extends InlineMd { | ||
320 | final GptMarkdownConfig config, | 320 | final GptMarkdownConfig config, |
321 | ) { | 321 | ) { |
322 | var match = exp.firstMatch(text); | 322 | var match = exp.firstMatch(text); |
323 | - int spaces = (match?[1] ?? "").length; | ||
324 | - var data = "${match?[2]}".trim(); | 323 | + var data = "${match?[1]}".trim(); |
325 | // data = data.replaceAll(RegExp(r'\n\ {' '$spaces' '}'), '\n').trim(); | 324 | // data = data.replaceAll(RegExp(r'\n\ {' '$spaces' '}'), '\n').trim(); |
326 | data = data.trim(); | 325 | data = data.trim(); |
327 | var child = TextSpan( | 326 | var child = TextSpan( |
@@ -331,23 +330,21 @@ class IndentMd extends InlineMd { | @@ -331,23 +330,21 @@ class IndentMd extends InlineMd { | ||
331 | config, | 330 | config, |
332 | ), | 331 | ), |
333 | ); | 332 | ); |
334 | - var leftPadding = 20.0; | ||
335 | - if (spaces < 4) { | ||
336 | - leftPadding = 0; | ||
337 | - } | ||
338 | - var padding = config.style?.fontSize ?? 16.0; | ||
339 | return TextSpan( | 333 | return TextSpan( |
340 | children: [ | 334 | children: [ |
341 | WidgetSpan( | 335 | WidgetSpan( |
342 | - child: Padding( | ||
343 | - padding: EdgeInsets.symmetric(vertical: padding), | ||
344 | - child: Row( | ||
345 | - children: [ | ||
346 | - SizedBox( | ||
347 | - width: leftPadding, | 336 | + child: Directionality( |
337 | + textDirection: config.textDirection, | ||
338 | + child: Padding( | ||
339 | + padding: const EdgeInsets.symmetric(vertical: 2), | ||
340 | + child: IndentWidget( | ||
341 | + color: Theme.of(context).colorScheme.onSurfaceVariant, | ||
342 | + direction: config.textDirection, | ||
343 | + child: Padding( | ||
344 | + padding: const EdgeInsetsDirectional.only(start: 10.0), | ||
345 | + child: Expanded(child: config.getRich(child)), | ||
348 | ), | 346 | ), |
349 | - Expanded(child: config.getRich(child)), | ||
350 | - ], | 347 | + ), |
351 | ), | 348 | ), |
352 | ), | 349 | ), |
353 | ), | 350 | ), |
@@ -517,7 +514,8 @@ class StrikeMd extends InlineMd { | @@ -517,7 +514,8 @@ class StrikeMd extends InlineMd { | ||
517 | class ItalicMd extends InlineMd { | 514 | class ItalicMd extends InlineMd { |
518 | @override | 515 | @override |
519 | RegExp get exp => | 516 | RegExp get exp => |
520 | - RegExp(r"(?<!\*)\*(?<!\s)(.+?)(?<!\s)\*(?!\*)", dotAll: true); | 517 | + RegExp(r"(?<!\*)\*(?<!\s)(.+?)(?<!\s)\*(?!\*)|\_(?<!\s)(.+?)(?<!\s)\_", |
518 | + dotAll: true); | ||
521 | 519 | ||
522 | @override | 520 | @override |
523 | InlineSpan span( | 521 | InlineSpan span( |
@@ -526,13 +524,14 @@ class ItalicMd extends InlineMd { | @@ -526,13 +524,14 @@ class ItalicMd extends InlineMd { | ||
526 | final GptMarkdownConfig config, | 524 | final GptMarkdownConfig config, |
527 | ) { | 525 | ) { |
528 | var match = exp.firstMatch(text.trim()); | 526 | var match = exp.firstMatch(text.trim()); |
527 | + var data = match?[1] ?? match?[2]; | ||
529 | var conf = config.copyWith( | 528 | var conf = config.copyWith( |
530 | style: (config.style ?? const TextStyle()) | 529 | style: (config.style ?? const TextStyle()) |
531 | .copyWith(fontStyle: FontStyle.italic)); | 530 | .copyWith(fontStyle: FontStyle.italic)); |
532 | return TextSpan( | 531 | return TextSpan( |
533 | children: MarkdownComponent.generate( | 532 | children: MarkdownComponent.generate( |
534 | context, | 533 | context, |
535 | - "${match?[1]}", | 534 | + "$data", |
536 | conf, | 535 | conf, |
537 | ), | 536 | ), |
538 | style: conf.style, | 537 | style: conf.style, |
@@ -733,10 +732,10 @@ class ATagMd extends InlineMd { | @@ -733,10 +732,10 @@ class ATagMd extends InlineMd { | ||
733 | if (match?[1] == null && match?[2] == null) { | 732 | if (match?[1] == null && match?[2] == null) { |
734 | return const TextSpan(); | 733 | return const TextSpan(); |
735 | } | 734 | } |
736 | - | 735 | + |
737 | final linkText = match?[1] ?? ""; | 736 | final linkText = match?[1] ?? ""; |
738 | final url = match?[2] ?? ""; | 737 | final url = match?[2] ?? ""; |
739 | - | 738 | + |
740 | // Use custom builder if provided | 739 | // Use custom builder if provided |
741 | if (config.linkBuilder != null) { | 740 | if (config.linkBuilder != null) { |
742 | return WidgetSpan( | 741 | return WidgetSpan( |
@@ -924,9 +923,8 @@ class CodeBlockMd extends BlockMd { | @@ -924,9 +923,8 @@ class CodeBlockMd extends BlockMd { | ||
924 | String codes = this.exp.firstMatch(text)?[2] ?? ""; | 923 | String codes = this.exp.firstMatch(text)?[2] ?? ""; |
925 | String name = this.exp.firstMatch(text)?[1] ?? ""; | 924 | String name = this.exp.firstMatch(text)?[1] ?? ""; |
926 | codes = codes.replaceAll(r"```", "").trim(); | 925 | codes = codes.replaceAll(r"```", "").trim(); |
927 | - | ||
928 | - return config.codeBuilder != null | ||
929 | - ? config.codeBuilder!(context, name, codes) | ||
930 | - : CodeField(name: name, codes: codes); | 926 | + |
927 | + return config.codeBuilder?.call(context, name, codes) ?? | ||
928 | + CodeField(name: name, codes: codes); | ||
931 | } | 929 | } |
932 | } | 930 | } |
1 | name: gpt_markdown | 1 | name: gpt_markdown |
2 | description: "Powerful Markdown & LaTeX Renderer for Flutter: Rich Text, Math, Tables, Links, and Text Selection. Ideal for ChatGPT, Gemini, and more." | 2 | description: "Powerful Markdown & LaTeX Renderer for Flutter: Rich Text, Math, Tables, Links, and Text Selection. Ideal for ChatGPT, Gemini, and more." |
3 | -version: 1.0.5 | 3 | +version: 1.0.6 |
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