refactor: remove flutter_highlight dependency and enhance code block styling

- Removed flutter_highlight as a dependency to streamline the codebase.
- Updated CodeBlockHeader to improve styling based on the current theme, enhancing visual consistency.
- Adjusted code block background and text colors to match GitHub/Atom themes for better readability.
- Refactored markdown processing logic to eliminate reliance on deprecated highlight features, improving maintainability.
This commit is contained in:
cogwheel0
2025-10-04 23:31:42 +05:30
parent a4319a1d9e
commit f4e4e86c38
5 changed files with 77 additions and 110 deletions

View File

@@ -14,16 +14,30 @@ class CodeBlockHeader extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = context.conduitTheme; final theme = context.conduitTheme;
final label = language.isEmpty ? 'code' : language; final materialTheme = Theme.of(context);
final isDark = materialTheme.brightness == Brightness.dark;
final label = language.isEmpty ? 'plaintext' : language;
// Match GitHub/Atom theme colors
final backgroundColor = isDark
? const Color(0xFF282c34) // Atom One Dark header
: const Color(0xFFf6f8fa); // GitHub light header
final textColor = isDark
? const Color(0xFF9da5b4) // Muted text for dark
: const Color(0xFF57606a); // GitHub gray for light
return Container( return Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: Spacing.sm, horizontal: Spacing.md,
vertical: Spacing.xs, vertical: Spacing.xs,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: theme.surfaceContainer.withValues(alpha: 0.35), color: backgroundColor,
borderRadius: const BorderRadius.vertical( border: Border(
top: Radius.circular(AppBorderRadius.sm), bottom: BorderSide(
color: theme.cardBorder.withValues(alpha: 0.15),
width: 1,
),
), ),
), ),
child: Row( child: Row(
@@ -31,16 +45,26 @@ class CodeBlockHeader extends StatelessWidget {
Text( Text(
label, label,
style: AppTypography.codeStyle.copyWith( style: AppTypography.codeStyle.copyWith(
color: theme.textSecondary, color: textColor,
fontWeight: FontWeight.w600, fontSize: 12,
fontWeight: FontWeight.w500,
), ),
), ),
const Spacer(), const Spacer(),
IconButton( Material(
icon: const Icon(Icons.copy_rounded, size: 18), color: Colors.transparent,
color: theme.iconPrimary, child: InkWell(
tooltip: 'Copy code', onTap: onCopy,
onPressed: onCopy, borderRadius: BorderRadius.circular(4),
child: Padding(
padding: const EdgeInsets.all(6),
child: Icon(
Icons.content_copy_rounded,
size: 16,
color: textColor,
),
),
),
), ),
], ],
), ),

View File

