chore: highlight code block (#5540)

This commit is contained in:
Nathan.fooo 2024-06-14 14:53:53 +08:00 committed by GitHub
parent 1d23e28eaf
commit ce1a6e4fca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 177 additions and 12 deletions

View File

@ -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;

View File

@ -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(

View File

@ -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!),
),
),
);
}
}

View File

@ -681,7 +681,7 @@ packages:
source: git
version: "1.0.2"
flutter_highlight:
dependency: transitive
dependency: "direct main"
description:
name: flutter_highlight
sha256: "7b96333867aa07e122e245c033b8ad622e4e3a42a1a2372cbb098a2541d8782c"

View File

@ -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