fix #10 : add support for reasoning tokens and fallback to plain text for unsupported syntax highlighting languages
fix #10 : add support for reasoning tokens and fallback to plain text for unsupported syntax highlighting languages
abdulmominsakib committed
May 23, 2026 at 20:36 UTC
51b319c281db4f0f4c081b6dd3502f0a4bf053b7
3 files changed
+58
-30
lib/core/providers/highlighter_provider.dart
+3
-3
@@ -1,7 +1,7 @@
1
import 'package:flutter_riverpod/flutter_riverpod.dart';
2
import 'package:syntax_highlight/syntax_highlight.dart';
3
4
-const _languages = [
4
+const supportedHighlighterLanguages = {
5
'css',
6
'dart',
7
'go',
@@ -16,7 +16,7 @@ const _languages = [
16
'swift',
17
'typescript',
18
'yaml',
19
-];
19
+};
20
21
final highlighterThemesProvider = FutureProvider<HighlighterThemes>((ref) {
22
return HighlighterThemes.load();
@@ -36,5 +36,5 @@ class HighlighterThemes {
36
}
37
38
Future<void> initializeHighlighter() async {
39
- await Highlighter.initialize(_languages);
39
+ await Highlighter.initialize(supportedHighlighterLanguages.toList());
40
}
lib/features/chat/data/chat_service.dart
+17
-6
@@ -678,12 +678,23 @@ class OllamaChatService implements ChatService {
678
if (line.isNotEmpty) {
679
try {
680
final json = jsonDecode(line) as Map<String, dynamic>;
681
- final content = json['message']?['content'];
682
- if (content != null && content is String) {
683
- yield ChatResponse(
684
- type: ChatResponseType.message,
685
- content: content,
686
- );
681
+ final message = json['message'];
682
+ if (message != null && message is Map<String, dynamic>) {
683
+ final content = message['content'] as String?;
684
+ final thinking = message['thinking'] as String?;
685
+
686
+ if (thinking != null && thinking.isNotEmpty) {
687
+ yield ChatResponse(
688
+ type: ChatResponseType.reasoning,
689
+ reasoningContent: thinking,
690
+ );
691
+ }
692
+ if (content != null && content.isNotEmpty) {
693
+ yield ChatResponse(
694
+ type: ChatResponseType.message,
695
+ content: content,
696
+ );
697
+ }
698
}
699
if (json['done'] == true) {
700
yield const ChatResponse(type: ChatResponseType.done);
lib/features/chat/views/components/code_block.dart
+38
-21
@@ -131,27 +131,44 @@ class CodeBlock extends ConsumerWidget {
131
),
132
themes.when(
133
data: (themesData) {
134
- final loadedTheme = isDark ? themesData.dark : themesData.light;
135
- final highlighter = Highlighter(
136
- language: languageName.isNotEmpty ? languageName : 'dart',
137
- theme: loadedTheme,
138
- );
139
- return SingleChildScrollView(
140
- scrollDirection: Axis.horizontal,
141
- padding: const EdgeInsets.all(12),
142
- child: SelectableText.rich(
143
- TextSpan(
144
- style: const TextStyle(
145
- fontFamily: 'monospace',
146
- fontSize: 13,
147
- height: 1.5,
148
- ),
149
- children: <TextSpan>[
150
- highlighter.highlight(code),
151
- ],
152
- ),
153
- ),
154
- );
134
+ final loadedTheme = isDark ? themesData.dark : themesData.light;
135
+ final effectiveLanguage = languageName.isNotEmpty ? languageName : 'dart';
136
+
137
+ if (supportedHighlighterLanguages.contains(effectiveLanguage)) {
138
+ final highlighter = Highlighter(
139
+ language: effectiveLanguage,
140
+ theme: loadedTheme,
141
+ );
142
+ return SingleChildScrollView(
143
+ scrollDirection: Axis.horizontal,
144
+ padding: const EdgeInsets.all(12),
145
+ child: SelectableText.rich(
146
+ TextSpan(
147
+ style: const TextStyle(
148
+ fontFamily: 'monospace',
149
+ fontSize: 13,
150
+ height: 1.5,
151
+ ),
152
+ children: <TextSpan>[
153
+ highlighter.highlight(code),
154
+ ],
155
+ ),
156
+ ),
157
+ );
158
+ } else {
159
+ return SingleChildScrollView(
160
+ scrollDirection: Axis.horizontal,
161
+ padding: const EdgeInsets.all(12),
162
+ child: SelectableText(
163
+ code,
164
+ style: const TextStyle(
165
+ fontFamily: 'monospace',
166
+ fontSize: 13,
167
+ height: 1.5,
168
+ ),
169
+ ),
170
+ );
171
+ }
172
},
173
loading: () => SingleChildScrollView(
174
scrollDirection: Axis.horizontal,