@@ -6,9 +6,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_highlight/flutter_highlight.dart';
import 'package:flutter_highlight/themes/a11y-dark.dart';
import 'package:flutter_highlight/themes/a11y-light.dart';
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart'; import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
import 'package:flutter_math_fork/flutter_math.dart'; import 'package:flutter_math_fork/flutter_math.dart';
import 'package:markdown/markdown.dart' as md; import 'package:markdown/markdown.dart' as md;
@@ -283,91 +280,54 @@ class _CodeBlockBuilder extends MarkdownElementBuilder {
final isDark = Theme.of(context).brightness == Brightness.dark; final isDark = Theme.of(context).brightness == Brightness.dark;
final code = element.textContent; final code = element.textContent;
final language = element.attributes['class']?.replaceFirst('language-', '') ?? 'plaintext'; final language = element.attributes['class']?.replaceFirst('language-', '') ?? 'plaintext';
final highlightTheme = _getCodeHighlightTheme(theme, isDark: isDark);
final normalizedLanguage = language.trim().isEmpty ? 'plaintext' : language.trim(); final normalizedLanguage = language.trim().isEmpty ? 'plaintext' : language.trim();
final highlight = HighlightView( // Match GitHub/Atom theme colors for code block container
code, final codeBackground = isDark
language: normalizedLanguage == 'plaintext' ? null : normalizedLanguage, ? const Color(0xFF282c34) // Atom One Dark background
theme: highlightTheme, : const Color(0xFFfafbfc); // GitHub light background
textStyle: AppTypography.codeStyle.copyWith(color: theme.codeText),
padding: EdgeInsets.zero,
);
final codeBackground = theme.surfaceContainer.withValues(alpha: 0.55); return Container(
final borderColor = theme.cardBorder.withValues(alpha: 0.25); margin: const EdgeInsets.symmetric(vertical: Spacing.sm),
decoration: BoxDecoration(
return LayoutBuilder( color: codeBackground,
builder: (context, constraints) { borderRadius: BorderRadius.circular(6),
final width = constraints.maxWidth.isFinite ),
? constraints.maxWidth clipBehavior: Clip.antiAlias,
: MediaQuery.sizeOf(context).width; child: Column(
mainAxisSize: MainAxisSize.min,
return Container( crossAxisAlignment: CrossAxisAlignment.stretch,
margin: const EdgeInsets.symmetric(vertical: Spacing.xs), children: [
decoration: BoxDecoration( CodeBlockHeader(
color: codeBackground, language: normalizedLanguage,
borderRadius: BorderRadius.circular(AppBorderRadius.sm), onCopy: () async {
border: Border.all(color: borderColor, width: BorderWidth.micro), await Clipboard.setData(ClipboardData(text: code));
if (!context.mounted) {
return;
}
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Code copied to clipboard.'),
),
);
},
), ),
child: ClipRect( SingleChildScrollView(
child: Column( scrollDirection: Axis.horizontal,
mainAxisSize: MainAxisSize.min, padding: const EdgeInsets.all(Spacing.md),
crossAxisAlignment: CrossAxisAlignment.stretch, child: SelectableText(
children: [ code,
CodeBlockHeader( style: AppTypography.codeStyle.copyWith(
language: normalizedLanguage, color: theme.codeText,
onCopy: () async { fontFamily: AppTypography.monospaceFontFamily,
await Clipboard.setData(ClipboardData(text: code)); ),
if (!context.mounted) {
return;
}
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Code copied to clipboard.'),
),
);
},
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: IntrinsicWidth(
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: width),
child: Padding(
padding: const EdgeInsets.all(Spacing.sm),
child: highlight,
),
),
),
),
],
), ),
), ),
); ],
}, ),
); );
} }
Map<String, TextStyle> _getCodeHighlightTheme(
ConduitThemeExtension theme, {
required bool isDark,
}) {
final baseTheme = isDark ? a11yDarkTheme : a11yLightTheme;
final codeStyle = AppTypography.codeStyle.copyWith(color: theme.codeText);
return {
for (final entry in baseTheme.entries)
entry.key: entry.value.copyWith(
color: entry.value.color ?? theme.codeText,
fontFamily: AppTypography.monospaceFontFamily,
fontSize: codeStyle.fontSize,
height: codeStyle.height,
),
};
}
} }
// Custom image builder // Custom image builder

View File

@@ -1,5 +1,5 @@
/// Utility helpers for normalising markdown content before handing it to /// Utility helpers for normalising markdown content before handing it to
/// [GptMarkdown]. The goal is to keep streaming responsive while smoothing /// [ConduitMarkdown]. The goal is to keep streaming responsive while smoothing
/// out troublesome edge-cases (e.g. nested fences inside lists). /// out troublesome edge-cases (e.g. nested fences inside lists).
class ConduitMarkdownPreprocessor { class ConduitMarkdownPreprocessor {
const ConduitMarkdownPreprocessor._(); const ConduitMarkdownPreprocessor._();

View File

@@ -430,14 +430,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.4.1" version: "3.4.1"
flutter_highlight:
dependency: "direct main"
description:
name: flutter_highlight
sha256: "7b96333867aa07e122e245c033b8ad622e4e3a42a1a2372cbb098a2541d8782c"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -621,14 +613,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.3.2"
highlight:
dependency: transitive
description:
name: highlight
sha256: "5353a83ffe3e3eca7df0abfb72dcf3fa66cc56b953728e7113ad4ad88497cf21"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
hive_ce: hive_ce:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -31,7 +31,6 @@ dependencies:
# UI Components - Markdown Rendering # UI Components - Markdown Rendering
cached_network_image: ^3.3.1 cached_network_image: ^3.3.1
flutter_highlight: ^0.7.0
flutter_markdown_plus: ^1.0.5 flutter_markdown_plus: ^1.0.5
markdown: ^7.3.0 markdown: ^7.3.0
webview_flutter: ^4.7.0 webview_flutter: ^4.7.0