Showing
15 changed files
with
846 additions
and
371 deletions
| 1 | +## 1.0.0 | ||
| 2 | + | ||
| 3 | +* `TexMarkdown` is renamed to `GptMarkdown`. | ||
| 4 | +* `h1` to `h6` style added to `GptMarkdownThemeData` class. | ||
| 5 | +* `hrLineThickness` value added to `GptMarkdownThemeData` class. | ||
| 6 | +* `hrLineColor` Color added to `GptMarkdownThemeData` class. | ||
| 7 | +* `linkColor` Color added to `GptMarkdownThemeData` class. | ||
| 8 | +* `linkHoverColor` Color added to `GptMarkdownThemeData` class. | ||
| 9 | +* Indentation improved. | ||
| 10 | +* Math equations are now default selectable. | ||
| 11 | +* `SelectableAdapter` Widget added to make any widget selectable. | ||
| 12 | + | ||
| 13 | +## 0.1.15 | ||
| 14 | + | ||
| 15 | +* `CodeBlock` is moved out of `gpt_markdown.dart` library. | ||
| 16 | + | ||
| 1 | ## 0.1.14 | 17 | ## 0.1.14 |
| 2 | 18 | ||
| 3 | -* Cnaged `withOpacity` to `withAlpha` in `theme.dart` for highlightColor. | 19 | +* Changed `withOpacity` to `withAlpha` in `theme.dart` for highlightColor. |
| 4 | 20 | ||
| 5 | ## 0.1.13 | 21 | ## 0.1.13 |
| 6 | 22 |
| @@ -81,7 +81,7 @@ Check the documentation [here.](https://github.com/saminsohag/flutter_packages/t | @@ -81,7 +81,7 @@ Check the documentation [here.](https://github.com/saminsohag/flutter_packages/t | ||
| 81 | import 'package:flutter/material.dart'; | 81 | import 'package:flutter/material.dart'; |
| 82 | import 'package:gpt_markdown/gpt_markdown.dart'; | 82 | import 'package:gpt_markdown/gpt_markdown.dart'; |
| 83 | 83 | ||
| 84 | -return TexMarkdown( | 84 | +return GptMarkdown( |
| 85 | ''' | 85 | ''' |
| 86 | * This is a unordered list. | 86 | * This is a unordered list. |
| 87 | ''', | 87 | ''', |
| @@ -48,7 +48,7 @@ class _MyHomePageState extends State<MyHomePage> { | @@ -48,7 +48,7 @@ class _MyHomePageState extends State<MyHomePage> { | ||
| 48 | AnimatedBuilder( | 48 | AnimatedBuilder( |
| 49 | animation: _controller, | 49 | animation: _controller, |
| 50 | builder: (context, _) { | 50 | builder: (context, _) { |
| 51 | - return TexMarkdown( | 51 | + return GptMarkdown( |
| 52 | _controller.text, | 52 | _controller.text, |
| 53 | style: const TextStyle( | 53 | style: const TextStyle( |
| 54 | color: Colors.red, | 54 | color: Colors.red, |
| @@ -72,4 +72,4 @@ class _MyHomePageState extends State<MyHomePage> { | @@ -72,4 +72,4 @@ class _MyHomePageState extends State<MyHomePage> { | ||
| 72 | } | 72 | } |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | -``` | ||
| 75 | +``` |
| @@ -30,16 +30,24 @@ class _MyAppState extends State<MyApp> { | @@ -30,16 +30,24 @@ class _MyAppState extends State<MyApp> { | ||
| 30 | useMaterial3: true, | 30 | useMaterial3: true, |
| 31 | brightness: Brightness.light, | 31 | brightness: Brightness.light, |
| 32 | colorSchemeSeed: Colors.blue, | 32 | colorSchemeSeed: Colors.blue, |
| 33 | + extensions: [ | ||
| 34 | + GptMarkdownThemeData( | ||
| 35 | + brightness: Brightness.light, | ||
| 36 | + highlightColor: Colors.red, | ||
| 37 | + ), | ||
| 38 | + ], | ||
| 33 | ), | 39 | ), |
| 34 | darkTheme: ThemeData( | 40 | darkTheme: ThemeData( |
| 35 | - useMaterial3: true, | ||
| 36 | - brightness: Brightness.dark, | ||
| 37 | - colorSchemeSeed: Colors.blue, | ||
| 38 | - extensions: [ | ||
| 39 | - GptMarkdownThemeData( | ||
| 40 | - highlightColor: Colors.red, | ||
| 41 | - ), | ||
| 42 | - ]), | 41 | + useMaterial3: true, |
| 42 | + brightness: Brightness.dark, | ||
| 43 | + colorSchemeSeed: Colors.blue, | ||
| 44 | + extensions: [ | ||
| 45 | + GptMarkdownThemeData( | ||
| 46 | + brightness: Brightness.dark, | ||
| 47 | + highlightColor: Colors.red, | ||
| 48 | + ), | ||
| 49 | + ], | ||
| 50 | + ), | ||
| 43 | home: MyHomePage( | 51 | home: MyHomePage( |
| 44 | title: 'GptMarkdown', | 52 | title: 'GptMarkdown', |
| 45 | onPressed: () { | 53 | onPressed: () { |
| @@ -170,7 +178,10 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex | @@ -170,7 +178,10 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex | ||
| 170 | Icons.select_all_outlined, | 178 | Icons.select_all_outlined, |
| 171 | color: selectable | 179 | color: selectable |
| 172 | ? Theme.of(context).colorScheme.onSurfaceVariant | 180 | ? Theme.of(context).colorScheme.onSurfaceVariant |
| 173 | - : Theme.of(context).colorScheme.onSurface.withOpacity(0.38), | 181 | + : Theme.of(context) |
| 182 | + .colorScheme | ||
| 183 | + .onSurface | ||
| 184 | + .withValues(alpha: 0.38), | ||
| 174 | ), | 185 | ), |
| 175 | ), | 186 | ), |
| 176 | IconButton( | 187 | IconButton( |
| @@ -243,7 +254,7 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex | @@ -243,7 +254,7 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex | ||
| 243 | // ), | 254 | // ), |
| 244 | child: Builder( | 255 | child: Builder( |
| 245 | builder: (context) { | 256 | builder: (context) { |
| 246 | - Widget child = TexMarkdown( | 257 | + Widget child = GptMarkdown( |
| 247 | _controller.text, | 258 | _controller.text, |
| 248 | textDirection: _direction, | 259 | textDirection: _direction, |
| 249 | onLinkTab: (url, title) { | 260 | onLinkTab: (url, title) { |
| @@ -301,7 +312,7 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex | @@ -301,7 +312,7 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex | ||
| 301 | ..insert(1, "|---|"); | 312 | ..insert(1, "|---|"); |
| 302 | tableString = | 313 | tableString = |
| 303 | tableStringList.join("\n"); | 314 | tableStringList.join("\n"); |
| 304 | - return TexMarkdown(tableString); | 315 | + return GptMarkdown(tableString); |
| 305 | } | 316 | } |
| 306 | var controller = ScrollController(); | 317 | var controller = ScrollController(); |
| 307 | Widget child = Math.tex( | 318 | Widget child = Math.tex( |
| @@ -6,4 +6,8 @@ class AppDelegate: FlutterAppDelegate { | @@ -6,4 +6,8 @@ class AppDelegate: FlutterAppDelegate { | ||
| 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { | 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { |
| 7 | return true | 7 | return true |
| 8 | } | 8 | } |
| 9 | + | ||
| 10 | + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { | ||
| 11 | + return true | ||
| 12 | + } | ||
| 9 | } | 13 | } |
| @@ -5,10 +5,10 @@ packages: | @@ -5,10 +5,10 @@ packages: | ||
| 5 | dependency: transitive | 5 | dependency: transitive |
| 6 | description: | 6 | description: |
| 7 | name: args | 7 | name: args |
| 8 | - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" | 8 | + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 |
| 9 | url: "https://pub.dev" | 9 | url: "https://pub.dev" |
| 10 | source: hosted | 10 | source: hosted |
| 11 | - version: "2.5.0" | 11 | + version: "2.6.0" |
| 12 | async: | 12 | async: |
| 13 | dependency: transitive | 13 | dependency: transitive |
| 14 | description: | 14 | description: |
| @@ -45,10 +45,10 @@ packages: | @@ -45,10 +45,10 @@ packages: | ||
| 45 | dependency: transitive | 45 | dependency: transitive |
| 46 | description: | 46 | description: |
| 47 | name: collection | 47 | name: collection |
| 48 | - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a | 48 | + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf |
| 49 | url: "https://pub.dev" | 49 | url: "https://pub.dev" |
| 50 | source: hosted | 50 | source: hosted |
| 51 | - version: "1.18.0" | 51 | + version: "1.19.0" |
| 52 | cross_file: | 52 | cross_file: |
| 53 | dependency: transitive | 53 | dependency: transitive |
| 54 | description: | 54 | description: |
| @@ -98,18 +98,18 @@ packages: | @@ -98,18 +98,18 @@ packages: | ||
| 98 | dependency: "direct main" | 98 | dependency: "direct main" |
| 99 | description: | 99 | description: |
| 100 | name: flutter_math_fork | 100 | name: flutter_math_fork |
| 101 | - sha256: "94bee4642892a94939af0748c6a7de0ff8318feee588379dcdfea7dc5cba06c8" | 101 | + sha256: "284bab89b2fbf1bc3a0baf13d011c1dd324d004e35d177626b77f2fc056366ac" |
| 102 | url: "https://pub.dev" | 102 | url: "https://pub.dev" |
| 103 | source: hosted | 103 | source: hosted |
| 104 | - version: "0.7.2" | 104 | + version: "0.7.3" |
| 105 | flutter_svg: | 105 | flutter_svg: |
| 106 | dependency: transitive | 106 | dependency: transitive |
| 107 | description: | 107 | description: |
| 108 | name: flutter_svg | 108 | name: flutter_svg |
| 109 | - sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" | 109 | + sha256: "54900a1a1243f3c4a5506d853a2b5c2dbc38d5f27e52a52618a8054401431123" |
| 110 | url: "https://pub.dev" | 110 | url: "https://pub.dev" |
| 111 | source: hosted | 111 | source: hosted |
| 112 | - version: "2.0.10+1" | 112 | + version: "2.0.16" |
| 113 | flutter_test: | 113 | flutter_test: |
| 114 | dependency: "direct dev" | 114 | dependency: "direct dev" |
| 115 | description: flutter | 115 | description: flutter |
| @@ -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: "0.1.14" | 129 | + version: "1.0.0" |
| 130 | http: | 130 | http: |
| 131 | dependency: transitive | 131 | dependency: transitive |
| 132 | description: | 132 | description: |
| @@ -139,26 +139,26 @@ packages: | @@ -139,26 +139,26 @@ packages: | ||
| 139 | dependency: transitive | 139 | dependency: transitive |
| 140 | description: | 140 | description: |
| 141 | name: http_parser | 141 | name: http_parser |
| 142 | - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" | 142 | + sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360" |
| 143 | url: "https://pub.dev" | 143 | url: "https://pub.dev" |
| 144 | source: hosted | 144 | source: hosted |
| 145 | - version: "4.0.2" | 145 | + version: "4.1.1" |
| 146 | leak_tracker: | 146 | leak_tracker: |
| 147 | dependency: transitive | 147 | dependency: transitive |
| 148 | description: | 148 | description: |
| 149 | name: leak_tracker | 149 | name: leak_tracker |
| 150 | - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" | 150 | + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" |
| 151 | url: "https://pub.dev" | 151 | url: "https://pub.dev" |
| 152 | source: hosted | 152 | source: hosted |
| 153 | - version: "10.0.5" | 153 | + version: "10.0.7" |
| 154 | leak_tracker_flutter_testing: | 154 | leak_tracker_flutter_testing: |
| 155 | dependency: transitive | 155 | dependency: transitive |
| 156 | description: | 156 | description: |
| 157 | name: leak_tracker_flutter_testing | 157 | name: leak_tracker_flutter_testing |
| 158 | - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" | 158 | + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" |
| 159 | url: "https://pub.dev" | 159 | url: "https://pub.dev" |
| 160 | source: hosted | 160 | source: hosted |
| 161 | - version: "3.0.5" | 161 | + version: "3.0.8" |
| 162 | leak_tracker_testing: | 162 | leak_tracker_testing: |
| 163 | dependency: transitive | 163 | dependency: transitive |
| 164 | description: | 164 | description: |
| @@ -219,10 +219,10 @@ packages: | @@ -219,10 +219,10 @@ packages: | ||
| 219 | dependency: transitive | 219 | dependency: transitive |
| 220 | description: | 220 | description: |
| 221 | name: path_parsing | 221 | name: path_parsing |
| 222 | - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf | 222 | + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" |
| 223 | url: "https://pub.dev" | 223 | url: "https://pub.dev" |
| 224 | source: hosted | 224 | source: hosted |
| 225 | - version: "1.0.1" | 225 | + version: "1.1.0" |
| 226 | petitparser: | 226 | petitparser: |
| 227 | dependency: transitive | 227 | dependency: transitive |
| 228 | description: | 228 | description: |
| @@ -243,7 +243,7 @@ packages: | @@ -243,7 +243,7 @@ packages: | ||
| 243 | dependency: transitive | 243 | dependency: transitive |
| 244 | description: flutter | 244 | description: flutter |
| 245 | source: sdk | 245 | source: sdk |
| 246 | - version: "0.0.99" | 246 | + version: "0.0.0" |
| 247 | source_span: | 247 | source_span: |
| 248 | dependency: transitive | 248 | dependency: transitive |
| 249 | description: | 249 | description: |
| @@ -256,10 +256,10 @@ packages: | @@ -256,10 +256,10 @@ packages: | ||
| 256 | dependency: transitive | 256 | dependency: transitive |
| 257 | description: | 257 | description: |
| 258 | name: stack_trace | 258 | name: stack_trace |
| 259 | - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" | 259 | + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" |
| 260 | url: "https://pub.dev" | 260 | url: "https://pub.dev" |
| 261 | source: hosted | 261 | source: hosted |
| 262 | - version: "1.11.1" | 262 | + version: "1.12.0" |
| 263 | stream_channel: | 263 | stream_channel: |
| 264 | dependency: transitive | 264 | dependency: transitive |
| 265 | description: | 265 | description: |
| @@ -272,10 +272,10 @@ packages: | @@ -272,10 +272,10 @@ packages: | ||
| 272 | dependency: transitive | 272 | dependency: transitive |
| 273 | description: | 273 | description: |
| 274 | name: string_scanner | 274 | name: string_scanner |
| 275 | - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" | 275 | + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" |
| 276 | url: "https://pub.dev" | 276 | url: "https://pub.dev" |
| 277 | source: hosted | 277 | source: hosted |
| 278 | - version: "1.2.0" | 278 | + version: "1.3.0" |
| 279 | term_glyph: | 279 | term_glyph: |
| 280 | dependency: transitive | 280 | dependency: transitive |
| 281 | description: | 281 | description: |
| @@ -288,10 +288,10 @@ packages: | @@ -288,10 +288,10 @@ packages: | ||
| 288 | dependency: transitive | 288 | dependency: transitive |
| 289 | description: | 289 | description: |
| 290 | name: test_api | 290 | name: test_api |
| 291 | - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" | 291 | + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" |
| 292 | url: "https://pub.dev" | 292 | url: "https://pub.dev" |
| 293 | source: hosted | 293 | source: hosted |
| 294 | - version: "0.7.2" | 294 | + version: "0.7.3" |
| 295 | tuple: | 295 | tuple: |
| 296 | dependency: transitive | 296 | dependency: transitive |
| 297 | description: | 297 | description: |
| @@ -304,34 +304,34 @@ packages: | @@ -304,34 +304,34 @@ packages: | ||
| 304 | dependency: transitive | 304 | dependency: transitive |
| 305 | description: | 305 | description: |
| 306 | name: typed_data | 306 | name: typed_data |
| 307 | - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c | 307 | + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 |
| 308 | url: "https://pub.dev" | 308 | url: "https://pub.dev" |
| 309 | source: hosted | 309 | source: hosted |
| 310 | - version: "1.3.2" | 310 | + version: "1.4.0" |
| 311 | vector_graphics: | 311 | vector_graphics: |
| 312 | dependency: transitive | 312 | dependency: transitive |
| 313 | description: | 313 | description: |
| 314 | name: vector_graphics | 314 | name: vector_graphics |
| 315 | - sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" | 315 | + sha256: "27d5fefe86fb9aace4a9f8375b56b3c292b64d8c04510df230f849850d912cb7" |
| 316 | url: "https://pub.dev" | 316 | url: "https://pub.dev" |
| 317 | source: hosted | 317 | source: hosted |
| 318 | - version: "1.1.11+1" | 318 | + version: "1.1.15" |
| 319 | vector_graphics_codec: | 319 | vector_graphics_codec: |
| 320 | dependency: transitive | 320 | dependency: transitive |
| 321 | description: | 321 | description: |
| 322 | name: vector_graphics_codec | 322 | name: vector_graphics_codec |
| 323 | - sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da | 323 | + sha256: "2430b973a4ca3c4dbc9999b62b8c719a160100dcbae5c819bae0cacce32c9cdb" |
| 324 | url: "https://pub.dev" | 324 | url: "https://pub.dev" |
| 325 | source: hosted | 325 | source: hosted |
| 326 | - version: "1.1.11+1" | 326 | + version: "1.1.12" |
| 327 | vector_graphics_compiler: | 327 | vector_graphics_compiler: |
| 328 | dependency: transitive | 328 | dependency: transitive |
| 329 | description: | 329 | description: |
| 330 | name: vector_graphics_compiler | 330 | name: vector_graphics_compiler |
| 331 | - sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" | 331 | + sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad" |
| 332 | url: "https://pub.dev" | 332 | url: "https://pub.dev" |
| 333 | source: hosted | 333 | source: hosted |
| 334 | - version: "1.1.11+1" | 334 | + version: "1.1.16" |
| 335 | vector_math: | 335 | vector_math: |
| 336 | dependency: transitive | 336 | dependency: transitive |
| 337 | description: | 337 | description: |
| @@ -344,26 +344,26 @@ packages: | @@ -344,26 +344,26 @@ packages: | ||
| 344 | dependency: transitive | 344 | dependency: transitive |
| 345 | description: | 345 | description: |
| 346 | name: vm_service | 346 | name: vm_service |
| 347 | - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" | 347 | + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b |
| 348 | url: "https://pub.dev" | 348 | url: "https://pub.dev" |
| 349 | source: hosted | 349 | source: hosted |
| 350 | - version: "14.2.5" | 350 | + version: "14.3.0" |
| 351 | watcher: | 351 | watcher: |
| 352 | dependency: "direct main" | 352 | dependency: "direct main" |
| 353 | description: | 353 | description: |
| 354 | name: watcher | 354 | name: watcher |
| 355 | - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" | 355 | + sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" |
| 356 | url: "https://pub.dev" | 356 | url: "https://pub.dev" |
| 357 | source: hosted | 357 | source: hosted |
| 358 | - version: "1.1.0" | 358 | + version: "1.1.1" |
| 359 | web: | 359 | web: |
| 360 | dependency: transitive | 360 | dependency: transitive |
| 361 | description: | 361 | description: |
| 362 | name: web | 362 | name: web |
| 363 | - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" | 363 | + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb |
| 364 | url: "https://pub.dev" | 364 | url: "https://pub.dev" |
| 365 | source: hosted | 365 | source: hosted |
| 366 | - version: "0.5.1" | 366 | + version: "1.1.0" |
| 367 | xml: | 367 | xml: |
| 368 | dependency: transitive | 368 | dependency: transitive |
| 369 | description: | 369 | description: |
| @@ -374,4 +374,4 @@ packages: | @@ -374,4 +374,4 @@ packages: | ||
| 374 | version: "6.5.0" | 374 | version: "6.5.0" |
| 375 | sdks: | 375 | sdks: |
| 376 | dart: ">=3.5.0 <4.0.0" | 376 | dart: ">=3.5.0 <4.0.0" |
| 377 | - flutter: ">=3.18.0-18.0.pre.54" | 377 | + flutter: ">=3.22.0" |
lib/custom_widgets/code_field.dart
0 → 100644
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | +import 'package:flutter/services.dart'; | ||
| 3 | + | ||
| 4 | +class CodeField extends StatefulWidget { | ||
| 5 | + const CodeField({super.key, required this.name, required this.codes}); | ||
| 6 | + final String name; | ||
| 7 | + final String codes; | ||
| 8 | + | ||
| 9 | + @override | ||
| 10 | + State<CodeField> createState() => _CodeFieldState(); | ||
| 11 | +} | ||
| 12 | + | ||
| 13 | +class _CodeFieldState extends State<CodeField> { | ||
| 14 | + bool _copied = false; | ||
| 15 | + @override | ||
| 16 | + Widget build(BuildContext context) { | ||
| 17 | + return Material( | ||
| 18 | + color: Theme.of(context).colorScheme.onInverseSurface, | ||
| 19 | + shape: RoundedRectangleBorder( | ||
| 20 | + borderRadius: BorderRadius.circular(8), | ||
| 21 | + ), | ||
| 22 | + child: Column( | ||
| 23 | + crossAxisAlignment: CrossAxisAlignment.stretch, | ||
| 24 | + children: [ | ||
| 25 | + Row( | ||
| 26 | + children: [ | ||
| 27 | + Padding( | ||
| 28 | + padding: | ||
| 29 | + const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), | ||
| 30 | + child: Text(widget.name), | ||
| 31 | + ), | ||
| 32 | + const Spacer(), | ||
| 33 | + TextButton.icon( | ||
| 34 | + style: TextButton.styleFrom( | ||
| 35 | + foregroundColor: Theme.of(context).colorScheme.onSurface, | ||
| 36 | + textStyle: const TextStyle( | ||
| 37 | + fontWeight: FontWeight.normal, | ||
| 38 | + ), | ||
| 39 | + ), | ||
| 40 | + onPressed: () async { | ||
| 41 | + await Clipboard.setData(ClipboardData(text: widget.codes)) | ||
| 42 | + .then((value) { | ||
| 43 | + setState(() { | ||
| 44 | + _copied = true; | ||
| 45 | + }); | ||
| 46 | + }); | ||
| 47 | + await Future.delayed(const Duration(seconds: 2)); | ||
| 48 | + setState(() { | ||
| 49 | + _copied = false; | ||
| 50 | + }); | ||
| 51 | + }, | ||
| 52 | + icon: Icon( | ||
| 53 | + (_copied) ? Icons.done : Icons.content_paste, | ||
| 54 | + size: 15, | ||
| 55 | + ), | ||
| 56 | + label: Text((_copied) ? "Copied!" : "Copy code"), | ||
| 57 | + ), | ||
| 58 | + ], | ||
| 59 | + ), | ||
| 60 | + const Divider( | ||
| 61 | + height: 1, | ||
| 62 | + ), | ||
| 63 | + SingleChildScrollView( | ||
| 64 | + scrollDirection: Axis.horizontal, | ||
| 65 | + padding: const EdgeInsets.all(16), | ||
| 66 | + child: Text( | ||
| 67 | + widget.codes, | ||
| 68 | + ), | ||
| 69 | + ), | ||
| 70 | + ], | ||
| 71 | + ), | ||
| 72 | + ); | ||
| 73 | + } | ||
| 74 | +} |
| @@ -8,11 +8,15 @@ class LinkButton extends StatefulWidget { | @@ -8,11 +8,15 @@ class LinkButton extends StatefulWidget { | ||
| 8 | final TextStyle? textStyle; | 8 | final TextStyle? textStyle; |
| 9 | final String? url; | 9 | final String? url; |
| 10 | final GptMarkdownConfig config; | 10 | final GptMarkdownConfig config; |
| 11 | + final Color color; | ||
| 12 | + final Color hoverColor; | ||
| 11 | 13 | ||
| 12 | const LinkButton( | 14 | const LinkButton( |
| 13 | {super.key, | 15 | {super.key, |
| 14 | required this.text, | 16 | required this.text, |
| 15 | required this.config, | 17 | required this.config, |
| 18 | + required this.color, | ||
| 19 | + required this.hoverColor, | ||
| 16 | this.onPressed, | 20 | this.onPressed, |
| 17 | this.textStyle, | 21 | this.textStyle, |
| 18 | this.url}); | 22 | this.url}); |
| @@ -27,9 +31,9 @@ class _LinkButtonState extends State<LinkButton> { | @@ -27,9 +31,9 @@ class _LinkButtonState extends State<LinkButton> { | ||
| 27 | @override | 31 | @override |
| 28 | Widget build(BuildContext context) { | 32 | Widget build(BuildContext context) { |
| 29 | var style = (widget.config.style ?? const TextStyle()).copyWith( | 33 | var style = (widget.config.style ?? const TextStyle()).copyWith( |
| 30 | - color: _isHovering ? Colors.red : Colors.blue, | 34 | + color: _isHovering ? widget.hoverColor : widget.color, |
| 31 | decoration: TextDecoration.underline, | 35 | decoration: TextDecoration.underline, |
| 32 | - decorationColor: _isHovering ? Colors.red : Colors.blue, | 36 | + decorationColor: _isHovering ? widget.hoverColor : widget.color, |
| 33 | ); | 37 | ); |
| 34 | return MouseRegion( | 38 | return MouseRegion( |
| 35 | cursor: SystemMouseCursors.click, | 39 | cursor: SystemMouseCursors.click, |
lib/custom_widgets/selectable_adapter.dart
0 → 100644
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | +import 'package:flutter/rendering.dart'; | ||
| 3 | + | ||
| 4 | +class SelectableAdapter extends StatelessWidget { | ||
| 5 | + const SelectableAdapter( | ||
| 6 | + {super.key, required this.selectedText, required this.child}); | ||
| 7 | + | ||
| 8 | + final Widget child; | ||
| 9 | + final String selectedText; | ||
| 10 | + | ||
| 11 | + @override | ||
| 12 | + Widget build(BuildContext context) { | ||
| 13 | + final SelectionRegistrar? registrar = SelectionContainer.maybeOf(context); | ||
| 14 | + if (registrar == null) { | ||
| 15 | + return child; | ||
| 16 | + } | ||
| 17 | + return MouseRegion( | ||
| 18 | + cursor: SystemMouseCursors.text, | ||
| 19 | + child: _SelectableAdapter( | ||
| 20 | + registrar: registrar, | ||
| 21 | + selectedText: selectedText, | ||
| 22 | + child: child, | ||
| 23 | + ), | ||
| 24 | + ); | ||
| 25 | + } | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +class _SelectableAdapter extends SingleChildRenderObjectWidget { | ||
| 29 | + const _SelectableAdapter({ | ||
| 30 | + required this.registrar, | ||
| 31 | + required Widget child, | ||
| 32 | + required this.selectedText, | ||
| 33 | + }) : super(child: child); | ||
| 34 | + | ||
| 35 | + final SelectionRegistrar registrar; | ||
| 36 | + final String selectedText; | ||
| 37 | + | ||
| 38 | + @override | ||
| 39 | + _RenderSelectableAdapter createRenderObject(BuildContext context) { | ||
| 40 | + return _RenderSelectableAdapter( | ||
| 41 | + DefaultSelectionStyle.of(context).selectionColor!, | ||
| 42 | + selectedText, | ||
| 43 | + registrar, | ||
| 44 | + ); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + @override | ||
| 48 | + void updateRenderObject( | ||
| 49 | + BuildContext context, _RenderSelectableAdapter renderObject) { | ||
| 50 | + renderObject | ||
| 51 | + ..selectionColor = DefaultSelectionStyle.of(context).selectionColor! | ||
| 52 | + ..registrar = registrar; | ||
| 53 | + } | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +class _RenderSelectableAdapter extends RenderProxyBox | ||
| 57 | + with Selectable, SelectionRegistrant { | ||
| 58 | + String selectionText; | ||
| 59 | + _RenderSelectableAdapter( | ||
| 60 | + Color selectionColor, | ||
| 61 | + this.selectionText, | ||
| 62 | + SelectionRegistrar registrar, | ||
| 63 | + ) : _selectionColor = selectionColor, | ||
| 64 | + _geometry = ValueNotifier<SelectionGeometry>(_noSelection) { | ||
| 65 | + this.registrar = registrar; | ||
| 66 | + _geometry.addListener(markNeedsPaint); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + static const SelectionGeometry _noSelection = | ||
| 70 | + SelectionGeometry(status: SelectionStatus.none, hasContent: true); | ||
| 71 | + final ValueNotifier<SelectionGeometry> _geometry; | ||
| 72 | + | ||
| 73 | + Color get selectionColor => _selectionColor; | ||
| 74 | + late Color _selectionColor; | ||
| 75 | + set selectionColor(Color value) { | ||
| 76 | + if (_selectionColor == value) { | ||
| 77 | + return; | ||
| 78 | + } | ||
| 79 | + _selectionColor = value; | ||
| 80 | + markNeedsPaint(); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + // ValueListenable APIs | ||
| 84 | + | ||
| 85 | + @override | ||
| 86 | + void addListener(VoidCallback listener) => _geometry.addListener(listener); | ||
| 87 | + | ||
| 88 | + @override | ||
| 89 | + void removeListener(VoidCallback listener) => | ||
| 90 | + _geometry.removeListener(listener); | ||
| 91 | + | ||
| 92 | + @override | ||
| 93 | + SelectionGeometry get value => _geometry.value; | ||
| 94 | + | ||
| 95 | + // Selectable APIs. | ||
| 96 | + | ||
| 97 | + @override | ||
| 98 | + List<Rect> get boundingBoxes => <Rect>[paintBounds]; | ||
| 99 | + | ||
| 100 | + // Adjust this value to enlarge or shrink the selection highlight. | ||
| 101 | + static const double _padding = 0.0; | ||
| 102 | + Rect _getSelectionHighlightRect() { | ||
| 103 | + return Rect.fromLTWH(0 - _padding, 0 - _padding, size.width + _padding * 2, | ||
| 104 | + size.height + _padding * 2); | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + Offset? _start; | ||
| 108 | + Offset? _end; | ||
| 109 | + void _updateGeometry() { | ||
| 110 | + if (_start == null || _end == null) { | ||
| 111 | + _geometry.value = _noSelection; | ||
| 112 | + return; | ||
| 113 | + } | ||
| 114 | + final Rect renderObjectRect = Rect.fromLTWH(0, 0, size.width, size.height); | ||
| 115 | + final Rect selectionRect = Rect.fromPoints(_start!, _end!); | ||
| 116 | + if (renderObjectRect.intersect(selectionRect).isEmpty) { | ||
| 117 | + _geometry.value = _noSelection; | ||
| 118 | + } else { | ||
| 119 | + final Rect selectionRect = _getSelectionHighlightRect(); | ||
| 120 | + final SelectionPoint firstSelectionPoint = SelectionPoint( | ||
| 121 | + localPosition: selectionRect.bottomLeft, | ||
| 122 | + lineHeight: selectionRect.size.height, | ||
| 123 | + handleType: TextSelectionHandleType.left, | ||
| 124 | + ); | ||
| 125 | + final SelectionPoint secondSelectionPoint = SelectionPoint( | ||
| 126 | + localPosition: selectionRect.bottomRight, | ||
| 127 | + lineHeight: selectionRect.size.height, | ||
| 128 | + handleType: TextSelectionHandleType.right, | ||
| 129 | + ); | ||
| 130 | + final bool isReversed; | ||
| 131 | + if (_start!.dy > _end!.dy) { | ||
| 132 | + isReversed = true; | ||
| 133 | + } else if (_start!.dy < _end!.dy) { | ||
| 134 | + isReversed = false; | ||
| 135 | + } else { | ||
| 136 | + isReversed = _start!.dx > _end!.dx; | ||
| 137 | + } | ||
| 138 | + _geometry.value = SelectionGeometry( | ||
| 139 | + status: SelectionStatus.uncollapsed, | ||
| 140 | + hasContent: true, | ||
| 141 | + startSelectionPoint: | ||
| 142 | + isReversed ? secondSelectionPoint : firstSelectionPoint, | ||
| 143 | + endSelectionPoint: | ||
| 144 | + isReversed ? firstSelectionPoint : secondSelectionPoint, | ||
| 145 | + selectionRects: <Rect>[selectionRect], | ||
| 146 | + ); | ||
| 147 | + } | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + @override | ||
| 151 | + SelectionResult dispatchSelectionEvent(SelectionEvent event) { | ||
| 152 | + SelectionResult result = SelectionResult.none; | ||
| 153 | + switch (event.type) { | ||
| 154 | + case SelectionEventType.startEdgeUpdate: | ||
| 155 | + case SelectionEventType.endEdgeUpdate: | ||
| 156 | + final Rect renderObjectRect = | ||
| 157 | + Rect.fromLTWH(0, 0, size.width, size.height); | ||
| 158 | + // Normalize offset in case it is out side of the rect. | ||
| 159 | + final Offset point = | ||
| 160 | + globalToLocal((event as SelectionEdgeUpdateEvent).globalPosition); | ||
| 161 | + final Offset adjustedPoint = | ||
| 162 | + SelectionUtils.adjustDragOffset(renderObjectRect, point); | ||
| 163 | + if (event.type == SelectionEventType.startEdgeUpdate) { | ||
| 164 | + _start = adjustedPoint; | ||
| 165 | + } else { | ||
| 166 | + _end = adjustedPoint; | ||
| 167 | + } | ||
| 168 | + result = SelectionUtils.getResultBasedOnRect(renderObjectRect, point); | ||
| 169 | + case SelectionEventType.clear: | ||
| 170 | + _start = _end = null; | ||
| 171 | + case SelectionEventType.selectAll: | ||
| 172 | + case SelectionEventType.selectWord: | ||
| 173 | + case SelectionEventType.selectParagraph: | ||
| 174 | + _start = Offset.zero; | ||
| 175 | + _end = Offset.infinite; | ||
| 176 | + case SelectionEventType.granularlyExtendSelection: | ||
| 177 | + result = SelectionResult.end; | ||
| 178 | + final GranularlyExtendSelectionEvent extendSelectionEvent = | ||
| 179 | + event as GranularlyExtendSelectionEvent; | ||
| 180 | + // Initialize the offset it there is no ongoing selection. | ||
| 181 | + if (_start == null || _end == null) { | ||
| 182 | + if (extendSelectionEvent.forward) { | ||
| 183 | + _start = _end = Offset.zero; | ||
| 184 | + } else { | ||
| 185 | + _start = _end = Offset.infinite; | ||
| 186 | + } | ||
| 187 | + } | ||
| 188 | + // Move the corresponding selection edge. | ||
| 189 | + final Offset newOffset = | ||
| 190 | + extendSelectionEvent.forward ? Offset.infinite : Offset.zero; | ||
| 191 | + if (extendSelectionEvent.isEnd) { | ||
| 192 | + if (newOffset == _end) { | ||
| 193 | + result = extendSelectionEvent.forward | ||
| 194 | + ? SelectionResult.next | ||
| 195 | + : SelectionResult.previous; | ||
| 196 | + } | ||
| 197 | + _end = newOffset; | ||
| 198 | + } else { | ||
| 199 | + if (newOffset == _start) { | ||
| 200 | + result = extendSelectionEvent.forward | ||
| 201 | + ? SelectionResult.next | ||
| 202 | + : SelectionResult.previous; | ||
| 203 | + } | ||
| 204 | + _start = newOffset; | ||
| 205 | + } | ||
| 206 | + case SelectionEventType.directionallyExtendSelection: | ||
| 207 | + result = SelectionResult.end; | ||
| 208 | + final DirectionallyExtendSelectionEvent extendSelectionEvent = | ||
| 209 | + event as DirectionallyExtendSelectionEvent; | ||
| 210 | + // Convert to local coordinates. | ||
| 211 | + final double horizontalBaseLine = globalToLocal(Offset(event.dx, 0)).dx; | ||
| 212 | + final Offset newOffset; | ||
| 213 | + final bool forward; | ||
| 214 | + switch (extendSelectionEvent.direction) { | ||
| 215 | + case SelectionExtendDirection.backward: | ||
| 216 | + case SelectionExtendDirection.previousLine: | ||
| 217 | + forward = false; | ||
| 218 | + // Initialize the offset it there is no ongoing selection. | ||
| 219 | + if (_start == null || _end == null) { | ||
| 220 | + _start = _end = Offset.infinite; | ||
| 221 | + } | ||
| 222 | + // Move the corresponding selection edge. | ||
| 223 | + if (extendSelectionEvent.direction == | ||
| 224 | + SelectionExtendDirection.previousLine || | ||
| 225 | + horizontalBaseLine < 0) { | ||
| 226 | + newOffset = Offset.zero; | ||
| 227 | + } else { | ||
| 228 | + newOffset = Offset.infinite; | ||
| 229 | + } | ||
| 230 | + case SelectionExtendDirection.nextLine: | ||
| 231 | + case SelectionExtendDirection.forward: | ||
| 232 | + forward = true; | ||
| 233 | + // Initialize the offset it there is no ongoing selection. | ||
| 234 | + if (_start == null || _end == null) { | ||
| 235 | + _start = _end = Offset.zero; | ||
| 236 | + } | ||
| 237 | + // Move the corresponding selection edge. | ||
| 238 | + if (extendSelectionEvent.direction == | ||
| 239 | + SelectionExtendDirection.nextLine || | ||
| 240 | + horizontalBaseLine > size.width) { | ||
| 241 | + newOffset = Offset.infinite; | ||
| 242 | + } else { | ||
| 243 | + newOffset = Offset.zero; | ||
| 244 | + } | ||
| 245 | + } | ||
| 246 | + if (extendSelectionEvent.isEnd) { | ||
| 247 | + if (newOffset == _end) { | ||
| 248 | + result = forward ? SelectionResult.next : SelectionResult.previous; | ||
| 249 | + } | ||
| 250 | + _end = newOffset; | ||
| 251 | + } else { | ||
| 252 | + if (newOffset == _start) { | ||
| 253 | + result = forward ? SelectionResult.next : SelectionResult.previous; | ||
| 254 | + } | ||
| 255 | + _start = newOffset; | ||
| 256 | + } | ||
| 257 | + } | ||
| 258 | + _updateGeometry(); | ||
| 259 | + return result; | ||
| 260 | + } | ||
| 261 | + | ||
| 262 | + // This method is called when users want to copy selected content in this | ||
| 263 | + // widget into clipboard. | ||
| 264 | + @override | ||
| 265 | + SelectedContent? getSelectedContent() { | ||
| 266 | + return value.hasSelection | ||
| 267 | + ? SelectedContent(plainText: selectionText) | ||
| 268 | + : null; | ||
| 269 | + } | ||
| 270 | + | ||
| 271 | + LayerLink? _startHandle; | ||
| 272 | + LayerLink? _endHandle; | ||
| 273 | + | ||
| 274 | + @override | ||
| 275 | + void pushHandleLayers(LayerLink? startHandle, LayerLink? endHandle) { | ||
| 276 | + if (_startHandle == startHandle && _endHandle == endHandle) { | ||
| 277 | + return; | ||
| 278 | + } | ||
| 279 | + _startHandle = startHandle; | ||
| 280 | + _endHandle = endHandle; | ||
| 281 | + markNeedsPaint(); | ||
| 282 | + } | ||
| 283 | + | ||
| 284 | + @override | ||
| 285 | + void paint(PaintingContext context, Offset offset) { | ||
| 286 | + super.paint(context, offset); | ||
| 287 | + if (!_geometry.value.hasSelection) { | ||
| 288 | + return; | ||
| 289 | + } | ||
| 290 | + // Draw the selection highlight. | ||
| 291 | + final Paint selectionPaint = Paint() | ||
| 292 | + ..style = PaintingStyle.fill | ||
| 293 | + ..color = _selectionColor; | ||
| 294 | + context.canvas | ||
| 295 | + .drawRect(_getSelectionHighlightRect().shift(offset), selectionPaint); | ||
| 296 | + | ||
| 297 | + // Push the layer links if any. | ||
| 298 | + if (_startHandle != null) { | ||
| 299 | + context.pushLayer( | ||
| 300 | + LeaderLayer( | ||
| 301 | + link: _startHandle!, | ||
| 302 | + offset: offset + value.startSelectionPoint!.localPosition, | ||
| 303 | + ), | ||
| 304 | + (PaintingContext context, Offset offset) {}, | ||
| 305 | + Offset.zero, | ||
| 306 | + ); | ||
| 307 | + } | ||
| 308 | + if (_endHandle != null) { | ||
| 309 | + context.pushLayer( | ||
| 310 | + LeaderLayer( | ||
| 311 | + link: _endHandle!, | ||
| 312 | + offset: offset + value.endSelectionPoint!.localPosition, | ||
| 313 | + ), | ||
| 314 | + (PaintingContext context, Offset offset) {}, | ||
| 315 | + Offset.zero, | ||
| 316 | + ); | ||
| 317 | + } | ||
| 318 | + } | ||
| 319 | + | ||
| 320 | + @override | ||
| 321 | + void dispose() { | ||
| 322 | + _geometry.dispose(); | ||
| 323 | + super.dispose(); | ||
| 324 | + } | ||
| 325 | +} |
| @@ -4,14 +4,15 @@ import 'package:flutter/material.dart'; | @@ -4,14 +4,15 @@ import 'package:flutter/material.dart'; | ||
| 4 | import 'package:gpt_markdown/custom_widgets/markdow_config.dart'; | 4 | import 'package:gpt_markdown/custom_widgets/markdow_config.dart'; |
| 5 | 5 | ||
| 6 | import 'package:flutter/foundation.dart'; | 6 | import 'package:flutter/foundation.dart'; |
| 7 | -import 'package:flutter/services.dart'; | ||
| 8 | import 'package:flutter_math_fork/flutter_math.dart'; | 7 | import 'package:flutter_math_fork/flutter_math.dart'; |
| 9 | import 'package:gpt_markdown/custom_widgets/custom_divider.dart'; | 8 | import 'package:gpt_markdown/custom_widgets/custom_divider.dart'; |
| 10 | import 'package:gpt_markdown/custom_widgets/custom_error_image.dart'; | 9 | import 'package:gpt_markdown/custom_widgets/custom_error_image.dart'; |
| 11 | import 'package:gpt_markdown/custom_widgets/custom_rb_cb.dart'; | 10 | import 'package:gpt_markdown/custom_widgets/custom_rb_cb.dart'; |
| 11 | +import 'package:gpt_markdown/custom_widgets/selectable_adapter.dart'; | ||
| 12 | import 'package:gpt_markdown/custom_widgets/unordered_ordered_list.dart'; | 12 | 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/link_button.dart'; | 16 | import 'custom_widgets/link_button.dart'; |
| 16 | 17 | ||
| 17 | part 'theme.dart'; | 18 | part 'theme.dart'; |
| @@ -19,8 +20,8 @@ part 'markdown_component.dart'; | @@ -19,8 +20,8 @@ part 'markdown_component.dart'; | ||
| 19 | part 'md_widget.dart'; | 20 | part 'md_widget.dart'; |
| 20 | 21 | ||
| 21 | /// This widget create a full markdown widget as a column view. | 22 | /// This widget create a full markdown widget as a column view. |
| 22 | -class TexMarkdown extends StatelessWidget { | ||
| 23 | - const TexMarkdown( | 23 | +class GptMarkdown extends StatelessWidget { |
| 24 | + const GptMarkdown( | ||
| 24 | this.data, { | 25 | this.data, { |
| 25 | super.key, | 26 | super.key, |
| 26 | this.style, | 27 | this.style, |
| @@ -2,24 +2,24 @@ part of 'gpt_markdown.dart'; | @@ -2,24 +2,24 @@ part of 'gpt_markdown.dart'; | ||
| 2 | 2 | ||
| 3 | /// Markdown components | 3 | /// Markdown components |
| 4 | abstract class MarkdownComponent { | 4 | abstract class MarkdownComponent { |
| 5 | - static List<MarkdownComponent> components = [ | 5 | + static final List<MarkdownComponent> components = [ |
| 6 | CodeBlockMd(), | 6 | CodeBlockMd(), |
| 7 | NewLines(), | 7 | NewLines(), |
| 8 | TableMd(), | 8 | TableMd(), |
| 9 | HTag(), | 9 | HTag(), |
| 10 | - IndentMd(), | ||
| 11 | UnOrderedList(), | 10 | UnOrderedList(), |
| 12 | OrderedList(), | 11 | OrderedList(), |
| 13 | RadioButtonMd(), | 12 | RadioButtonMd(), |
| 14 | CheckBoxMd(), | 13 | CheckBoxMd(), |
| 15 | HrLine(), | 14 | HrLine(), |
| 15 | + IndentMd(), | ||
| 16 | + LatexMathMultyLine(), | ||
| 17 | + LatexMath(), | ||
| 16 | ImageMd(), | 18 | ImageMd(), |
| 17 | HighlightedText(), | 19 | HighlightedText(), |
| 18 | StrikeMd(), | 20 | StrikeMd(), |
| 19 | BoldMd(), | 21 | BoldMd(), |
| 20 | ItalicMd(), | 22 | ItalicMd(), |
| 21 | - LatexMathMultyLine(), | ||
| 22 | - LatexMath(), | ||
| 23 | ATagMd(), | 23 | ATagMd(), |
| 24 | SourceTag(), | 24 | SourceTag(), |
| 25 | ]; | 25 | ]; |
| @@ -38,46 +38,42 @@ abstract class MarkdownComponent { | @@ -38,46 +38,42 @@ abstract class MarkdownComponent { | ||
| 38 | multiLine: true, | 38 | multiLine: true, |
| 39 | dotAll: true, | 39 | dotAll: true, |
| 40 | ); | 40 | ); |
| 41 | - List<String> elements = []; | ||
| 42 | text.splitMapJoin( | 41 | text.splitMapJoin( |
| 43 | combinedRegex, | 42 | combinedRegex, |
| 44 | onMatch: (p0) { | 43 | onMatch: (p0) { |
| 45 | String element = p0[0] ?? ""; | 44 | String element = p0[0] ?? ""; |
| 46 | - elements.add(element); | ||
| 47 | for (var each in components) { | 45 | for (var each in components) { |
| 48 | if (each.exp.hasMatch(element)) { | 46 | if (each.exp.hasMatch(element)) { |
| 49 | - if (each is InlineMd) { | 47 | + if (each.inline) { |
| 50 | spans.add(each.span( | 48 | spans.add(each.span( |
| 51 | context, | 49 | context, |
| 52 | element, | 50 | element, |
| 53 | config, | 51 | config, |
| 54 | )); | 52 | )); |
| 55 | } else { | 53 | } else { |
| 56 | - if (each is BlockMd) { | ||
| 57 | - spans.addAll([ | ||
| 58 | - TextSpan( | ||
| 59 | - text: "\n ", | ||
| 60 | - style: TextStyle( | ||
| 61 | - fontSize: 0, | ||
| 62 | - height: 0, | ||
| 63 | - color: config.style?.color, | ||
| 64 | - ), | 54 | + spans.addAll([ |
| 55 | + TextSpan( | ||
| 56 | + text: "\n ", | ||
| 57 | + style: TextStyle( | ||
| 58 | + fontSize: 0, | ||
| 59 | + height: 0, | ||
| 60 | + color: config.style?.color, | ||
| 65 | ), | 61 | ), |
| 66 | - each.span( | ||
| 67 | - context, | ||
| 68 | - element, | ||
| 69 | - config, | ||
| 70 | - ), | ||
| 71 | - TextSpan( | ||
| 72 | - text: "\n ", | ||
| 73 | - style: TextStyle( | ||
| 74 | - fontSize: 0, | ||
| 75 | - height: 0, | ||
| 76 | - color: config.style?.color, | ||
| 77 | - ), | 62 | + ), |
| 63 | + each.span( | ||
| 64 | + context, | ||
| 65 | + element, | ||
| 66 | + config, | ||
| 67 | + ), | ||
| 68 | + TextSpan( | ||
| 69 | + text: "\n ", | ||
| 70 | + style: TextStyle( | ||
| 71 | + fontSize: 0, | ||
| 72 | + height: 0, | ||
| 73 | + color: config.style?.color, | ||
| 78 | ), | 74 | ), |
| 79 | - ]); | ||
| 80 | - } | 75 | + ), |
| 76 | + ]); | ||
| 81 | } | 77 | } |
| 82 | return ""; | 78 | return ""; |
| 83 | } | 79 | } |
| @@ -125,18 +121,38 @@ abstract class InlineMd extends MarkdownComponent { | @@ -125,18 +121,38 @@ abstract class InlineMd extends MarkdownComponent { | ||
| 125 | abstract class BlockMd extends MarkdownComponent { | 121 | abstract class BlockMd extends MarkdownComponent { |
| 126 | @override | 122 | @override |
| 127 | bool get inline => false; | 123 | bool get inline => false; |
| 124 | + | ||
| 125 | + @override | ||
| 126 | + RegExp get exp => RegExp( | ||
| 127 | + r'^\ *?' + expString, | ||
| 128 | + dotAll: true, | ||
| 129 | + multiLine: true, | ||
| 130 | + ); | ||
| 131 | + | ||
| 132 | + String get expString; | ||
| 133 | + | ||
| 128 | @override | 134 | @override |
| 129 | InlineSpan span( | 135 | InlineSpan span( |
| 130 | BuildContext context, | 136 | BuildContext context, |
| 131 | String text, | 137 | String text, |
| 132 | final GptMarkdownConfig config, | 138 | final GptMarkdownConfig config, |
| 133 | ) { | 139 | ) { |
| 140 | + var matches = RegExp(r'^(?<spaces>\ \ +).*').firstMatch(text); | ||
| 141 | + var spaces = matches?.namedGroup('spaces'); | ||
| 142 | + var length = spaces?.length ?? 0; | ||
| 143 | + var child = build( | ||
| 144 | + context, | ||
| 145 | + text, | ||
| 146 | + config, | ||
| 147 | + ); | ||
| 148 | + if (length > 0) { | ||
| 149 | + child = UnorderedListView( | ||
| 150 | + spacing: length.toDouble() * 6, | ||
| 151 | + child: child, | ||
| 152 | + ); | ||
| 153 | + } | ||
| 134 | return WidgetSpan( | 154 | return WidgetSpan( |
| 135 | - child: build( | ||
| 136 | - context, | ||
| 137 | - text, | ||
| 138 | - config, | ||
| 139 | - ), | 155 | + child: child, |
| 140 | alignment: PlaceholderAlignment.middle, | 156 | alignment: PlaceholderAlignment.middle, |
| 141 | ); | 157 | ); |
| 142 | } | 158 | } |
| @@ -151,57 +167,44 @@ abstract class BlockMd extends MarkdownComponent { | @@ -151,57 +167,44 @@ abstract class BlockMd extends MarkdownComponent { | ||
| 151 | /// Heading component | 167 | /// Heading component |
| 152 | class HTag extends BlockMd { | 168 | class HTag extends BlockMd { |
| 153 | @override | 169 | @override |
| 154 | - RegExp get exp => RegExp(r"^(#{1,6})\ ([^\n]+?)$"); | 170 | + String get expString => (r"(?<hash>#{1,6})\ (?<data>[^\n]+?)$"); |
| 155 | @override | 171 | @override |
| 156 | Widget build( | 172 | Widget build( |
| 157 | BuildContext context, | 173 | BuildContext context, |
| 158 | String text, | 174 | String text, |
| 159 | final GptMarkdownConfig config, | 175 | final GptMarkdownConfig config, |
| 160 | ) { | 176 | ) { |
| 161 | - var match = exp.firstMatch(text.trim()); | 177 | + var theme = GptMarkdownTheme.of(context); |
| 178 | + var match = this.exp.firstMatch(text.trim()); | ||
| 162 | var conf = config.copyWith( | 179 | var conf = config.copyWith( |
| 163 | - style: [ | ||
| 164 | - Theme.of(context) | ||
| 165 | - .textTheme | ||
| 166 | - .headlineLarge | ||
| 167 | - ?.copyWith(color: config.style?.color), | ||
| 168 | - Theme.of(context) | ||
| 169 | - .textTheme | ||
| 170 | - .headlineMedium | ||
| 171 | - ?.copyWith(color: config.style?.color), | ||
| 172 | - Theme.of(context) | ||
| 173 | - .textTheme | ||
| 174 | - .headlineSmall | ||
| 175 | - ?.copyWith(color: config.style?.color), | ||
| 176 | - Theme.of(context) | ||
| 177 | - .textTheme | ||
| 178 | - .titleLarge | ||
| 179 | - ?.copyWith(color: config.style?.color), | ||
| 180 | - Theme.of(context) | ||
| 181 | - .textTheme | ||
| 182 | - .titleMedium | ||
| 183 | - ?.copyWith(color: config.style?.color), | ||
| 184 | - Theme.of(context) | ||
| 185 | - .textTheme | ||
| 186 | - .titleSmall | ||
| 187 | - ?.copyWith(color: config.style?.color), | ||
| 188 | - ][match![1]!.length - 1]); | 180 | + style: [ |
| 181 | + theme.h1, | ||
| 182 | + theme.h2, | ||
| 183 | + theme.h3, | ||
| 184 | + theme.h4, | ||
| 185 | + theme.h5, | ||
| 186 | + theme.h6, | ||
| 187 | + ][match![1]!.length - 1] | ||
| 188 | + ?.copyWith( | ||
| 189 | + color: config.style?.color, | ||
| 190 | + ), | ||
| 191 | + ); | ||
| 189 | return config.getRich( | 192 | return config.getRich( |
| 190 | TextSpan( | 193 | TextSpan( |
| 191 | children: [ | 194 | children: [ |
| 192 | ...(MarkdownComponent.generate( | 195 | ...(MarkdownComponent.generate( |
| 193 | context, | 196 | context, |
| 194 | - "${match[2]}", | 197 | + "${match.namedGroup('data')}", |
| 195 | conf, | 198 | conf, |
| 196 | )), | 199 | )), |
| 197 | - if (match[1]!.length == 1) ...[ | 200 | + if (match.namedGroup('hash')!.length == 1) ...[ |
| 198 | const TextSpan( | 201 | const TextSpan( |
| 199 | text: "\n ", | 202 | text: "\n ", |
| 200 | style: TextStyle(fontSize: 0, height: 0), | 203 | style: TextStyle(fontSize: 0, height: 0), |
| 201 | ), | 204 | ), |
| 202 | WidgetSpan( | 205 | WidgetSpan( |
| 203 | child: CustomDivider( | 206 | child: CustomDivider( |
| 204 | - height: 2, | 207 | + height: theme.hrLineThickness, |
| 205 | color: config.style?.color ?? | 208 | color: config.style?.color ?? |
| 206 | Theme.of(context).colorScheme.outline, | 209 | Theme.of(context).colorScheme.outline, |
| 207 | ), | 210 | ), |
| @@ -235,16 +238,18 @@ class NewLines extends InlineMd { | @@ -235,16 +238,18 @@ class NewLines extends InlineMd { | ||
| 235 | /// Horizontal line component | 238 | /// Horizontal line component |
| 236 | class HrLine extends BlockMd { | 239 | class HrLine extends BlockMd { |
| 237 | @override | 240 | @override |
| 238 | - RegExp get exp => RegExp(r"^(--)[-]+$"); | 241 | + String get expString => (r"(--)[-]+$"); |
| 239 | @override | 242 | @override |
| 240 | Widget build( | 243 | Widget build( |
| 241 | BuildContext context, | 244 | BuildContext context, |
| 242 | String text, | 245 | String text, |
| 243 | final GptMarkdownConfig config, | 246 | final GptMarkdownConfig config, |
| 244 | ) { | 247 | ) { |
| 248 | + var thickness = GptMarkdownTheme.of(context).hrLineThickness; | ||
| 249 | + var color = GptMarkdownTheme.of(context).hrLineColor; | ||
| 245 | return CustomDivider( | 250 | return CustomDivider( |
| 246 | - height: 2, | ||
| 247 | - color: config.style?.color ?? Theme.of(context).colorScheme.outline, | 251 | + height: thickness, |
| 252 | + color: config.style?.color ?? color, | ||
| 248 | ); | 253 | ); |
| 249 | } | 254 | } |
| 250 | } | 255 | } |
| @@ -252,7 +257,7 @@ class HrLine extends BlockMd { | @@ -252,7 +257,7 @@ class HrLine extends BlockMd { | ||
| 252 | /// Checkbox component | 257 | /// Checkbox component |
| 253 | class CheckBoxMd extends BlockMd { | 258 | class CheckBoxMd extends BlockMd { |
| 254 | @override | 259 | @override |
| 255 | - RegExp get exp => RegExp(r"^\[(\x?)\]\ (\S[^\n]*?)$"); | 260 | + String get expString => (r"\[(\x?)\]\ (\S[^\n]*?)$"); |
| 256 | get onLinkTab => null; | 261 | get onLinkTab => null; |
| 257 | 262 | ||
| 258 | @override | 263 | @override |
| @@ -261,7 +266,7 @@ class CheckBoxMd extends BlockMd { | @@ -261,7 +266,7 @@ class CheckBoxMd extends BlockMd { | ||
| 261 | String text, | 266 | String text, |
| 262 | final GptMarkdownConfig config, | 267 | final GptMarkdownConfig config, |
| 263 | ) { | 268 | ) { |
| 264 | - var match = exp.firstMatch(text.trim()); | 269 | + var match = this.exp.firstMatch(text.trim()); |
| 265 | return CustomCb( | 270 | return CustomCb( |
| 266 | value: ("${match?[1]}" == "x"), | 271 | value: ("${match?[1]}" == "x"), |
| 267 | textDirection: config.textDirection, | 272 | textDirection: config.textDirection, |
| @@ -276,7 +281,7 @@ class CheckBoxMd extends BlockMd { | @@ -276,7 +281,7 @@ class CheckBoxMd extends BlockMd { | ||
| 276 | /// Radio Button component | 281 | /// Radio Button component |
| 277 | class RadioButtonMd extends BlockMd { | 282 | class RadioButtonMd extends BlockMd { |
| 278 | @override | 283 | @override |
| 279 | - RegExp get exp => RegExp(r"^\((\x?)\)\ (\S[^\n]*)$"); | 284 | + String get expString => (r"\((\x?)\)\ (\S[^\n]*)$"); |
| 280 | get onLinkTab => null; | 285 | get onLinkTab => null; |
| 281 | 286 | ||
| 282 | @override | 287 | @override |
| @@ -285,7 +290,7 @@ class RadioButtonMd extends BlockMd { | @@ -285,7 +290,7 @@ class RadioButtonMd extends BlockMd { | ||
| 285 | String text, | 290 | String text, |
| 286 | final GptMarkdownConfig config, | 291 | final GptMarkdownConfig config, |
| 287 | ) { | 292 | ) { |
| 288 | - var match = exp.firstMatch(text.trim()); | 293 | + var match = this.exp.firstMatch(text.trim()); |
| 289 | return CustomRb( | 294 | return CustomRb( |
| 290 | value: ("${match?[1]}" == "x"), | 295 | value: ("${match?[1]}" == "x"), |
| 291 | textDirection: config.textDirection, | 296 | textDirection: config.textDirection, |
| @@ -298,36 +303,45 @@ class RadioButtonMd extends BlockMd { | @@ -298,36 +303,45 @@ class RadioButtonMd extends BlockMd { | ||
| 298 | } | 303 | } |
| 299 | 304 | ||
| 300 | /// Indent | 305 | /// Indent |
| 301 | -class IndentMd extends BlockMd { | 306 | +class IndentMd extends InlineMd { |
| 302 | @override | 307 | @override |
| 303 | - RegExp get exp => RegExp(r"^(\ \ \ \ +)([^\n]+)$"); | ||
| 304 | - get onLinkTab => null; | 308 | + RegExp get exp => |
| 309 | + RegExp(r"^(\ +)(((?!\n\n).)+)$", dotAll: true, multiLine: true); | ||
| 305 | 310 | ||
| 306 | @override | 311 | @override |
| 307 | - Widget build( | 312 | + InlineSpan span( |
| 308 | BuildContext context, | 313 | BuildContext context, |
| 309 | String text, | 314 | String text, |
| 310 | final GptMarkdownConfig config, | 315 | final GptMarkdownConfig config, |
| 311 | ) { | 316 | ) { |
| 312 | - [ | ||
| 313 | - r"\\\[(.*?)\\\]", | ||
| 314 | - r"\\\((.*?)\\\)", | ||
| 315 | - r"(?<!\\)\$((?:\\.|[^$])*?)\$(?!\\)", | ||
| 316 | - ].join("|"); | ||
| 317 | var match = exp.firstMatch(text); | 317 | var match = exp.firstMatch(text); |
| 318 | int spaces = (match?[1] ?? "").length; | 318 | int spaces = (match?[1] ?? "").length; |
| 319 | - return UnorderedListView( | ||
| 320 | - bulletColor: config.style?.color, | ||
| 321 | - padding: spaces * 5, | ||
| 322 | - bulletSize: 0, | ||
| 323 | - textDirection: config.textDirection, | ||
| 324 | - child: Text.rich(TextSpan( | ||
| 325 | - children: MarkdownComponent.generate( | ||
| 326 | - context, | ||
| 327 | - "${match?[2]}", | ||
| 328 | - config, | 319 | + var data = "${match?[2]}".trim(); |
| 320 | + data.replaceAll(RegExp(r'\n\ *'), '\n').trim(); | ||
| 321 | + var child = TextSpan( | ||
| 322 | + children: MarkdownComponent.generate( | ||
| 323 | + context, | ||
| 324 | + data, | ||
| 325 | + config, | ||
| 326 | + ), | ||
| 327 | + ); | ||
| 328 | + if (spaces < 4) { | ||
| 329 | + return child; | ||
| 330 | + } | ||
| 331 | + return TextSpan( | ||
| 332 | + children: [ | ||
| 333 | + const TextSpan(text: '\n'), | ||
| 334 | + WidgetSpan( | ||
| 335 | + child: UnorderedListView( | ||
| 336 | + bulletColor: config.style?.color, | ||
| 337 | + padding: spaces * 5, | ||
| 338 | + bulletSize: 0, | ||
| 339 | + textDirection: config.textDirection, | ||
| 340 | + child: Text.rich(child), | ||
| 341 | + ), | ||
| 329 | ), | 342 | ), |
| 330 | - )), | 343 | + const TextSpan(text: '\n'), |
| 344 | + ], | ||
| 331 | ); | 345 | ); |
| 332 | } | 346 | } |
| 333 | } | 347 | } |
| @@ -335,7 +349,7 @@ class IndentMd extends BlockMd { | @@ -335,7 +349,7 @@ class IndentMd extends BlockMd { | ||
| 335 | /// Unordered list component | 349 | /// Unordered list component |
| 336 | class UnOrderedList extends BlockMd { | 350 | class UnOrderedList extends BlockMd { |
| 337 | @override | 351 | @override |
| 338 | - RegExp get exp => RegExp(r"^(?:\-|\*)\ ([^\n]+)$"); | 352 | + String get expString => (r"(?:\-|\*)\ ([^\n]+)$"); |
| 339 | get onLinkTab => null; | 353 | get onLinkTab => null; |
| 340 | 354 | ||
| 341 | @override | 355 | @override |
| @@ -344,7 +358,7 @@ class UnOrderedList extends BlockMd { | @@ -344,7 +358,7 @@ class UnOrderedList extends BlockMd { | ||
| 344 | String text, | 358 | String text, |
| 345 | final GptMarkdownConfig config, | 359 | final GptMarkdownConfig config, |
| 346 | ) { | 360 | ) { |
| 347 | - var match = exp.firstMatch(text); | 361 | + var match = this.exp.firstMatch(text); |
| 348 | return UnorderedListView( | 362 | return UnorderedListView( |
| 349 | bulletColor: | 363 | bulletColor: |
| 350 | config.style?.color ?? DefaultTextStyle.of(context).style.color, | 364 | config.style?.color ?? DefaultTextStyle.of(context).style.color, |
| @@ -365,7 +379,7 @@ class UnOrderedList extends BlockMd { | @@ -365,7 +379,7 @@ class UnOrderedList extends BlockMd { | ||
| 365 | /// Ordered list component | 379 | /// Ordered list component |
| 366 | class OrderedList extends BlockMd { | 380 | class OrderedList extends BlockMd { |
| 367 | @override | 381 | @override |
| 368 | - RegExp get exp => RegExp(r"^([0-9]+\.)\ ([^\n]+)$"); | 382 | + String get expString => (r"([0-9]+\.)\ ([^\n]+)$"); |
| 369 | 383 | ||
| 370 | get onLinkTab => null; | 384 | get onLinkTab => null; |
| 371 | 385 | ||
| @@ -375,7 +389,7 @@ class OrderedList extends BlockMd { | @@ -375,7 +389,7 @@ class OrderedList extends BlockMd { | ||
| 375 | String text, | 389 | String text, |
| 376 | final GptMarkdownConfig config, | 390 | final GptMarkdownConfig config, |
| 377 | ) { | 391 | ) { |
| 378 | - var match = exp.firstMatch(text.trim()); | 392 | + var match = this.exp.firstMatch(text.trim()); |
| 379 | return OrderedListView( | 393 | return OrderedListView( |
| 380 | no: "${match?[1]}", | 394 | no: "${match?[1]}", |
| 381 | textDirection: config.textDirection, | 395 | textDirection: config.textDirection, |
| @@ -508,10 +522,9 @@ class ItalicMd extends InlineMd { | @@ -508,10 +522,9 @@ class ItalicMd extends InlineMd { | ||
| 508 | 522 | ||
| 509 | class LatexMathMultyLine extends BlockMd { | 523 | class LatexMathMultyLine extends BlockMd { |
| 510 | @override | 524 | @override |
| 511 | - RegExp get exp => RegExp( | ||
| 512 | - r"\\\[(.*?)\\\]|(\\begin.*?\\end{.*?})", | ||
| 513 | - dotAll: true, | ||
| 514 | - ); | 525 | + String get expString => (r"\\\[(((?!\n\n).)*)\\\]|(\\begin.*?\\end{.*?})"); |
| 526 | + @override | ||
| 527 | + RegExp get exp => RegExp(expString, dotAll: true, multiLine: true); | ||
| 515 | 528 | ||
| 516 | @override | 529 | @override |
| 517 | Widget build( | 530 | Widget build( |
| @@ -520,48 +533,50 @@ class LatexMathMultyLine extends BlockMd { | @@ -520,48 +533,50 @@ class LatexMathMultyLine extends BlockMd { | ||
| 520 | final GptMarkdownConfig config, | 533 | final GptMarkdownConfig config, |
| 521 | ) { | 534 | ) { |
| 522 | var p0 = exp.firstMatch(text.trim()); | 535 | var p0 = exp.firstMatch(text.trim()); |
| 523 | - p0?.group(0); | ||
| 524 | String mathText = p0?[1] ?? p0?[2] ?? ""; | 536 | String mathText = p0?[1] ?? p0?[2] ?? ""; |
| 525 | var workaround = config.latexWorkaround ?? (String tex) => tex; | 537 | var workaround = config.latexWorkaround ?? (String tex) => tex; |
| 526 | 538 | ||
| 527 | var builder = config.latexBuilder ?? | 539 | var builder = config.latexBuilder ?? |
| 528 | (BuildContext context, String tex, TextStyle textStyle, bool inline) => | 540 | (BuildContext context, String tex, TextStyle textStyle, bool inline) => |
| 529 | - Math.tex( | ||
| 530 | - tex, | ||
| 531 | - textStyle: textStyle, | ||
| 532 | - mathStyle: MathStyle.display, | ||
| 533 | - textScaleFactor: 1, | ||
| 534 | - settings: const TexParserSettings( | ||
| 535 | - strict: Strict.ignore, | ||
| 536 | - ), | ||
| 537 | - options: MathOptions( | ||
| 538 | - sizeUnderTextStyle: MathSize.large, | ||
| 539 | - color: config.style?.color ?? | ||
| 540 | - Theme.of(context).colorScheme.onSurface, | ||
| 541 | - fontSize: config.style?.fontSize ?? | ||
| 542 | - Theme.of(context).textTheme.bodyMedium?.fontSize, | ||
| 543 | - mathFontOptions: FontOptions( | ||
| 544 | - fontFamily: "Main", | ||
| 545 | - fontWeight: config.style?.fontWeight ?? FontWeight.normal, | ||
| 546 | - fontShape: FontStyle.normal, | 541 | + SelectableAdapter( |
| 542 | + selectedText: tex, | ||
| 543 | + child: Math.tex( | ||
| 544 | + tex, | ||
| 545 | + textStyle: textStyle, | ||
| 546 | + mathStyle: MathStyle.display, | ||
| 547 | + textScaleFactor: 1, | ||
| 548 | + settings: const TexParserSettings( | ||
| 549 | + strict: Strict.ignore, | ||
| 547 | ), | 550 | ), |
| 548 | - textFontOptions: FontOptions( | ||
| 549 | - fontFamily: "Main", | ||
| 550 | - fontWeight: config.style?.fontWeight ?? FontWeight.normal, | ||
| 551 | - fontShape: FontStyle.normal, | 551 | + options: MathOptions( |
| 552 | + sizeUnderTextStyle: MathSize.large, | ||
| 553 | + color: config.style?.color ?? | ||
| 554 | + Theme.of(context).colorScheme.onSurface, | ||
| 555 | + fontSize: config.style?.fontSize ?? | ||
| 556 | + Theme.of(context).textTheme.bodyMedium?.fontSize, | ||
| 557 | + mathFontOptions: FontOptions( | ||
| 558 | + fontFamily: "Main", | ||
| 559 | + fontWeight: config.style?.fontWeight ?? FontWeight.normal, | ||
| 560 | + fontShape: FontStyle.normal, | ||
| 561 | + ), | ||
| 562 | + textFontOptions: FontOptions( | ||
| 563 | + fontFamily: "Main", | ||
| 564 | + fontWeight: config.style?.fontWeight ?? FontWeight.normal, | ||
| 565 | + fontShape: FontStyle.normal, | ||
| 566 | + ), | ||
| 567 | + style: MathStyle.display, | ||
| 552 | ), | 568 | ), |
| 553 | - style: MathStyle.display, | 569 | + onErrorFallback: (err) { |
| 570 | + return Text( | ||
| 571 | + workaround(mathText), | ||
| 572 | + textDirection: config.textDirection, | ||
| 573 | + style: textStyle.copyWith( | ||
| 574 | + color: (!kDebugMode) | ||
| 575 | + ? null | ||
| 576 | + : Theme.of(context).colorScheme.error), | ||
| 577 | + ); | ||
| 578 | + }, | ||
| 554 | ), | 579 | ), |
| 555 | - onErrorFallback: (err) { | ||
| 556 | - return Text( | ||
| 557 | - workaround(mathText), | ||
| 558 | - textDirection: config.textDirection, | ||
| 559 | - style: textStyle.copyWith( | ||
| 560 | - color: (!kDebugMode) | ||
| 561 | - ? null | ||
| 562 | - : Theme.of(context).colorScheme.error), | ||
| 563 | - ); | ||
| 564 | - }, | ||
| 565 | ); | 580 | ); |
| 566 | return builder(context, workaround(mathText), | 581 | return builder(context, workaround(mathText), |
| 567 | config.style ?? const TextStyle(), false); | 582 | config.style ?? const TextStyle(), false); |
| @@ -591,42 +606,45 @@ class LatexMath extends InlineMd { | @@ -591,42 +606,45 @@ class LatexMath extends InlineMd { | ||
| 591 | var workaround = config.latexWorkaround ?? (String tex) => tex; | 606 | var workaround = config.latexWorkaround ?? (String tex) => tex; |
| 592 | var builder = config.latexBuilder ?? | 607 | var builder = config.latexBuilder ?? |
| 593 | (BuildContext context, String tex, TextStyle textStyle, bool inline) => | 608 | (BuildContext context, String tex, TextStyle textStyle, bool inline) => |
| 594 | - Math.tex( | ||
| 595 | - tex, | ||
| 596 | - textStyle: textStyle, | ||
| 597 | - mathStyle: MathStyle.display, | ||
| 598 | - textScaleFactor: 1, | ||
| 599 | - settings: const TexParserSettings( | ||
| 600 | - strict: Strict.ignore, | ||
| 601 | - ), | ||
| 602 | - options: MathOptions( | ||
| 603 | - sizeUnderTextStyle: MathSize.large, | ||
| 604 | - color: config.style?.color ?? | ||
| 605 | - Theme.of(context).colorScheme.onSurface, | ||
| 606 | - fontSize: config.style?.fontSize ?? | ||
| 607 | - Theme.of(context).textTheme.bodyMedium?.fontSize, | ||
| 608 | - mathFontOptions: FontOptions( | ||
| 609 | - fontFamily: "Main", | ||
| 610 | - fontWeight: config.style?.fontWeight ?? FontWeight.normal, | ||
| 611 | - fontShape: FontStyle.normal, | 609 | + SelectableAdapter( |
| 610 | + selectedText: tex, | ||
| 611 | + child: Math.tex( | ||
| 612 | + tex, | ||
| 613 | + textStyle: textStyle, | ||
| 614 | + mathStyle: MathStyle.display, | ||
| 615 | + textScaleFactor: 1, | ||
| 616 | + settings: const TexParserSettings( | ||
| 617 | + strict: Strict.ignore, | ||
| 612 | ), | 618 | ), |
| 613 | - textFontOptions: FontOptions( | ||
| 614 | - fontFamily: "Main", | ||
| 615 | - fontWeight: config.style?.fontWeight ?? FontWeight.normal, | ||
| 616 | - fontShape: FontStyle.normal, | 619 | + options: MathOptions( |
| 620 | + sizeUnderTextStyle: MathSize.large, | ||
| 621 | + color: config.style?.color ?? | ||
| 622 | + Theme.of(context).colorScheme.onSurface, | ||
| 623 | + fontSize: config.style?.fontSize ?? | ||
| 624 | + Theme.of(context).textTheme.bodyMedium?.fontSize, | ||
| 625 | + mathFontOptions: FontOptions( | ||
| 626 | + fontFamily: "Main", | ||
| 627 | + fontWeight: config.style?.fontWeight ?? FontWeight.normal, | ||
| 628 | + fontShape: FontStyle.normal, | ||
| 629 | + ), | ||
| 630 | + textFontOptions: FontOptions( | ||
| 631 | + fontFamily: "Main", | ||
| 632 | + fontWeight: config.style?.fontWeight ?? FontWeight.normal, | ||
| 633 | + fontShape: FontStyle.normal, | ||
| 634 | + ), | ||
| 635 | + style: MathStyle.display, | ||
| 617 | ), | 636 | ), |
| 618 | - style: MathStyle.display, | 637 | + onErrorFallback: (err) { |
| 638 | + return Text( | ||
| 639 | + workaround(mathText), | ||
| 640 | + textDirection: config.textDirection, | ||
| 641 | + style: textStyle.copyWith( | ||
| 642 | + color: (!kDebugMode) | ||
| 643 | + ? null | ||
| 644 | + : Theme.of(context).colorScheme.error), | ||
| 645 | + ); | ||
| 646 | + }, | ||
| 619 | ), | 647 | ), |
| 620 | - onErrorFallback: (err) { | ||
| 621 | - return Text( | ||
| 622 | - workaround(mathText), | ||
| 623 | - textDirection: config.textDirection, | ||
| 624 | - style: textStyle.copyWith( | ||
| 625 | - color: (!kDebugMode) | ||
| 626 | - ? null | ||
| 627 | - : Theme.of(context).colorScheme.error), | ||
| 628 | - ); | ||
| 629 | - }, | ||
| 630 | ); | 648 | ); |
| 631 | return WidgetSpan( | 649 | return WidgetSpan( |
| 632 | alignment: PlaceholderAlignment.baseline, | 650 | alignment: PlaceholderAlignment.baseline, |
| @@ -655,7 +673,6 @@ class SourceTag extends InlineMd { | @@ -655,7 +673,6 @@ class SourceTag extends InlineMd { | ||
| 655 | } | 673 | } |
| 656 | return WidgetSpan( | 674 | return WidgetSpan( |
| 657 | alignment: PlaceholderAlignment.middle, | 675 | alignment: PlaceholderAlignment.middle, |
| 658 | - // baseline: TextBaseline.alphabetic, | ||
| 659 | child: Padding( | 676 | child: Padding( |
| 660 | padding: const EdgeInsets.all(2), | 677 | padding: const EdgeInsets.all(2), |
| 661 | child: config.sourceTagBuilder | 678 | child: config.sourceTagBuilder |
| @@ -696,8 +713,11 @@ class ATagMd extends InlineMd { | @@ -696,8 +713,11 @@ class ATagMd extends InlineMd { | ||
| 696 | if (match?[1] == null && match?[2] == null) { | 713 | if (match?[1] == null && match?[2] == null) { |
| 697 | return const TextSpan(); | 714 | return const TextSpan(); |
| 698 | } | 715 | } |
| 716 | + var theme = GptMarkdownTheme.of(context); | ||
| 699 | return WidgetSpan( | 717 | return WidgetSpan( |
| 700 | child: LinkButton( | 718 | child: LinkButton( |
| 719 | + hoverColor: theme.linkHoverColor, | ||
| 720 | + color: theme.linkColor, | ||
| 701 | onPressed: () { | 721 | onPressed: () { |
| 702 | config.onLinkTab?.call("${match?[2]}", "${match?[1]}"); | 722 | config.onLinkTab?.call("${match?[2]}", "${match?[1]}"); |
| 703 | }, | 723 | }, |
| @@ -762,6 +782,9 @@ class ImageMd extends InlineMd { | @@ -762,6 +782,9 @@ class ImageMd extends InlineMd { | ||
| 762 | /// Table component | 782 | /// Table component |
| 763 | class TableMd extends BlockMd { | 783 | class TableMd extends BlockMd { |
| 764 | @override | 784 | @override |
| 785 | + String get expString => | ||
| 786 | + (r"(((\|[^\n\|]+\|)((([^\n\|]+\|)+)?))(\n(((\|[^\n\|]+\|)(([^\n\|]+\|)+)?)))+)$"); | ||
| 787 | + @override | ||
| 765 | Widget build( | 788 | Widget build( |
| 766 | BuildContext context, | 789 | BuildContext context, |
| 767 | String text, | 790 | String text, |
| @@ -847,28 +870,19 @@ class TableMd extends BlockMd { | @@ -847,28 +870,19 @@ class TableMd extends BlockMd { | ||
| 847 | ), | 870 | ), |
| 848 | ); | 871 | ); |
| 849 | } | 872 | } |
| 850 | - | ||
| 851 | - @override | ||
| 852 | - RegExp get exp => RegExp( | ||
| 853 | - r"^(((\|[^\n\|]+\|)((([^\n\|]+\|)+)?))(\n(((\|[^\n\|]+\|)(([^\n\|]+\|)+)?)))+)$", | ||
| 854 | - ); | ||
| 855 | } | 873 | } |
| 856 | 874 | ||
| 857 | class CodeBlockMd extends BlockMd { | 875 | class CodeBlockMd extends BlockMd { |
| 858 | @override | 876 | @override |
| 859 | - RegExp get exp => RegExp( | ||
| 860 | - r"\s*?```(.*?)\n((.*?)(:?\n\s*?```)|(.*)(:?\n```)?)$", | ||
| 861 | - multiLine: true, | ||
| 862 | - dotAll: true, | ||
| 863 | - ); | 877 | + String get expString => r"```(.*?)\n((.*?)(:?\n\s*?```)|(.*)(:?\n```)?)$"; |
| 864 | @override | 878 | @override |
| 865 | Widget build( | 879 | Widget build( |
| 866 | BuildContext context, | 880 | BuildContext context, |
| 867 | String text, | 881 | String text, |
| 868 | final GptMarkdownConfig config, | 882 | final GptMarkdownConfig config, |
| 869 | ) { | 883 | ) { |
| 870 | - String codes = exp.firstMatch(text)?[2] ?? ""; | ||
| 871 | - String name = exp.firstMatch(text)?[1] ?? ""; | 884 | + String codes = this.exp.firstMatch(text)?[2] ?? ""; |
| 885 | + String name = this.exp.firstMatch(text)?[1] ?? ""; | ||
| 872 | codes = codes.replaceAll(r"```", "").trim(); | 886 | codes = codes.replaceAll(r"```", "").trim(); |
| 873 | return Padding( | 887 | return Padding( |
| 874 | padding: const EdgeInsets.all(16.0), | 888 | padding: const EdgeInsets.all(16.0), |
| @@ -878,75 +892,3 @@ class CodeBlockMd extends BlockMd { | @@ -878,75 +892,3 @@ class CodeBlockMd extends BlockMd { | ||
| 878 | ); | 892 | ); |
| 879 | } | 893 | } |
| 880 | } | 894 | } |
| 881 | - | ||
| 882 | -class CodeField extends StatefulWidget { | ||
| 883 | - const CodeField({super.key, required this.name, required this.codes}); | ||
| 884 | - final String name; | ||
| 885 | - final String codes; | ||
| 886 | - | ||
| 887 | - @override | ||
| 888 | - State<CodeField> createState() => _CodeFieldState(); | ||
| 889 | -} | ||
| 890 | - | ||
| 891 | -class _CodeFieldState extends State<CodeField> { | ||
| 892 | - bool _copied = false; | ||
| 893 | - @override | ||
| 894 | - Widget build(BuildContext context) { | ||
| 895 | - return Material( | ||
| 896 | - color: Theme.of(context).colorScheme.onInverseSurface, | ||
| 897 | - shape: RoundedRectangleBorder( | ||
| 898 | - borderRadius: BorderRadius.circular(8), | ||
| 899 | - ), | ||
| 900 | - child: Column( | ||
| 901 | - crossAxisAlignment: CrossAxisAlignment.stretch, | ||
| 902 | - children: [ | ||
| 903 | - Row( | ||
| 904 | - children: [ | ||
| 905 | - Padding( | ||
| 906 | - padding: | ||
| 907 | - const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), | ||
| 908 | - child: Text(widget.name), | ||
| 909 | - ), | ||
| 910 | - const Spacer(), | ||
| 911 | - TextButton.icon( | ||
| 912 | - style: TextButton.styleFrom( | ||
| 913 | - foregroundColor: Theme.of(context).colorScheme.onSurface, | ||
| 914 | - textStyle: const TextStyle( | ||
| 915 | - fontWeight: FontWeight.normal, | ||
| 916 | - ), | ||
| 917 | - ), | ||
| 918 | - onPressed: () async { | ||
| 919 | - await Clipboard.setData(ClipboardData(text: widget.codes)) | ||
| 920 | - .then((value) { | ||
| 921 | - setState(() { | ||
| 922 | - _copied = true; | ||
| 923 | - }); | ||
| 924 | - }); | ||
| 925 | - await Future.delayed(const Duration(seconds: 2)); | ||
| 926 | - setState(() { | ||
| 927 | - _copied = false; | ||
| 928 | - }); | ||
| 929 | - }, | ||
| 930 | - icon: Icon( | ||
| 931 | - (_copied) ? Icons.done : Icons.content_paste, | ||
| 932 | - size: 15, | ||
| 933 | - ), | ||
| 934 | - label: Text((_copied) ? "Copied!" : "Copy code"), | ||
| 935 | - ), | ||
| 936 | - ], | ||
| 937 | - ), | ||
| 938 | - const Divider( | ||
| 939 | - height: 1, | ||
| 940 | - ), | ||
| 941 | - SingleChildScrollView( | ||
| 942 | - scrollDirection: Axis.horizontal, | ||
| 943 | - padding: const EdgeInsets.all(16), | ||
| 944 | - child: Text( | ||
| 945 | - widget.codes, | ||
| 946 | - ), | ||
| 947 | - ), | ||
| 948 | - ], | ||
| 949 | - ), | ||
| 950 | - ); | ||
| 951 | - } | ||
| 952 | -} |
| @@ -16,16 +16,17 @@ class MdWidget extends StatelessWidget { | @@ -16,16 +16,17 @@ class MdWidget extends StatelessWidget { | ||
| 16 | list.addAll( | 16 | list.addAll( |
| 17 | MarkdownComponent.generate( | 17 | MarkdownComponent.generate( |
| 18 | context, | 18 | context, |
| 19 | - exp.replaceAllMapped( | ||
| 20 | - RegExp( | ||
| 21 | - r"\\\[(.*?)\\\]|(\\begin.*?\\end{.*?})", | ||
| 22 | - multiLine: true, | ||
| 23 | - dotAll: true, | ||
| 24 | - ), (match) { | ||
| 25 | - // | ||
| 26 | - String body = (match[1] ?? match[2])?.replaceAll("\n", " ") ?? ""; | ||
| 27 | - return "\\[$body\\]"; | ||
| 28 | - }), | 19 | + exp, |
| 20 | + // .replaceAllMapped( | ||
| 21 | + // RegExp( | ||
| 22 | + // r"\\\[(.*?)\\\]|(\\begin.*?\\end{.*?})", | ||
| 23 | + // multiLine: true, | ||
| 24 | + // dotAll: true, | ||
| 25 | + // ), (match) { | ||
| 26 | + // // | ||
| 27 | + // String body = (match[1] ?? match[2])?.replaceAll("\n", " ") ?? ""; | ||
| 28 | + // return "\\[$body\\]"; | ||
| 29 | + // }), | ||
| 29 | config, | 30 | config, |
| 30 | ), | 31 | ), |
| 31 | ); | 32 | ); |
| 1 | part of 'gpt_markdown.dart'; | 1 | part of 'gpt_markdown.dart'; |
| 2 | 2 | ||
| 3 | -/// Theme defined for `TexMarkdown` widget | 3 | +/// Theme defined for `GptMarkdown` widget |
| 4 | class GptMarkdownThemeData extends ThemeExtension<GptMarkdownThemeData> { | 4 | class GptMarkdownThemeData extends ThemeExtension<GptMarkdownThemeData> { |
| 5 | - GptMarkdownThemeData({ | 5 | + GptMarkdownThemeData._({ |
| 6 | required this.highlightColor, | 6 | required this.highlightColor, |
| 7 | + required this.h1, | ||
| 8 | + required this.h2, | ||
| 9 | + required this.h3, | ||
| 10 | + required this.h4, | ||
| 11 | + required this.h5, | ||
| 12 | + required this.h6, | ||
| 13 | + required this.hrLineThickness, | ||
| 14 | + required this.hrLineColor, | ||
| 15 | + required this.linkColor, | ||
| 16 | + required this.linkHoverColor, | ||
| 7 | }); | 17 | }); |
| 8 | 18 | ||
| 9 | - /// Define default attributes. | ||
| 10 | - factory GptMarkdownThemeData.from(BuildContext context) { | ||
| 11 | - return GptMarkdownThemeData( | ||
| 12 | - highlightColor: | ||
| 13 | - Theme.of(context).colorScheme.onSurfaceVariant.withAlpha(50), | 19 | + factory GptMarkdownThemeData({ |
| 20 | + required Brightness brightness, | ||
| 21 | + Color? highlightColor, | ||
| 22 | + TextStyle? h1, | ||
| 23 | + TextStyle? h2, | ||
| 24 | + TextStyle? h3, | ||
| 25 | + TextStyle? h4, | ||
| 26 | + TextStyle? h5, | ||
| 27 | + TextStyle? h6, | ||
| 28 | + double? hrLineThickness, | ||
| 29 | + Color? hrLineColor, | ||
| 30 | + Color? linkColor, | ||
| 31 | + Color? linkHoverColor, | ||
| 32 | + }) { | ||
| 33 | + ThemeData themeData = switch (brightness) { | ||
| 34 | + Brightness.light => ThemeData.light(), | ||
| 35 | + Brightness.dark => ThemeData.dark(), | ||
| 36 | + }; | ||
| 37 | + final typography = Typography.tall2021.copyWith( | ||
| 38 | + displayLarge: Typography.tall2021.displayLarge?.copyWith(inherit: true), | ||
| 39 | + displayMedium: Typography.tall2021.displayMedium?.copyWith(inherit: true), | ||
| 40 | + displaySmall: Typography.tall2021.displaySmall?.copyWith(inherit: true), | ||
| 41 | + headlineLarge: Typography.tall2021.headlineLarge?.copyWith(inherit: true), | ||
| 42 | + headlineMedium: | ||
| 43 | + Typography.tall2021.headlineMedium?.copyWith(inherit: true), | ||
| 44 | + headlineSmall: Typography.tall2021.headlineSmall?.copyWith(inherit: true), | ||
| 45 | + titleLarge: Typography.tall2021.titleLarge?.copyWith(inherit: true), | ||
| 46 | + titleMedium: Typography.tall2021.titleMedium?.copyWith(inherit: true), | ||
| 47 | + titleSmall: Typography.tall2021.titleSmall?.copyWith(inherit: true), | ||
| 48 | + bodyLarge: Typography.tall2021.bodyLarge?.copyWith(inherit: true), | ||
| 49 | + bodyMedium: Typography.tall2021.bodyMedium?.copyWith(inherit: true), | ||
| 50 | + bodySmall: Typography.tall2021.bodySmall?.copyWith(inherit: true), | ||
| 51 | + labelLarge: Typography.tall2021.labelLarge?.copyWith(inherit: true), | ||
| 52 | + labelMedium: Typography.tall2021.labelMedium?.copyWith(inherit: true), | ||
| 53 | + labelSmall: Typography.tall2021.labelSmall?.copyWith(inherit: true), | ||
| 54 | + ); | ||
| 55 | + themeData = themeData.copyWith( | ||
| 56 | + textTheme: typography, | ||
| 57 | + ); | ||
| 58 | + TextTheme textTheme = themeData.textTheme; | ||
| 59 | + return GptMarkdownThemeData._fromTheme(themeData, textTheme).copyWith( | ||
| 60 | + highlightColor: highlightColor, | ||
| 61 | + h1: h1, | ||
| 62 | + h2: h2, | ||
| 63 | + h3: h3, | ||
| 64 | + h4: h4, | ||
| 65 | + h5: h5, | ||
| 66 | + h6: h6, | ||
| 67 | + hrLineThickness: hrLineThickness, | ||
| 68 | + hrLineColor: hrLineColor, | ||
| 69 | + linkColor: linkColor, | ||
| 70 | + linkHoverColor: linkHoverColor, | ||
| 71 | + ); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + factory GptMarkdownThemeData._fromTheme( | ||
| 75 | + ThemeData theme, TextTheme textTheme) { | ||
| 76 | + return GptMarkdownThemeData._( | ||
| 77 | + highlightColor: theme.colorScheme.onSurfaceVariant.withAlpha(50), | ||
| 78 | + h1: textTheme.headlineLarge, | ||
| 79 | + h2: textTheme.headlineMedium, | ||
| 80 | + h3: textTheme.headlineSmall, | ||
| 81 | + h4: textTheme.titleLarge, | ||
| 82 | + h5: textTheme.titleMedium, | ||
| 83 | + h6: textTheme.titleSmall, | ||
| 84 | + hrLineThickness: 1, | ||
| 85 | + hrLineColor: theme.colorScheme.outline, | ||
| 86 | + linkColor: Colors.blue, | ||
| 87 | + linkHoverColor: Colors.red, | ||
| 14 | ); | 88 | ); |
| 15 | } | 89 | } |
| 16 | 90 | ||
| 17 | Color highlightColor; | 91 | Color highlightColor; |
| 92 | + TextStyle? h1; | ||
| 93 | + TextStyle? h2; | ||
| 94 | + TextStyle? h3; | ||
| 95 | + TextStyle? h4; | ||
| 96 | + TextStyle? h5; | ||
| 97 | + TextStyle? h6; | ||
| 98 | + double hrLineThickness; | ||
| 99 | + Color hrLineColor; | ||
| 100 | + Color linkColor; | ||
| 101 | + Color linkHoverColor; | ||
| 18 | 102 | ||
| 103 | + /// Define default attributes. | ||
| 19 | @override | 104 | @override |
| 20 | - GptMarkdownThemeData copyWith({Color? highlightColor}) { | ||
| 21 | - return GptMarkdownThemeData( | 105 | + GptMarkdownThemeData copyWith({ |
| 106 | + Color? highlightColor, | ||
| 107 | + TextStyle? h1, | ||
| 108 | + TextStyle? h2, | ||
| 109 | + TextStyle? h3, | ||
| 110 | + TextStyle? h4, | ||
| 111 | + TextStyle? h5, | ||
| 112 | + TextStyle? h6, | ||
| 113 | + double? hrLineThickness, | ||
| 114 | + Color? hrLineColor, | ||
| 115 | + Color? linkColor, | ||
| 116 | + Color? linkHoverColor, | ||
| 117 | + }) { | ||
| 118 | + return GptMarkdownThemeData._( | ||
| 22 | highlightColor: highlightColor ?? this.highlightColor, | 119 | highlightColor: highlightColor ?? this.highlightColor, |
| 120 | + h1: h1 ?? this.h1, | ||
| 121 | + h2: h2 ?? this.h2, | ||
| 122 | + h3: h3 ?? this.h3, | ||
| 123 | + h4: h4 ?? this.h4, | ||
| 124 | + h5: h5 ?? this.h5, | ||
| 125 | + h6: h6 ?? this.h6, | ||
| 126 | + hrLineThickness: hrLineThickness ?? this.hrLineThickness, | ||
| 127 | + hrLineColor: hrLineColor ?? this.hrLineColor, | ||
| 128 | + linkColor: linkColor ?? this.linkColor, | ||
| 129 | + linkHoverColor: linkHoverColor ?? this.linkHoverColor, | ||
| 23 | ); | 130 | ); |
| 24 | } | 131 | } |
| 25 | 132 | ||
| @@ -28,9 +135,21 @@ class GptMarkdownThemeData extends ThemeExtension<GptMarkdownThemeData> { | @@ -28,9 +135,21 @@ class GptMarkdownThemeData extends ThemeExtension<GptMarkdownThemeData> { | ||
| 28 | if (other == null) { | 135 | if (other == null) { |
| 29 | return this; | 136 | return this; |
| 30 | } | 137 | } |
| 31 | - return GptMarkdownThemeData( | 138 | + return GptMarkdownThemeData._( |
| 32 | highlightColor: | 139 | highlightColor: |
| 33 | Color.lerp(highlightColor, other.highlightColor, t) ?? highlightColor, | 140 | Color.lerp(highlightColor, other.highlightColor, t) ?? highlightColor, |
| 141 | + h1: TextStyle.lerp(h1, other.h1, t) ?? h1, | ||
| 142 | + h2: TextStyle.lerp(h2, other.h2, t) ?? h2, | ||
| 143 | + h3: TextStyle.lerp(h3, other.h3, t) ?? h3, | ||
| 144 | + h4: TextStyle.lerp(h4, other.h4, t) ?? h4, | ||
| 145 | + h5: TextStyle.lerp(h5, other.h5, t) ?? h5, | ||
| 146 | + h6: TextStyle.lerp(h6, other.h6, t) ?? h6, | ||
| 147 | + hrLineThickness: Tween(begin: hrLineThickness, end: other.hrLineThickness) | ||
| 148 | + .transform(t), | ||
| 149 | + hrLineColor: Color.lerp(hrLineColor, other.hrLineColor, t) ?? hrLineColor, | ||
| 150 | + linkColor: Color.lerp(linkColor, other.linkColor, t) ?? linkColor, | ||
| 151 | + linkHoverColor: | ||
| 152 | + Color.lerp(linkHoverColor, other.linkHoverColor, t) ?? linkHoverColor, | ||
| 34 | ); | 153 | ); |
| 35 | } | 154 | } |
| 36 | } | 155 | } |
| @@ -45,16 +164,17 @@ class GptMarkdownTheme extends InheritedWidget { | @@ -45,16 +164,17 @@ class GptMarkdownTheme extends InheritedWidget { | ||
| 45 | final GptMarkdownThemeData gptThemeData; | 164 | final GptMarkdownThemeData gptThemeData; |
| 46 | 165 | ||
| 47 | static GptMarkdownThemeData of(BuildContext context) { | 166 | static GptMarkdownThemeData of(BuildContext context) { |
| 167 | + var theme = Theme.of(context); | ||
| 48 | final provider = | 168 | final provider = |
| 49 | context.dependOnInheritedWidgetOfExactType<GptMarkdownTheme>(); | 169 | context.dependOnInheritedWidgetOfExactType<GptMarkdownTheme>(); |
| 50 | if (provider != null) { | 170 | if (provider != null) { |
| 51 | return provider.gptThemeData; | 171 | return provider.gptThemeData; |
| 52 | } | 172 | } |
| 53 | - final themeData = Theme.of(context).extension<GptMarkdownThemeData>(); | 173 | + final themeData = theme.extension<GptMarkdownThemeData>(); |
| 54 | if (themeData != null) { | 174 | if (themeData != null) { |
| 55 | return themeData; | 175 | return themeData; |
| 56 | } | 176 | } |
| 57 | - return GptMarkdownThemeData.from(context); | 177 | + return GptMarkdownThemeData._fromTheme(theme, theme.textTheme); |
| 58 | } | 178 | } |
| 59 | 179 | ||
| 60 | @override | 180 | @override |
| 1 | name: gpt_markdown | 1 | name: gpt_markdown |
| 2 | -description: "The purpose of this package is to render the response of ChatGPT into a Flutter app." | ||
| 3 | -version: 0.1.14 | 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.0 | ||
| 4 | homepage: https://github.com/Infinitix-LLC/gpt_markdown | 4 | homepage: https://github.com/Infinitix-LLC/gpt_markdown |
| 5 | 5 | ||
| 6 | environment: | 6 | environment: |
| @@ -10,46 +10,21 @@ environment: | @@ -10,46 +10,21 @@ environment: | ||
| 10 | dependencies: | 10 | dependencies: |
| 11 | flutter: | 11 | flutter: |
| 12 | sdk: flutter | 12 | sdk: flutter |
| 13 | - flutter_math_fork: ^0.7.2 | 13 | + flutter_math_fork: ^0.7.3 |
| 14 | 14 | ||
| 15 | dev_dependencies: | 15 | dev_dependencies: |
| 16 | flutter_test: | 16 | flutter_test: |
| 17 | sdk: flutter | 17 | sdk: flutter |
| 18 | flutter_lints: ^4.0.0 | 18 | flutter_lints: ^4.0.0 |
| 19 | 19 | ||
| 20 | -# For information on the generic Dart part of this file, see the | ||
| 21 | -# following page: https://dart.dev/tools/pub/pubspec | ||
| 22 | - | ||
| 23 | -# The following section is specific to Flutter packages. | ||
| 24 | flutter: | 20 | flutter: |
| 25 | 21 | ||
| 26 | - # To add assets to your package, add an assets section, like this: | ||
| 27 | - # assets: | ||
| 28 | - # - images/a_dot_burr.jpeg | ||
| 29 | - # - images/a_dot_ham.jpeg | ||
| 30 | - # | ||
| 31 | - # For details regarding assets in packages, see | ||
| 32 | - # https://flutter.dev/assets-and-images/#from-packages | ||
| 33 | - # | ||
| 34 | - # An image asset can refer to one or more resolution-specific "variants", see | ||
| 35 | - # https://flutter.dev/assets-and-images/#resolution-aware | ||
| 36 | - | ||
| 37 | - # To add custom fonts to your package, add a fonts section here, | ||
| 38 | - # in this "flutter" section. Each entry in this list should have a | ||
| 39 | - # "family" key with the font family name, and a "fonts" key with a | ||
| 40 | - # list giving the asset and other descriptors for the font. For | ||
| 41 | - # example: | ||
| 42 | - # fonts: | ||
| 43 | - # - family: Schyler | ||
| 44 | - # fonts: | ||
| 45 | - # - asset: fonts/Schyler-Regular.ttf | ||
| 46 | - # - asset: fonts/Schyler-Italic.ttf | ||
| 47 | - # style: italic | ||
| 48 | - # - family: Trajan Pro | ||
| 49 | - # fonts: | ||
| 50 | - # - asset: fonts/TrajanPro.ttf | ||
| 51 | - # - asset: fonts/TrajanPro_Bold.ttf | ||
| 52 | - # weight: 700 | ||
| 53 | - # | ||
| 54 | - # For details regarding fonts in packages, see | ||
| 55 | - # https://flutter.dev/custom-fonts/#from-packages | 22 | +topics: |
| 23 | + - markdown | ||
| 24 | + - latex | ||
| 25 | + - selectable | ||
| 26 | + - math | ||
| 27 | + - chatgpt | ||
| 28 | + - gemini | ||
| 29 | + - ai | ||
| 30 | + - gpt |
-
Please register or login to post a comment