Merge remote-tracking branch 'github/main'
# Conflicts: # example/lib/main.dart
Showing
10 changed files
with
405 additions
and
91 deletions
@@ -21,15 +21,16 @@ A comprehensive Flutter package for rendering rich Markdown and LaTeX content in | @@ -21,15 +21,16 @@ A comprehensive Flutter package for rendering rich Markdown and LaTeX content in | ||
21 | | ➖ Horizontal Line | ✅ | | | 21 | | ➖ Horizontal Line | ✅ | | |
22 | | 🔢 Latex Math | ✅ | | | 22 | | 🔢 Latex Math | ✅ | | |
23 | | ↩️ Indent | ✅ | | 23 | | ↩️ Indent | ✅ | |
24 | +| ↩️ BlockQuote | ✅ | | ||
24 | | 🖼️ Image | ✅ | | 25 | | 🖼️ Image | ✅ | |
25 | | ✨ Highlighted Text | ✅ | | 26 | | ✨ Highlighted Text | ✅ | |
26 | -| ✂️ Striked Text | ✅ | | 27 | +| ✂️ Strike Text | ✅ | |
27 | | 🔵 Bold Text | ✅ | | 28 | | 🔵 Bold Text | ✅ | |
28 | | 📜 Italic Text | ✅ | | 29 | | 📜 Italic Text | ✅ | |
29 | | 🔗 Links | ✅ | | 30 | | 🔗 Links | ✅ | |
30 | | 📱 Selectable | ✅ | | 31 | | 📱 Selectable | ✅ | |
32 | +| 🧩 Custom components | ✅ | | | ||
31 | | 📎 Underline | | 🔜 | | 33 | | 📎 Underline | | 🔜 | |
32 | -| 🧩 Custom components | | 🔜 | | ||
33 | 34 | ||
34 | ## ✨ Key Features | 35 | ## ✨ Key Features |
35 | 36 |
devtools_options.yaml
0 → 100644
example/devtools_options.yaml
0 → 100644
@@ -76,19 +76,216 @@ class _MyHomePageState extends State<MyHomePage> { | @@ -76,19 +76,216 @@ 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''' | 78 | text: r''' |
79 | +```markdown | ||
80 | +# Complex Markdown Document for Testing | ||
79 | 81 | ||
80 | -一袋大米25千克,已经吃了它的$\frac{2}{5}$,吃了( )千克,还剩( )千克。 | ||
81 | -1. 计算已经吃的大米重量: | ||
82 | - $$ | ||
83 | - 已经吃的大米重量 = 25 \text{千克} \times \frac{2}{5} = 25 \times 0.4 = 1 | ||
84 | -$$ | ||
85 | -2. 计算剩余的大米重量: | ||
86 | - $$ | ||
87 | - 剩余的大米重量 = 25 \text{千克} - 10 \text{千克} = 15 \text{千克} | ||
88 | -$$ | ||
89 | -【答案】 | ||
90 | -吃了10千克,还剩15千克。 | 82 | +This document is designed to **challenge** your `gpt_markdown` package by incorporating a wide variety of Markdown components including headers, lists, tables, code blocks, blockquotes, footnotes, and LaTeX math expressions. |
91 | 83 | ||
84 | +--- | ||
85 | + | ||
86 | +## Table of Contents | ||
87 | +1. [Headers and Emphasis](#headers-and-emphasis) | ||
88 | +2. [Lists](#lists) | ||
89 | +3. [Code Blocks and Inline Code](#code-blocks-and-inline-code) | ||
90 | +4. [Tables](#tables) | ||
91 | +5. [Blockquotes and Nested Elements](#blockquotes-and-nested-elements) | ||
92 | +6. [Mathematical Expressions](#mathematical-expressions) | ||
93 | +7. [Links and Images](#links-and-images) | ||
94 | +8. [Footnotes](#footnotes) | ||
95 | +9. [Horizontal Rules and Miscellaneous](#horizontal-rules-and-miscellaneous) | ||
96 | + | ||
97 | +--- | ||
98 | + | ||
99 | +## Headers and Emphasis | ||
100 | + | ||
101 | +### Header Levels | ||
102 | +Markdown supports multiple header levels: | ||
103 | +- `# Header 1` | ||
104 | +- `## Header 2` | ||
105 | +- `### Header 3` | ||
106 | +- `#### Header 4` | ||
107 | +- `##### Header 5` | ||
108 | +- `###### Header 6` | ||
109 | + | ||
110 | +### Emphasis Examples | ||
111 | +- *Italicized text* using single asterisks or underscores. | ||
112 | +- **Bold text** using double asterisks or underscores. | ||
113 | +- ***Bold and italic*** by combining them. | ||
114 | +- ~~Strikethrough~~ text using two tildes. | ||
115 | + | ||
116 | +--- | ||
117 | + | ||
118 | +## Lists | ||
119 | + | ||
120 | +### Unordered List | ||
121 | +- Item 1 | ||
122 | + - Nested Item 1.1 | ||
123 | + - Nested Item 1.2 | ||
124 | + - Deeply Nested Item 1.2.1 | ||
125 | +- Item 2 | ||
126 | + - [ ] Task not completed | ||
127 | + - [x] Task completed | ||
128 | + | ||
129 | +### Ordered List | ||
130 | +1. First item | ||
131 | +2. Second item with nested list: | ||
132 | + 1. Subitem 2.1 | ||
133 | + 2. Subitem 2.2 | ||
134 | +3. Third item | ||
135 | + | ||
136 | +### Mixed List Example | ||
137 | +- **Fruits** | ||
138 | + 1. Apple | ||
139 | + 2. Banana | ||
140 | + 3. Cherry | ||
141 | +- **Vegetables** | ||
142 | + - Carrot | ||
143 | + - Lettuce | ||
144 | + - Spinach | ||
145 | + | ||
146 | +--- | ||
147 | + | ||
148 | +## Code Blocks and Inline Code | ||
149 | + | ||
150 | +### Inline Code | ||
151 | +Here is an example of inline code: `print("Hello, world!")`. | ||
152 | + | ||
153 | +### Fenced Code Block (Python) | ||
154 | +```python | ||
155 | +def greet(name): | ||
156 | + """ | ||
157 | + Greets a person with the provided name. | ||
158 | + """ | ||
159 | + print(f"Hello, {name}!") | ||
160 | + | ||
161 | +greet("Alice") | ||
162 | +``` | ||
163 | + | ||
164 | +### Fenced Code Block (JavaScript) | ||
165 | +```javascript | ||
166 | +function greet(name) { | ||
167 | + console.log(`Hello, ${name}!`); | ||
168 | +} | ||
169 | +greet("Bob"); | ||
170 | +``` | ||
171 | + | ||
172 | +--- | ||
173 | + | ||
174 | +## Tables | ||
175 | + | ||
176 | +Here is a table demonstrating various elements: | ||
177 | + | ||
178 | +| Syntax | Description | Example | | ||
179 | +| ----------- | ---------------------------------------- | --------------------------------- | | ||
180 | +| Header | Title | **Bold Header** | | ||
181 | +| Paragraph | Text with *italic* and **bold** elements | This is a sample paragraph. | | ||
182 | +| Inline Code | `code snippet` | `let x = 10;` | | ||
183 | + | ||
184 | +Additional table with alignment: | ||
185 | + | ||
186 | +| Left Align | Center Align | Right Align | | ||
187 | +| :--------- |:------------:| ----------:| | ||
188 | +| Row 1 | Row 1 | Row 1 | | ||
189 | +| Row 2 | Row 2 | Row 2 | | ||
190 | + | ||
191 | +--- | ||
192 | + | ||
193 | +## Blockquotes and Nested Elements | ||
194 | + | ||
195 | +> **Blockquote Header** | ||
196 | +> | ||
197 | +> This is a blockquote. You can include **bold** and *italic* text, as well as `inline code` within blockquotes. | ||
198 | +> | ||
199 | +> > ### Nested Blockquote | ||
200 | +> > - Nested list item 1 | ||
201 | +> > - Nested list item 2 | ||
202 | +> > 1. Numbered subitem 1 | ||
203 | +> > 2. Numbered subitem 2 | ||
204 | +> > | ||
205 | +> > ```python | ||
206 | +> > # Code snippet inside nested blockquote | ||
207 | +> > for i in range(3): | ||
208 | +> > print(i) | ||
209 | +> > ``` | ||
210 | +> | ||
211 | +> Back to the outer blockquote. | ||
212 | + | ||
213 | +--- | ||
214 | + | ||
215 | +## Mathematical Expressions | ||
216 | + | ||
217 | +### Inline Math | ||
218 | +You can write inline math using the `\( ... \)` syntax. For example, the quadratic formula is given by: | ||
219 | +\( x = \frac{-b \pm \sqrt{b^2-4ac}}{2a} \). | ||
220 | + | ||
221 | +### Display Math | ||
222 | +Display math can be rendered using the `\[ ... \]` syntax. For example, consider the integral: | ||
223 | +\[ | ||
224 | +\int_{-\infty}^{\infty} e^{-x^2} \, dx = \sqrt{\pi} | ||
225 | +\] | ||
226 | + | ||
227 | +More complex display equations: | ||
228 | +\[ | ||
229 | +E = mc^2 \quad \text{and} \quad F = ma | ||
230 | +\] | ||
231 | + | ||
232 | +--- | ||
233 | + | ||
234 | +## Links and Images | ||
235 | + | ||
236 | +### Links | ||
237 | +Here are examples of links: | ||
238 | +- [OpenAI](https://www.openai.com) | ||
239 | +- [GitHub](https://github.com) | ||
240 | + | ||
241 | +### Images | ||
242 | +Inline images can be embedded as follows: | ||
243 | + | ||
244 | + | ||
245 | +Images can also be referenced with links: | ||
246 | +[](https://via.placeholder.com/500 "Full Image") | ||
247 | + | ||
248 | +--- | ||
249 | + | ||
250 | +## Footnotes | ||
251 | + | ||
252 | +Here is a statement with a footnote.[^1] Another reference can be added here.[^long] | ||
253 | + | ||
254 | +[^1]: This is a simple footnote. | ||
255 | +[^long]: This footnote contains a longer explanation to showcase how multiple lines can be formatted in a footnote. It supports Markdown formatting such as **bold** and *italic* text. | ||
256 | + | ||
257 | +--- | ||
258 | + | ||
259 | +## Horizontal Rules and Miscellaneous | ||
260 | + | ||
261 | +Horizontal rules can be used to separate sections: | ||
262 | + | ||
263 | +--- | ||
264 | + | ||
265 | +### Task List Example | ||
266 | +- [x] Write complex Markdown document | ||
267 | +- [x] Include LaTeX math expressions | ||
268 | +- [ ] Add more Markdown components if needed | ||
269 | + | ||
270 | +### Nested Quotes with Code and Math | ||
271 | +> **Example of Nested Components** | ||
272 | +> | ||
273 | +> - Inline code: `sum = a + b` | ||
274 | +> - Math expression: \( \sum_{i=1}^n i = \frac{n(n+1)}{2} \) | ||
275 | +> - More text with **bold** formatting. | ||
276 | +> | ||
277 | +> ```javascript | ||
278 | +> // JavaScript code example inside a nested blockquote | ||
279 | +> const sum = (n) => (n * (n + 1)) / 2; | ||
280 | +> console.log(sum(10)); | ||
281 | +> ``` | ||
282 | + | ||
283 | +--- | ||
284 | + | ||
285 | +## Conclusion | ||
286 | + | ||
287 | +This document was created to test the robustness of Markdown parsers and to ensure that all components, including advanced LaTeX expressions and nested structures, are rendered correctly. Enjoy testing and feel free to extend it further! | ||
288 | +``` | ||
92 | ''', | 289 | ''', |
93 | ); | 290 | ); |
94 | File? file; | 291 | File? file; |
@@ -143,7 +340,8 @@ $$ | @@ -143,7 +340,8 @@ $$ | ||
143 | ? Theme.of(context).colorScheme.onSurfaceVariant | 340 | ? Theme.of(context).colorScheme.onSurfaceVariant |
144 | : Theme.of(context) | 341 | : Theme.of(context) |
145 | .colorScheme | 342 | .colorScheme |
146 | - .onSurface, | 343 | + .onSurface |
344 | + .withValues(alpha: 0.38), | ||
147 | ), | 345 | ), |
148 | ), | 346 | ), |
149 | IconButton( | 347 | IconButton( |
@@ -241,7 +439,8 @@ $$ | @@ -241,7 +439,8 @@ $$ | ||
241 | border: Border.all( | 439 | border: Border.all( |
242 | color: Theme.of(context) | 440 | color: Theme.of(context) |
243 | .colorScheme | 441 | .colorScheme |
244 | - .secondary, | 442 | + .secondary |
443 | + .withValues(alpha: 0.5), | ||
245 | width: 1, | 444 | width: 1, |
246 | ), | 445 | ), |
247 | ), | 446 | ), |
@@ -387,6 +586,40 @@ $$ | @@ -387,6 +586,40 @@ $$ | ||
387 | ), | 586 | ), |
388 | ); | 587 | ); |
389 | }, | 588 | }, |
589 | + components: [ | ||
590 | + CodeBlockMd(), | ||
591 | + NewLines(), | ||
592 | + BlockQuote(), | ||
593 | + ImageMd(), | ||
594 | + ATagMd(), | ||
595 | + TableMd(), | ||
596 | + HTag(), | ||
597 | + UnOrderedList(), | ||
598 | + OrderedList(), | ||
599 | + RadioButtonMd(), | ||
600 | + CheckBoxMd(), | ||
601 | + HrLine(), | ||
602 | + StrikeMd(), | ||
603 | + BoldMd(), | ||
604 | + ItalicMd(), | ||
605 | + LatexMath(), | ||
606 | + LatexMathMultiLine(), | ||
607 | + HighlightedText(), | ||
608 | + SourceTag(), | ||
609 | + IndentMd(), | ||
610 | + ], | ||
611 | + inlineComponents: [ | ||
612 | + ImageMd(), | ||
613 | + ATagMd(), | ||
614 | + TableMd(), | ||
615 | + StrikeMd(), | ||
616 | + BoldMd(), | ||
617 | + ItalicMd(), | ||
618 | + LatexMath(), | ||
619 | + LatexMathMultiLine(), | ||
620 | + HighlightedText(), | ||
621 | + SourceTag(), | ||
622 | + ], | ||
390 | // codeBuilder: (context, name, code, closed) { | 623 | // codeBuilder: (context, name, code, closed) { |
391 | // return Padding( | 624 | // return Padding( |
392 | // padding: const EdgeInsets.symmetric( | 625 | // padding: const EdgeInsets.symmetric( |
@@ -12,6 +12,7 @@ class BlockQuoteWidget extends StatelessWidget { | @@ -12,6 +12,7 @@ class BlockQuoteWidget extends StatelessWidget { | ||
12 | required this.child, | 12 | required this.child, |
13 | required this.direction, | 13 | required this.direction, |
14 | required this.color, | 14 | required this.color, |
15 | + this.width = 3, | ||
15 | }); | 16 | }); |
16 | 17 | ||
17 | /// The child widget to be indented. | 18 | /// The child widget to be indented. |
@@ -23,29 +24,39 @@ class BlockQuoteWidget extends StatelessWidget { | @@ -23,29 +24,39 @@ class BlockQuoteWidget extends StatelessWidget { | ||
23 | /// The color of the indent. | 24 | /// The color of the indent. |
24 | final Color color; | 25 | final Color color; |
25 | 26 | ||
27 | + /// The width of the indent. | ||
28 | + final double width; | ||
29 | + | ||
26 | @override | 30 | @override |
27 | Widget build(BuildContext context) { | 31 | Widget build(BuildContext context) { |
28 | - return CustomPaint( | ||
29 | - foregroundPainter: IndentPainter(color, direction), | ||
30 | - child: child, | 32 | + return Row( |
33 | + children: [ | ||
34 | + Flexible( | ||
35 | + child: CustomPaint( | ||
36 | + foregroundPainter: BlockQuotePainter(color, direction, width), | ||
37 | + child: child, | ||
38 | + ), | ||
39 | + ), | ||
40 | + ], | ||
31 | ); | 41 | ); |
32 | } | 42 | } |
33 | } | 43 | } |
34 | 44 | ||
35 | /// A custom painter that draws an indent on a canvas. | 45 | /// A custom painter that draws an indent on a canvas. |
36 | /// | 46 | /// |
37 | -/// The [IndentPainter] class extends CustomPainter and is responsible for | 47 | +/// The [BlockQuotePainter] class extends CustomPainter and is responsible for |
38 | /// painting the indent on a canvas. It takes a [color] and [direction] parameter | 48 | /// painting the indent on a canvas. It takes a [color] and [direction] parameter |
39 | /// and uses them to draw an indent in the UI. | 49 | /// and uses them to draw an indent in the UI. |
40 | -class IndentPainter extends CustomPainter { | ||
41 | - IndentPainter(this.color, this.direction); | 50 | +class BlockQuotePainter extends CustomPainter { |
51 | + BlockQuotePainter(this.color, this.direction, this.width); | ||
42 | final Color color; | 52 | final Color color; |
43 | final TextDirection direction; | 53 | final TextDirection direction; |
54 | + final double width; | ||
44 | @override | 55 | @override |
45 | void paint(Canvas canvas, Size size) { | 56 | void paint(Canvas canvas, Size size) { |
46 | var left = direction == TextDirection.ltr; | 57 | var left = direction == TextDirection.ltr; |
47 | - var start = left ? 0.0 : size.width - 4; | ||
48 | - var rect = Rect.fromLTWH(start, 0, 4, size.height); | 58 | + var start = left ? 0.0 : size.width - width; |
59 | + var rect = Rect.fromLTWH(start, 0, width, size.height); | ||
49 | var paint = Paint()..color = color; | 60 | var paint = Paint()..color = color; |
50 | canvas.drawRect(rect, paint); | 61 | canvas.drawRect(rect, paint); |
51 | } | 62 | } |
1 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; |
2 | +import 'package:gpt_markdown/gpt_markdown.dart'; | ||
2 | 3 | ||
3 | /// A builder function for the ordered list. | 4 | /// A builder function for the ordered list. |
4 | typedef OrderedListBuilder = | 5 | typedef OrderedListBuilder = |
@@ -80,6 +81,8 @@ class GptMarkdownConfig { | @@ -80,6 +81,8 @@ class GptMarkdownConfig { | ||
80 | this.imageBuilder, | 81 | this.imageBuilder, |
81 | this.maxLines, | 82 | this.maxLines, |
82 | this.overflow, | 83 | this.overflow, |
84 | + this.components, | ||
85 | + this.inlineComponents, | ||
83 | }); | 86 | }); |
84 | 87 | ||
85 | /// The direction of the text. | 88 | /// The direction of the text. |
@@ -133,6 +136,12 @@ class GptMarkdownConfig { | @@ -133,6 +136,12 @@ class GptMarkdownConfig { | ||
133 | /// The image builder. | 136 | /// The image builder. |
134 | final ImageBuilder? imageBuilder; | 137 | final ImageBuilder? imageBuilder; |
135 | 138 | ||
139 | + /// The list of components. | ||
140 | + final List<MarkdownComponent>? components; | ||
141 | + | ||
142 | + /// The list of inline components. | ||
143 | + final List<MarkdownComponent>? inlineComponents; | ||
144 | + | ||
136 | /// A copy of the configuration with the specified parameters. | 145 | /// A copy of the configuration with the specified parameters. |
137 | GptMarkdownConfig copyWith({ | 146 | GptMarkdownConfig copyWith({ |
138 | TextStyle? style, | 147 | TextStyle? style, |
@@ -152,6 +161,8 @@ class GptMarkdownConfig { | @@ -152,6 +161,8 @@ class GptMarkdownConfig { | ||
152 | final ImageBuilder? imageBuilder, | 161 | final ImageBuilder? imageBuilder, |
153 | final OrderedListBuilder? orderedListBuilder, | 162 | final OrderedListBuilder? orderedListBuilder, |
154 | final UnOrderedListBuilder? unOrderedListBuilder, | 163 | final UnOrderedListBuilder? unOrderedListBuilder, |
164 | + final List<MarkdownComponent>? components, | ||
165 | + final List<MarkdownComponent>? inlineComponents, | ||
155 | }) { | 166 | }) { |
156 | return GptMarkdownConfig( | 167 | return GptMarkdownConfig( |
157 | style: style ?? this.style, | 168 | style: style ?? this.style, |
@@ -171,6 +182,8 @@ class GptMarkdownConfig { | @@ -171,6 +182,8 @@ class GptMarkdownConfig { | ||
171 | imageBuilder: imageBuilder ?? this.imageBuilder, | 182 | imageBuilder: imageBuilder ?? this.imageBuilder, |
172 | orderedListBuilder: orderedListBuilder ?? this.orderedListBuilder, | 183 | orderedListBuilder: orderedListBuilder ?? this.orderedListBuilder, |
173 | unOrderedListBuilder: unOrderedListBuilder ?? this.unOrderedListBuilder, | 184 | unOrderedListBuilder: unOrderedListBuilder ?? this.unOrderedListBuilder, |
185 | + components: components ?? this.components, | ||
186 | + inlineComponents: inlineComponents ?? this.inlineComponents, | ||
174 | ); | 187 | ); |
175 | } | 188 | } |
176 | 189 |
@@ -40,6 +40,8 @@ class GptMarkdown extends StatelessWidget { | @@ -40,6 +40,8 @@ class GptMarkdown extends StatelessWidget { | ||
40 | this.overflow, | 40 | this.overflow, |
41 | this.orderedListBuilder, | 41 | this.orderedListBuilder, |
42 | this.unOrderedListBuilder, | 42 | this.unOrderedListBuilder, |
43 | + this.components, | ||
44 | + this.inlineComponents, | ||
43 | }); | 45 | }); |
44 | 46 | ||
45 | /// The direction of the text. | 47 | /// The direction of the text. |
@@ -94,16 +96,60 @@ class GptMarkdown extends StatelessWidget { | @@ -94,16 +96,60 @@ class GptMarkdown extends StatelessWidget { | ||
94 | /// The unordered list builder. | 96 | /// The unordered list builder. |
95 | final UnOrderedListBuilder? unOrderedListBuilder; | 97 | final UnOrderedListBuilder? unOrderedListBuilder; |
96 | 98 | ||
99 | + /// The list of components. | ||
100 | + /// ```dart | ||
101 | + /// List<MarkdownComponent> components = [ | ||
102 | + /// CodeBlockMd(), | ||
103 | + /// NewLines(), | ||
104 | + /// BlockQuote(), | ||
105 | + /// ImageMd(), | ||
106 | + /// ATagMd(), | ||
107 | + /// TableMd(), | ||
108 | + /// HTag(), | ||
109 | + /// UnOrderedList(), | ||
110 | + /// OrderedList(), | ||
111 | + /// RadioButtonMd(), | ||
112 | + /// CheckBoxMd(), | ||
113 | + /// HrLine(), | ||
114 | + /// StrikeMd(), | ||
115 | + /// BoldMd(), | ||
116 | + /// ItalicMd(), | ||
117 | + /// LatexMath(), | ||
118 | + /// LatexMathMultiLine(), | ||
119 | + /// HighlightedText(), | ||
120 | + /// SourceTag(), | ||
121 | + /// IndentMd(), | ||
122 | + /// ]; | ||
123 | + /// ``` | ||
124 | + final List<MarkdownComponent>? components; | ||
125 | + | ||
126 | + /// The list of inline components. | ||
127 | + /// ```dart | ||
128 | + /// List<MarkdownComponent> inlineComponents = [ | ||
129 | + /// ImageMd(), | ||
130 | + /// ATagMd(), | ||
131 | + /// TableMd(), | ||
132 | + /// StrikeMd(), | ||
133 | + /// BoldMd(), | ||
134 | + /// ItalicMd(), | ||
135 | + /// LatexMath(), | ||
136 | + /// LatexMathMultiLine(), | ||
137 | + /// HighlightedText(), | ||
138 | + /// SourceTag(), | ||
139 | + /// ]; | ||
140 | + /// ``` | ||
141 | + final List<MarkdownComponent>? inlineComponents; | ||
142 | + | ||
97 | /// A method to remove extra lines inside block LaTeX. | 143 | /// A method to remove extra lines inside block LaTeX. |
98 | - String _removeExtraLinesInsideBlockLatex(String text) { | ||
99 | - return text.replaceAllMapped( | ||
100 | - RegExp(r"\\\[(.*?)\\\]", multiLine: true, dotAll: true), | ||
101 | - (match) { | ||
102 | - String content = match[0] ?? ""; | ||
103 | - return content.replaceAllMapped(RegExp(r"\n[\n\ ]+"), (match) => "\n"); | ||
104 | - }, | ||
105 | - ); | ||
106 | - } | 144 | + // String _removeExtraLinesInsideBlockLatex(String text) { |
145 | + // return text.replaceAllMapped( | ||
146 | + // RegExp(r"\\\[(.*?)\\\]", multiLine: true, dotAll: true), | ||
147 | + // (match) { | ||
148 | + // String content = match[0] ?? ""; | ||
149 | + // return content.replaceAllMapped(RegExp(r"\n[\n\ ]+"), (match) => "\n"); | ||
150 | + // }, | ||
151 | + // ); | ||
152 | + // } | ||
107 | 153 | ||
108 | @override | 154 | @override |
109 | Widget build(BuildContext context) { | 155 | Widget build(BuildContext context) { |
@@ -130,7 +176,7 @@ class GptMarkdown extends StatelessWidget { | @@ -130,7 +176,7 @@ class GptMarkdown extends StatelessWidget { | ||
130 | }, | 176 | }, |
131 | ); | 177 | ); |
132 | } | 178 | } |
133 | - tex = _removeExtraLinesInsideBlockLatex(tex); | 179 | + // tex = _removeExtraLinesInsideBlockLatex(tex); |
134 | return ClipRRect( | 180 | return ClipRRect( |
135 | child: MdWidget( | 181 | child: MdWidget( |
136 | tex, | 182 | tex, |
@@ -153,6 +199,8 @@ class GptMarkdown extends StatelessWidget { | @@ -153,6 +199,8 @@ class GptMarkdown extends StatelessWidget { | ||
153 | imageBuilder: imageBuilder, | 199 | imageBuilder: imageBuilder, |
154 | orderedListBuilder: orderedListBuilder, | 200 | orderedListBuilder: orderedListBuilder, |
155 | unOrderedListBuilder: unOrderedListBuilder, | 201 | unOrderedListBuilder: unOrderedListBuilder, |
202 | + components: components, | ||
203 | + inlineComponents: inlineComponents, | ||
156 | ), | 204 | ), |
157 | ), | 205 | ), |
158 | ); | 206 | ); |
@@ -47,8 +47,8 @@ abstract class MarkdownComponent { | @@ -47,8 +47,8 @@ abstract class MarkdownComponent { | ||
47 | ) { | 47 | ) { |
48 | var components = | 48 | var components = |
49 | includeGlobalComponents | 49 | includeGlobalComponents |
50 | - ? MarkdownComponent.components | ||
51 | - : MarkdownComponent.inlineComponents; | 50 | + ? config.components ?? MarkdownComponent.components |
51 | + : config.inlineComponents ?? MarkdownComponent.inlineComponents; | ||
52 | List<InlineSpan> spans = []; | 52 | List<InlineSpan> spans = []; |
53 | Iterable<String> regexes = components.map<String>((e) => e.exp.pattern); | 53 | Iterable<String> regexes = components.map<String>((e) => e.exp.pattern); |
54 | final combinedRegex = RegExp( | 54 | final combinedRegex = RegExp( |
@@ -68,29 +68,7 @@ abstract class MarkdownComponent { | @@ -68,29 +68,7 @@ abstract class MarkdownComponent { | ||
68 | dotAll: each.exp.isDotAll, | 68 | dotAll: each.exp.isDotAll, |
69 | ); | 69 | ); |
70 | if (exp.hasMatch(element)) { | 70 | if (exp.hasMatch(element)) { |
71 | - if (each.inline) { | ||
72 | - spans.add(each.span(context, element, config)); | ||
73 | - } else { | ||
74 | - spans.addAll([ | ||
75 | - TextSpan( | ||
76 | - text: "\n ", | ||
77 | - style: TextStyle( | ||
78 | - fontSize: 0, | ||
79 | - height: 0, | ||
80 | - color: config.style?.color, | ||
81 | - ), | ||
82 | - ), | ||
83 | - each.span(context, element, config), | ||
84 | - TextSpan( | ||
85 | - text: "\n ", | ||
86 | - style: TextStyle( | ||
87 | - fontSize: 0, | ||
88 | - height: 0, | ||
89 | - color: config.style?.color, | ||
90 | - ), | ||
91 | - ), | ||
92 | - ]); | ||
93 | - } | 71 | + spans.add(each.span(context, element, config)); |
94 | return ""; | 72 | return ""; |
95 | } | 73 | } |
96 | } | 74 | } |
@@ -156,7 +134,12 @@ abstract class BlockMd extends MarkdownComponent { | @@ -156,7 +134,12 @@ abstract class BlockMd extends MarkdownComponent { | ||
156 | child: child, | 134 | child: child, |
157 | ); | 135 | ); |
158 | } | 136 | } |
159 | - return WidgetSpan(child: child, alignment: PlaceholderAlignment.middle); | 137 | + child = Row(children: [Flexible(child: child)]); |
138 | + return WidgetSpan( | ||
139 | + child: child, | ||
140 | + alignment: PlaceholderAlignment.baseline, | ||
141 | + baseline: TextBaseline.alphabetic, | ||
142 | + ); | ||
160 | } | 143 | } |
161 | 144 | ||
162 | Widget build( | 145 | Widget build( |
@@ -182,7 +165,7 @@ class IndentMd extends BlockMd { | @@ -182,7 +165,7 @@ class IndentMd extends BlockMd { | ||
182 | textDirection: config.textDirection, | 165 | textDirection: config.textDirection, |
183 | child: Row( | 166 | child: Row( |
184 | children: [ | 167 | children: [ |
185 | - Expanded( | 168 | + Flexible( |
186 | child: config.getRich( | 169 | child: config.getRich( |
187 | TextSpan( | 170 | TextSpan( |
188 | children: MarkdownComponent.generate( | 171 | children: MarkdownComponent.generate( |
@@ -293,7 +276,7 @@ class HrLine extends BlockMd { | @@ -293,7 +276,7 @@ class HrLine extends BlockMd { | ||
293 | /// Checkbox component | 276 | /// Checkbox component |
294 | class CheckBoxMd extends BlockMd { | 277 | class CheckBoxMd extends BlockMd { |
295 | @override | 278 | @override |
296 | - String get expString => (r"\[(\x?)\]\ (\S[^\n]*?)$"); | 279 | + String get expString => (r"\[((?:\x|\ ))\]\ (\S[^\n]*?)$"); |
297 | get onLinkTab => null; | 280 | get onLinkTab => null; |
298 | 281 | ||
299 | @override | 282 | @override |
@@ -314,7 +297,7 @@ class CheckBoxMd extends BlockMd { | @@ -314,7 +297,7 @@ class CheckBoxMd extends BlockMd { | ||
314 | /// Radio Button component | 297 | /// Radio Button component |
315 | class RadioButtonMd extends BlockMd { | 298 | class RadioButtonMd extends BlockMd { |
316 | @override | 299 | @override |
317 | - String get expString => (r"\((\x?)\)\ (\S[^\n]*)$"); | 300 | + String get expString => (r"\(((?:\x|\ ))\)\ (\S[^\n]*)$"); |
318 | get onLinkTab => null; | 301 | get onLinkTab => null; |
319 | 302 | ||
320 | @override | 303 | @override |
@@ -339,7 +322,11 @@ class BlockQuote extends InlineMd { | @@ -339,7 +322,11 @@ class BlockQuote extends InlineMd { | ||
339 | @override | 322 | @override |
340 | RegExp get exp => | 323 | RegExp get exp => |
341 | // RegExp(r"(?<=\n\n)(\ +)(.+?)(?=\n\n)", dotAll: true, multiLine: true); | 324 | // RegExp(r"(?<=\n\n)(\ +)(.+?)(?=\n\n)", dotAll: true, multiLine: true); |
342 | - RegExp(r"^>([^\n]+)$", dotAll: true, multiLine: true); | 325 | + RegExp( |
326 | + r"(?:(?:^)\ *>[^\n]+)(?:(?:\n)\ *>[^\n]+)*", | ||
327 | + dotAll: true, | ||
328 | + multiLine: true, | ||
329 | + ); | ||
343 | 330 | ||
344 | @override | 331 | @override |
345 | InlineSpan span( | 332 | InlineSpan span( |
@@ -348,9 +335,20 @@ class BlockQuote extends InlineMd { | @@ -348,9 +335,20 @@ class BlockQuote extends InlineMd { | ||
348 | final GptMarkdownConfig config, | 335 | final GptMarkdownConfig config, |
349 | ) { | 336 | ) { |
350 | var match = exp.firstMatch(text); | 337 | var match = exp.firstMatch(text); |
351 | - var data = "${match?[1]}".trim(); | ||
352 | - // data = data.replaceAll(RegExp(r'\n\ {' '$spaces' '}'), '\n').trim(); | ||
353 | - data = data.trim(); | 338 | + var dataBuilder = StringBuffer(); |
339 | + var m = match?[0] ?? ''; | ||
340 | + for (var each in m.split('\n')) { | ||
341 | + if (each.startsWith(RegExp(r'\ *>'))) { | ||
342 | + var subString = each.trimLeft().substring(1); | ||
343 | + if (subString.startsWith(' ')) { | ||
344 | + subString = subString.substring(1); | ||
345 | + } | ||
346 | + dataBuilder.writeln(subString); | ||
347 | + } else { | ||
348 | + dataBuilder.writeln(each); | ||
349 | + } | ||
350 | + } | ||
351 | + var data = dataBuilder.toString().trim(); | ||
354 | var child = TextSpan( | 352 | var child = TextSpan( |
355 | children: MarkdownComponent.generate(context, data, config, true), | 353 | children: MarkdownComponent.generate(context, data, config, true), |
356 | ); | 354 | ); |
@@ -364,8 +362,9 @@ class BlockQuote extends InlineMd { | @@ -364,8 +362,9 @@ class BlockQuote extends InlineMd { | ||
364 | child: BlockQuoteWidget( | 362 | child: BlockQuoteWidget( |
365 | color: Theme.of(context).colorScheme.onSurfaceVariant, | 363 | color: Theme.of(context).colorScheme.onSurfaceVariant, |
366 | direction: config.textDirection, | 364 | direction: config.textDirection, |
365 | + width: 3, | ||
367 | child: Padding( | 366 | child: Padding( |
368 | - padding: const EdgeInsetsDirectional.only(start: 10.0), | 367 | + padding: const EdgeInsetsDirectional.only(start: 8.0), |
369 | child: config.getRich(child), | 368 | child: config.getRich(child), |
370 | ), | 369 | ), |
371 | ), | 370 | ), |
@@ -390,7 +389,7 @@ class UnOrderedList extends BlockMd { | @@ -390,7 +389,7 @@ class UnOrderedList extends BlockMd { | ||
390 | ) { | 389 | ) { |
391 | var match = this.exp.firstMatch(text); | 390 | var match = this.exp.firstMatch(text); |
392 | 391 | ||
393 | - var child = MdWidget("${match?[1]?.trim()}", false, config: config); | 392 | + var child = MdWidget("${match?[1]?.trim()}", true, config: config); |
394 | 393 | ||
395 | return config.unOrderedListBuilder?.call( | 394 | return config.unOrderedListBuilder?.call( |
396 | context, | 395 | context, |
@@ -428,7 +427,7 @@ class OrderedList extends BlockMd { | @@ -428,7 +427,7 @@ class OrderedList extends BlockMd { | ||
428 | 427 | ||
429 | var no = "${match?[1]}"; | 428 | var no = "${match?[1]}"; |
430 | 429 | ||
431 | - var child = MdWidget("${match?[2]?.trim()}", false, config: config); | 430 | + var child = MdWidget("${match?[2]?.trim()}", true, config: config); |
432 | return config.orderedListBuilder?.call( | 431 | return config.orderedListBuilder?.call( |
433 | context, | 432 | context, |
434 | no, | 433 | no, |
@@ -582,7 +581,8 @@ class ItalicMd extends InlineMd { | @@ -582,7 +581,8 @@ class ItalicMd extends InlineMd { | ||
582 | 581 | ||
583 | class LatexMathMultiLine extends BlockMd { | 582 | class LatexMathMultiLine extends BlockMd { |
584 | @override | 583 | @override |
585 | - String get expString => (r"\\\[(((?!\n\n).)*?)\\\]|(\\begin.*?\\end{.*?})"); | 584 | + String get expString => (r"\ *\\\[((?:.)*?)\\\]|(\ *\\begin.*?\\end{.*?})"); |
585 | + // (r"\ *\\\[((?:(?!\n\n\n).)*?)\\\]|(\\begin.*?\\end{.*?})"); | ||
586 | @override | 586 | @override |
587 | RegExp get exp => RegExp(expString, dotAll: true, multiLine: true); | 587 | RegExp get exp => RegExp(expString, dotAll: true, multiLine: true); |
588 | 588 | ||
@@ -593,7 +593,7 @@ class LatexMathMultiLine extends BlockMd { | @@ -593,7 +593,7 @@ class LatexMathMultiLine extends BlockMd { | ||
593 | final GptMarkdownConfig config, | 593 | final GptMarkdownConfig config, |
594 | ) { | 594 | ) { |
595 | var p0 = exp.firstMatch(text.trim()); | 595 | var p0 = exp.firstMatch(text.trim()); |
596 | - String mathText = p0?[1] ?? p0?[2] ?? ""; | 596 | + String mathText = p0?[1] ?? p0?[2] ?? ''; |
597 | var workaround = config.latexWorkaround ?? (String tex) => tex; | 597 | var workaround = config.latexWorkaround ?? (String tex) => tex; |
598 | 598 | ||
599 | var builder = | 599 | var builder = |
@@ -889,7 +889,7 @@ class ImageMd extends InlineMd { | @@ -889,7 +889,7 @@ class ImageMd extends InlineMd { | ||
889 | class TableMd extends BlockMd { | 889 | class TableMd extends BlockMd { |
890 | @override | 890 | @override |
891 | String get expString => | 891 | String get expString => |
892 | - (r"(((\|[^\n\|]+\|)((([^\n\|]+\|)+)?))(\n(((\|[^\n\|]+\|)(([^\n\|]+\|)+)?)))+)$"); | 892 | + (r"(((\|[^\n\|]+\|)((([^\n\|]+\|)+)?)\ *)(\n\ *(((\|[^\n\|]+\|)(([^\n\|]+\|)+)?))\ *)+)$"); |
893 | @override | 893 | @override |
894 | Widget build( | 894 | Widget build( |
895 | BuildContext context, | 895 | BuildContext context, |
@@ -902,6 +902,7 @@ class TableMd extends BlockMd { | @@ -902,6 +902,7 @@ class TableMd extends BlockMd { | ||
902 | .map<Map<int, String>>( | 902 | .map<Map<int, String>>( |
903 | (e) => | 903 | (e) => |
904 | e | 904 | e |
905 | + .trim() | ||
905 | .split('|') | 906 | .split('|') |
906 | .where((element) => element.isNotEmpty) | 907 | .where((element) => element.isNotEmpty) |
907 | .toList() | 908 | .toList() |
@@ -955,7 +956,7 @@ class TableMd extends BlockMd { | @@ -955,7 +956,7 @@ class TableMd extends BlockMd { | ||
955 | children: List.generate(maxCol, (index) { | 956 | children: List.generate(maxCol, (index) { |
956 | var e = entry.value; | 957 | var e = entry.value; |
957 | String data = e[index] ?? ""; | 958 | String data = e[index] ?? ""; |
958 | - if (RegExp(r"^--+$").hasMatch(data.trim()) || | 959 | + if (RegExp(r"^:?--+:?$").hasMatch(data.trim()) || |
959 | data.trim().isEmpty) { | 960 | data.trim().isEmpty) { |
960 | return const SizedBox(); | 961 | return const SizedBox(); |
961 | } | 962 | } |
@@ -20,24 +20,21 @@ class MdWidget extends StatelessWidget { | @@ -20,24 +20,21 @@ class MdWidget extends StatelessWidget { | ||
20 | 20 | ||
21 | @override | 21 | @override |
22 | Widget build(BuildContext context) { | 22 | Widget build(BuildContext context) { |
23 | - List<InlineSpan> list = []; | ||
24 | - list.addAll( | ||
25 | - MarkdownComponent.generate( | ||
26 | - context, | ||
27 | - exp, | ||
28 | - // .replaceAllMapped( | ||
29 | - // RegExp( | ||
30 | - // r"\\\[(.*?)\\\]|(\\begin.*?\\end{.*?})", | ||
31 | - // multiLine: true, | ||
32 | - // dotAll: true, | ||
33 | - // ), (match) { | ||
34 | - // // | ||
35 | - // String body = (match[1] ?? match[2])?.replaceAll("\n", " ") ?? ""; | ||
36 | - // return "\\[$body\\]"; | ||
37 | - // }), | ||
38 | - config, | ||
39 | - includeGlobalComponents, | ||
40 | - ), | 23 | + List<InlineSpan> list = MarkdownComponent.generate( |
24 | + context, | ||
25 | + exp, | ||
26 | + // .replaceAllMapped( | ||
27 | + // RegExp( | ||
28 | + // r"\\\[(.*?)\\\]|(\\begin.*?\\end{.*?})", | ||
29 | + // multiLine: true, | ||
30 | + // dotAll: true, | ||
31 | + // ), (match) { | ||
32 | + // // | ||
33 | + // String body = (match[1] ?? match[2])?.replaceAll("\n", " ") ?? ""; | ||
34 | + // return "\\[$body\\]"; | ||
35 | + // }), | ||
36 | + config, | ||
37 | + includeGlobalComponents, | ||
41 | ); | 38 | ); |
42 | return config.getRich( | 39 | return config.getRich( |
43 | TextSpan(children: list, style: config.style?.copyWith()), | 40 | TextSpan(children: list, style: config.style?.copyWith()), |
-
Please register or login to post a comment