mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-10-31 01:54:37 +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, |               chatId: state.view.id, | ||||||
|               limit: Int64(10), |               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 { |           startLoadingPrevMessage: () async { | ||||||
|             Int64? beforeMessageId; |             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_ai_message_bloc.dart'; | ||||||
| import 'package:appflowy/plugins/ai_chat/application/chat_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/plugins/ai_chat/presentation/chat_loading.dart'; | ||||||
|  | import 'package:appflowy/util/theme_extension.dart'; | ||||||
| import 'package:easy_localization/easy_localization.dart'; | import 'package:easy_localization/easy_localization.dart'; | ||||||
| import 'package:fixnum/fixnum.dart'; | import 'package:fixnum/fixnum.dart'; | ||||||
| import 'package:flowy_infra/theme_extension.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:flutter_chat_types/flutter_chat_types.dart'; | ||||||
| import 'package:markdown_widget/markdown_widget.dart'; | import 'package:markdown_widget/markdown_widget.dart'; | ||||||
| 
 | 
 | ||||||
|  | import 'selectable_highlight.dart'; | ||||||
|  | 
 | ||||||
| class ChatAITextMessageWidget extends StatelessWidget { | class ChatAITextMessageWidget extends StatelessWidget { | ||||||
|   const ChatAITextMessageWidget({ |   const ChatAITextMessageWidget({ | ||||||
|     super.key, |     super.key, | ||||||
| @ -132,16 +135,28 @@ class ChatAITextMessageWidget extends StatelessWidget { | |||||||
|           ), |           ), | ||||||
|         ), |         ), | ||||||
|         PreConfig( |         PreConfig( | ||||||
|           padding: const EdgeInsets.all(14), |           builder: (code, language) { | ||||||
|           decoration: BoxDecoration( |             return ConstrainedBox( | ||||||
|             color: Theme.of(context) |               constraints: const BoxConstraints( | ||||||
|                 .colorScheme |                 minWidth: 800, | ||||||
|                 .surfaceContainerHighest |               ), | ||||||
|                 .withOpacity(0.6), |               child: ClipRRect( | ||||||
|             borderRadius: const BorderRadius.all( |                 borderRadius: const BorderRadius.all(Radius.circular(6.0)), | ||||||
|               Radius.circular(8.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( |         PConfig( | ||||||
|           textStyle: TextStyle( |           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 { | class ChatH1Config extends HeadingConfig { | ||||||
|   const ChatH1Config({ |   const ChatH1Config({ | ||||||
|     this.style = const TextStyle( |     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 |     source: git | ||||||
|     version: "1.0.2" |     version: "1.0.2" | ||||||
|   flutter_highlight: |   flutter_highlight: | ||||||
|     dependency: transitive |     dependency: "direct main" | ||||||
|     description: |     description: | ||||||
|       name: flutter_highlight |       name: flutter_highlight | ||||||
|       sha256: "7b96333867aa07e122e245c033b8ad622e4e3a42a1a2372cbb098a2541d8782c" |       sha256: "7b96333867aa07e122e245c033b8ad622e4e3a42a1a2372cbb098a2541d8782c" | ||||||
|  | |||||||
| @ -149,6 +149,7 @@ dependencies: | |||||||
| 
 | 
 | ||||||
|   # BitsDojo Window for Windows |   # BitsDojo Window for Windows | ||||||
|   bitsdojo_window: ^0.1.6 |   bitsdojo_window: ^0.1.6 | ||||||
|  |   flutter_highlight: ^0.7.0 | ||||||
| 
 | 
 | ||||||
| dev_dependencies: | dev_dependencies: | ||||||
|   flutter_lints: ^3.0.1 |   flutter_lints: ^3.0.1 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Nathan.fooo
						Nathan.fooo