mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-10-31 10:03:18 +00:00 
			
		
		
		
	chore: highlight code block (#5540)
This commit is contained in:
		
							parent
							
								
									1d23e28eaf
								
							
						
					
					
						commit
						ce1a6e4fca
					
				| @ -67,7 +67,19 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> { | ||||
|               chatId: state.view.id, | ||||
|               limit: Int64(10), | ||||
|             ); | ||||
|             ChatEventLoadNextMessage(payload).send(); | ||||
|             ChatEventLoadNextMessage(payload).send().then( | ||||
|               (result) { | ||||
|                 result.fold((list) { | ||||
|                   if (!isClosed) { | ||||
|                     final messages = | ||||
|                         list.messages.map(_createTextMessage).toList(); | ||||
|                     add(ChatEvent.didLoadLatestMessages(messages)); | ||||
|                   } | ||||
|                 }, (err) { | ||||
|                   Log.error("Failed to load messages: $err"); | ||||
|                 }); | ||||
|               }, | ||||
|             ); | ||||
|           }, | ||||
|           startLoadingPrevMessage: () async { | ||||
|             Int64? beforeMessageId; | ||||
|  | ||||
| @ -2,6 +2,7 @@ import 'package:appflowy/generated/locale_keys.g.dart'; | ||||
| import 'package:appflowy/plugins/ai_chat/application/chat_ai_message_bloc.dart'; | ||||
| import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart'; | ||||
| import 'package:appflowy/plugins/ai_chat/presentation/chat_loading.dart'; | ||||
| import 'package:appflowy/util/theme_extension.dart'; | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:fixnum/fixnum.dart'; | ||||
| import 'package:flowy_infra/theme_extension.dart'; | ||||
| @ -13,6 +14,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; | ||||
| import 'package:flutter_chat_types/flutter_chat_types.dart'; | ||||
| import 'package:markdown_widget/markdown_widget.dart'; | ||||
| 
 | ||||
| import 'selectable_highlight.dart'; | ||||
| 
 | ||||
| class ChatAITextMessageWidget extends StatelessWidget { | ||||
|   const ChatAITextMessageWidget({ | ||||
|     super.key, | ||||
| @ -132,16 +135,28 @@ class ChatAITextMessageWidget extends StatelessWidget { | ||||
|           ), | ||||
|         ), | ||||
|         PreConfig( | ||||
|           padding: const EdgeInsets.all(14), | ||||
|           decoration: BoxDecoration( | ||||
|             color: Theme.of(context) | ||||
|                 .colorScheme | ||||
|                 .surfaceContainerHighest | ||||
|                 .withOpacity(0.6), | ||||
|             borderRadius: const BorderRadius.all( | ||||
|               Radius.circular(8.0), | ||||
|             ), | ||||
|           ), | ||||
|           builder: (code, language) { | ||||
|             return ConstrainedBox( | ||||
|               constraints: const BoxConstraints( | ||||
|                 minWidth: 800, | ||||
|               ), | ||||
|               child: ClipRRect( | ||||
|                 borderRadius: const BorderRadius.all(Radius.circular(6.0)), | ||||
|                 child: SelectableHighlightView( | ||||
|                   code, | ||||
|                   language: language, | ||||
|                   theme: getHightlineTheme(context), | ||||
|                   padding: const EdgeInsets.all(14), | ||||
|                   textStyle: TextStyle( | ||||
|                     color: AFThemeExtension.of(context).textColor, | ||||
|                     fontSize: 14, | ||||
|                     fontWeight: FontWeight.bold, | ||||
|                     height: 1.5, | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ); | ||||
|           }, | ||||
|         ), | ||||
|         PConfig( | ||||
|           textStyle: TextStyle( | ||||
| @ -168,6 +183,51 @@ class ChatAITextMessageWidget extends StatelessWidget { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| Map<String, TextStyle> getHightlineTheme(BuildContext context) { | ||||
|   return { | ||||
|     'root': TextStyle( | ||||
|       color: const Color(0xffabb2bf), | ||||
|       backgroundColor: | ||||
|           Theme.of(context).isLightMode ? Colors.white : Colors.black38, | ||||
|     ), | ||||
|     'comment': | ||||
|         const TextStyle(color: Color(0xff5c6370), fontStyle: FontStyle.italic), | ||||
|     'quote': | ||||
|         const TextStyle(color: Color(0xff5c6370), fontStyle: FontStyle.italic), | ||||
|     'doctag': const TextStyle(color: Color(0xffc678dd)), | ||||
|     'keyword': const TextStyle(color: Color(0xffc678dd)), | ||||
|     'formula': const TextStyle(color: Color(0xffc678dd)), | ||||
|     'section': const TextStyle(color: Color(0xffe06c75)), | ||||
|     'name': const TextStyle(color: Color(0xffe06c75)), | ||||
|     'selector-tag': const TextStyle(color: Color(0xffe06c75)), | ||||
|     'deletion': const TextStyle(color: Color(0xffe06c75)), | ||||
|     'subst': const TextStyle(color: Color(0xffe06c75)), | ||||
|     'literal': const TextStyle(color: Color(0xff56b6c2)), | ||||
|     'string': const TextStyle(color: Color(0xff98c379)), | ||||
|     'regexp': const TextStyle(color: Color(0xff98c379)), | ||||
|     'addition': const TextStyle(color: Color(0xff98c379)), | ||||
|     'attribute': const TextStyle(color: Color(0xff98c379)), | ||||
|     'meta-string': const TextStyle(color: Color(0xff98c379)), | ||||
|     'built_in': const TextStyle(color: Color(0xffe6c07b)), | ||||
|     'attr': const TextStyle(color: Color(0xffd19a66)), | ||||
|     'variable': const TextStyle(color: Color(0xffd19a66)), | ||||
|     'template-variable': const TextStyle(color: Color(0xffd19a66)), | ||||
|     'type': const TextStyle(color: Color(0xffd19a66)), | ||||
|     'selector-class': const TextStyle(color: Color(0xffd19a66)), | ||||
|     'selector-attr': const TextStyle(color: Color(0xffd19a66)), | ||||
|     'selector-pseudo': const TextStyle(color: Color(0xffd19a66)), | ||||
|     'number': const TextStyle(color: Color(0xffd19a66)), | ||||
|     'symbol': const TextStyle(color: Color(0xff61aeee)), | ||||
|     'bullet': const TextStyle(color: Color(0xff61aeee)), | ||||
|     'link': const TextStyle(color: Color(0xff61aeee)), | ||||
|     'meta': const TextStyle(color: Color(0xff61aeee)), | ||||
|     'selector-id': const TextStyle(color: Color(0xff61aeee)), | ||||
|     'title': const TextStyle(color: Color(0xff61aeee)), | ||||
|     'emphasis': const TextStyle(fontStyle: FontStyle.italic), | ||||
|     'strong': const TextStyle(fontWeight: FontWeight.bold), | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| class ChatH1Config extends HeadingConfig { | ||||
|   const ChatH1Config({ | ||||
|     this.style = const TextStyle( | ||||
|  | ||||
| @ -0,0 +1,92 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:highlight/highlight.dart'; | ||||
| 
 | ||||
| /// Highlight Flutter Widget | ||||
| class SelectableHighlightView extends StatelessWidget { | ||||
|   SelectableHighlightView( | ||||
|     String input, { | ||||
|     super.key, | ||||
|     this.language, | ||||
|     this.theme = const {}, | ||||
|     this.padding, | ||||
|     this.textStyle, | ||||
|     int tabSize = 8, | ||||
|   }) : source = input.replaceAll('\t', ' ' * tabSize); | ||||
| 
 | ||||
|   /// The original code to be highlighted | ||||
|   final String source; | ||||
| 
 | ||||
|   /// Highlight language | ||||
|   /// | ||||
|   /// It is recommended to give it a value for performance | ||||
|   /// | ||||
|   /// [All available languages](https://github.com/pd4d10/highlight/tree/master/highlight/lib/languages) | ||||
|   final String? language; | ||||
| 
 | ||||
|   /// Highlight theme | ||||
|   /// | ||||
|   /// [All available themes](https://github.com/pd4d10/highlight/blob/master/flutter_highlight/lib/themes) | ||||
|   final Map<String, TextStyle> theme; | ||||
| 
 | ||||
|   /// Padding | ||||
|   final EdgeInsetsGeometry? padding; | ||||
| 
 | ||||
|   /// Text styles | ||||
|   /// | ||||
|   /// Specify text styles such as font family and font size | ||||
|   final TextStyle? textStyle; | ||||
| 
 | ||||
|   List<TextSpan> _convert(List<Node> nodes) { | ||||
|     final List<TextSpan> spans = []; | ||||
|     var currentSpans = spans; | ||||
|     final List<List<TextSpan>> stack = []; | ||||
| 
 | ||||
|     // ignore: always_declare_return_types | ||||
|     traverse(Node node) { | ||||
|       if (node.value != null) { | ||||
|         currentSpans.add( | ||||
|           node.className == null | ||||
|               ? TextSpan(text: node.value) | ||||
|               : TextSpan(text: node.value, style: theme[node.className!]), | ||||
|         ); | ||||
|       } else if (node.children != null) { | ||||
|         final List<TextSpan> tmp = []; | ||||
|         currentSpans | ||||
|             .add(TextSpan(children: tmp, style: theme[node.className!])); | ||||
|         stack.add(currentSpans); | ||||
|         currentSpans = tmp; | ||||
| 
 | ||||
|         for (final n in node.children!) { | ||||
|           traverse(n); | ||||
|           if (n == node.children!.last) { | ||||
|             currentSpans = stack.isEmpty ? spans : stack.removeLast(); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     for (final node in nodes) { | ||||
|       traverse(node); | ||||
|     } | ||||
| 
 | ||||
|     return spans; | ||||
|   } | ||||
| 
 | ||||
|   static const _rootKey = 'root'; | ||||
|   static const _defaultBackgroundColor = Color(0xffffffff); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Container( | ||||
|       color: theme[_rootKey]?.backgroundColor ?? _defaultBackgroundColor, | ||||
|       padding: padding, | ||||
|       child: SelectableText.rich( | ||||
|         TextSpan( | ||||
|           style: textStyle, | ||||
|           children: | ||||
|               _convert(highlight.parse(source, language: language).nodes!), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -681,7 +681,7 @@ packages: | ||||
|     source: git | ||||
|     version: "1.0.2" | ||||
|   flutter_highlight: | ||||
|     dependency: transitive | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: flutter_highlight | ||||
|       sha256: "7b96333867aa07e122e245c033b8ad622e4e3a42a1a2372cbb098a2541d8782c" | ||||
|  | ||||
| @ -149,6 +149,7 @@ dependencies: | ||||
| 
 | ||||
|   # BitsDojo Window for Windows | ||||
|   bitsdojo_window: ^0.1.6 | ||||
|   flutter_highlight: ^0.7.0 | ||||
| 
 | ||||
| dev_dependencies: | ||||
|   flutter_lints: ^3.0.1 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Nathan.fooo
						Nathan.fooo