diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index ae4997e40..ddb1a394f 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -2,13 +2,13 @@ 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'; +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'; @@ -137,6 +137,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, @@ -334,33 +347,60 @@ 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!'); + } + final controller = isInline ? null : ScrollController(); + 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: isInline + ? Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: Text.rich( + TextSpan( + children: [_renderCodeBlockNode(element)], + ), + selectionColor: hightlightTextColor.withAlpha(128), + ), + ) + : RawScrollbar( + thumbVisibility: true, + trackVisibility: true, + controller: controller, + thumbColor: hightlightTextColor, + trackColor: hightlightTextColor.withAlpha(128), + thickness: 8, + child: SingleChildScrollView( + controller: controller, + scrollDirection: Axis.horizontal, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Text.rich( + TextSpan( + children: [_renderCodeBlockNode(element)], + ), + selectionColor: hightlightTextColor.withAlpha(212), + ), + ), + ), + ), ), ); case 'img': 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 9f3e7e86f..5e90afcb5 100644 --- a/lib/utils/error_reporter.dart +++ b/lib/utils/error_reporter.dart @@ -1,8 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_highlighter/flutter_highlighter.dart'; -import 'package:flutter_highlighter/themes/shades-of-purple.dart'; import 'package:matrix/matrix.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -40,10 +38,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 5ff83a837..965667782 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -483,14 +483,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_linkify: dependency: "direct main" description: @@ -799,14 +791,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 cc38297df..e133ad76e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,7 +30,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: @@ -45,6 +44,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