diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index b8122a5b4..89d5fad05 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -2,9 +2,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; -import 'package:flutter_highlighter/flutter_highlighter.dart'; -import 'package:flutter_highlighter/themes/shades-of-purple.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; +import 'package:highlight/highlight.dart' show highlight; import 'package:html/dom.dart' as dom; import 'package:html/parser.dart' as parser; import 'package:matrix/matrix.dart'; @@ -23,6 +22,7 @@ import 'package:fluffychat/pangea/toolbar/reading_assistance/token_emoji_button. import 'package:fluffychat/pangea/toolbar/reading_assistance/token_rendering_util.dart'; import 'package:fluffychat/pangea/toolbar/reading_assistance/tokens_util.dart'; import 'package:fluffychat/pangea/toolbar/reading_assistance/underline_text_widget.dart'; +import 'package:fluffychat/utils/code_highlight_theme.dart'; import 'package:fluffychat/utils/event_checkbox_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -368,6 +368,19 @@ class HtmlMessage extends StatelessWidget { ]; } + InlineSpan _renderCodeBlockNode(dom.Node node) { + if (node is! dom.Element) { + return TextSpan(text: node.text); + } + final style = atomOneDarkTheme[node.className.split('-').last] ?? + atomOneDarkTheme['root']; + + return TextSpan( + children: node.nodes.map(_renderCodeBlockNode).toList(), + style: style, + ); + } + /// Transforms a Node to an InlineSpan. InlineSpan _renderHtml( dom.Node node, @@ -775,31 +788,40 @@ class HtmlMessage extends StatelessWidget { ); case 'code': final isInline = node.parent?.localName != 'pre'; + final lang = node.className + .split(' ') + .singleWhereOrNull( + (className) => className.startsWith('language-'), + ) + ?.split('language-') + .last ?? + 'md'; + final highlightedHtml = + highlight.parse(node.text, language: lang).toHtml(); + final element = parser.parse(highlightedHtml).body; + if (element == null) { + return const TextSpan(text: 'Unable to render code block!'); + } + return WidgetSpan( child: Material( - clipBehavior: Clip.hardEdge, - borderRadius: BorderRadius.circular(4), - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: HighlightView( - node.text, - language: node.className - .split(' ') - .singleWhereOrNull( - (className) => className.startsWith('language-'), - ) - ?.split('language-') - .last ?? - 'md', - theme: shadesOfPurpleTheme, - padding: EdgeInsets.symmetric( - horizontal: 8, - vertical: isInline ? 0 : 8, - ), - textStyle: TextStyle( - fontSize: fontSize, - fontFamily: 'RobotoMono', + color: atomOneBackgroundColor, + shape: RoundedRectangleBorder( + side: const BorderSide(color: hightlightTextColor), + borderRadius: BorderRadius.circular(4), + ), + child: Padding( + padding: isInline + ? const EdgeInsets.symmetric(horizontal: 4.0) + : const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 4.0, + ), + child: Text.rich( + TextSpan( + children: [_renderCodeBlockNode(element)], ), + selectionColor: hightlightTextColor.withAlpha(128), ), ), ), diff --git a/lib/utils/code_highlight_theme.dart b/lib/utils/code_highlight_theme.dart new file mode 100644 index 000000000..ac3a19144 --- /dev/null +++ b/lib/utils/code_highlight_theme.dart @@ -0,0 +1,40 @@ +import 'package:flutter/widgets.dart'; + +const hightlightTextColor = Color(0xffabb2bf); +const atomOneBackgroundColor = Color(0xff282c34); +const atomOneDarkTheme = { + 'root': TextStyle(color: hightlightTextColor), + 'comment': TextStyle(color: Color(0xff5c6370), fontStyle: FontStyle.italic), + 'quote': TextStyle(color: Color(0xff5c6370), fontStyle: FontStyle.italic), + 'doctag': TextStyle(color: Color(0xffc678dd)), + 'keyword': TextStyle(color: Color(0xffc678dd)), + 'formula': TextStyle(color: Color(0xffc678dd)), + 'section': TextStyle(color: Color(0xffe06c75)), + 'name': TextStyle(color: Color(0xffe06c75)), + 'selector-tag': TextStyle(color: Color(0xffe06c75)), + 'deletion': TextStyle(color: Color(0xffe06c75)), + 'subst': TextStyle(color: Color(0xffe06c75)), + 'literal': TextStyle(color: Color(0xff56b6c2)), + 'string': TextStyle(color: Color(0xff98c379)), + 'regexp': TextStyle(color: Color(0xff98c379)), + 'addition': TextStyle(color: Color(0xff98c379)), + 'attribute': TextStyle(color: Color(0xff98c379)), + 'meta-string': TextStyle(color: Color(0xff98c379)), + 'built_in': TextStyle(color: Color(0xffe6c07b)), + 'attr': TextStyle(color: Color(0xffd19a66)), + 'variable': TextStyle(color: Color(0xffd19a66)), + 'template-variable': TextStyle(color: Color(0xffd19a66)), + 'type': TextStyle(color: Color(0xffd19a66)), + 'selector-class': TextStyle(color: Color(0xffd19a66)), + 'selector-attr': TextStyle(color: Color(0xffd19a66)), + 'selector-pseudo': TextStyle(color: Color(0xffd19a66)), + 'number': TextStyle(color: Color(0xffd19a66)), + 'symbol': TextStyle(color: Color(0xff61aeee)), + 'bullet': TextStyle(color: Color(0xff61aeee)), + 'link': TextStyle(color: Color(0xff61aeee)), + 'meta': TextStyle(color: Color(0xff61aeee)), + 'selector-id': TextStyle(color: Color(0xff61aeee)), + 'title': TextStyle(color: Color(0xff61aeee)), + 'emphasis': TextStyle(fontStyle: FontStyle.italic), + 'strong': TextStyle(fontWeight: FontWeight.bold), +}; diff --git a/lib/utils/error_reporter.dart b/lib/utils/error_reporter.dart index 7eec3d480..15d5c1097 100644 --- a/lib/utils/error_reporter.dart +++ b/lib/utils/error_reporter.dart @@ -62,10 +62,12 @@ class ErrorReporter { // height: 256, // width: 256, // child: SingleChildScrollView( - // child: HighlightView( + // child: Text( // text, - // language: 'sh', - // theme: shadesOfPurpleTheme, + // style: const TextStyle( + // fontSize: 14, + // fontFamily: 'RobotoMono', + // ), // ), // ), // ), diff --git a/pubspec.lock b/pubspec.lock index 94b3860bc..17727f667 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -740,14 +740,6 @@ packages: url: "https://pub.dev" source: hosted version: "9.1.0" - flutter_highlighter: - dependency: "direct main" - description: - name: flutter_highlighter - sha256: "93173afd47a9ada53f3176371755e7ea4a1065362763976d06d6adfb4d946e10" - url: "https://pub.dev" - source: hosted - version: "0.1.1" flutter_html: dependency: "direct main" description: @@ -1120,14 +1112,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.0" - highlighter: - dependency: transitive + highlight: + dependency: "direct main" description: - name: highlighter - sha256: "92180c72b9da8758e1acf39a45aa305a97dcfe2fdc8f3d1d2947c23f2772bfbc" + name: highlight + sha256: "5353a83ffe3e3eca7df0abfb72dcf3fa66cc56b953728e7113ad4ad88497cf21" url: "https://pub.dev" source: hosted - version: "0.1.1" + version: "0.7.0" html: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 835079c44..b61ada8f0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,7 +36,6 @@ dependencies: flutter: sdk: flutter flutter_foreground_task: ^9.1.0 - flutter_highlighter: ^0.1.1 flutter_linkify: ^6.0.0 flutter_local_notifications: ^19.5.0 flutter_localizations: @@ -51,6 +50,7 @@ dependencies: geolocator: ^14.0.2 go_router: ^17.0.0 handy_window: ^0.4.0 + highlight: ^0.7.0 html: ^0.15.4 http: ^1.6.0 image: ^4.1.7