This commit is contained in:
Chen Li
2025-08-12 13:36:42 +08:00
parent 8191bef32e
commit 130755f9e1
47 changed files with 3728 additions and 761 deletions

View File

@@ -0,0 +1,60 @@
import 'dart:ui';
class CommonUtil {
static const Color commonColor = Color(0xFF6F72F1);
static bool containChinese(String text) {
return RegExp(r'[\u4E00-\u9FFF]').hasMatch(text);
}
static String cleanText(String text, bool forTts) {
String cleanedText = text
// 修正:使用 replaceAllMapped 确保安全提取内容
.replaceAllMapped(RegExp(r'\*\*(.*?)\*\*'), (m) => m.group(1) ?? '') // 粗体
.replaceAllMapped(RegExp(r'\*(.*?)\*'), (m) => m.group(1) ?? '') // 斜体
.replaceAllMapped(RegExp(r'`([^`]+)`'), (m) => m.group(1) ?? '') // 行内代码
// 修正:处理不完整的代码块
.replaceAll(RegExp(r'```[^`]*```', multiLine: true), '') // 完整代码块
.replaceAll(RegExp(r'```[^`]*$', multiLine: true), '') // 不完整代码块开始
.replaceAll(RegExp(r'^[^`]*```', multiLine: true), '') // 不完整代码块结束
// 新增:处理 markdown 表格
.replaceAll(RegExp(r'^\s*\|.*\|\s*$', multiLine: true), '') // 表格行:| 内容 | 内容 |
.replaceAll(RegExp(r'^\s*[\|\-\:\+\=\s]+\s*$', multiLine: true), '') // 表格分隔符行:|---|---|
.replaceAll(RegExp(r'\|'), ' ') // 清理残留的竖线
// 修正:使用 replaceAllMapped 避免 $1 问题
.replaceAllMapped(RegExp(r'^#{1,6}\s+(.*)$', multiLine: true),
(m) => m.group(1) ?? '')
.replaceAllMapped(RegExp(r'\[([^\]]+)\]\([^\)]+\)'), (m) => m.group(1) ?? '')
// 修正:处理不完整的链接
.replaceAll(RegExp(r'\[([^\]]*)\](?!\()'), r'$1') // 只有方括号的链接
.replaceAll(RegExp(r'\]\([^\)]*\)'), '') // 只有圆括号部分
.replaceAll(RegExp(r'!\[([^\]]*)\]\([^\)]+\)'), '') // 图片链接
.replaceAllMapped(RegExp(r'^>\s*(.*)$', multiLine: true), (m) => m.group(1) ?? '')
.replaceAllMapped(RegExp(r'^\s*[–—\-*+]+\s*(.*)$', multiLine: true), (m) => m.group(1) ?? '')
.replaceAllMapped(RegExp(r'^\s*\d+\.\s+(.*)$', multiLine: true), (m) => m.group(1) ?? '')
.replaceAll(RegExp(r'^\s*[-*]{3,}\s*$', multiLine: true), '')
// 修正:清理残留的 markdown 符号
.replaceAll(RegExp(r'\*+'), '') // 清理残留星号
.replaceAll(RegExp(r'`+'), '') // 清理残留反引号
.replaceAll(RegExp(r'#+'), '') // 清理残留井号
.replaceAll(RegExp(r'\n\s*\n'), '\n')
.replaceAll(RegExp(r'\n'), ' ')
.replaceAll(RegExp(r'!\$\d'), '') // 清理占位符
.replaceAll(RegExp(r'\$\d+'), '') // 清理所有 $数字 占位符
.trim();
if (forTts) {
cleanedText = cleanedText.replaceAllMapped(
RegExp(r'ID\.UNYX', caseSensitive: false), (m) => 'I D Unix');
}
return cleanedText;
}
}

View File

@@ -0,0 +1,56 @@
import 'package:flutter_tts/flutter_tts.dart';
typedef TtsCompletionCallback = void Function();
typedef TtsErrorCallback = void Function(String error);
class TtsEngineManager {
static final TtsEngineManager _instance = TtsEngineManager._internal();
factory TtsEngineManager() => _instance;
TtsEngineManager._internal();
final FlutterTts _flutterTts = FlutterTts();
TtsCompletionCallback? onComplete;
TtsErrorCallback? onError;
bool _isInitialized = false;
Future<void> init({String language = 'zh-CN', double speechRate = 0.5}) async {
if (_isInitialized) return;
await _flutterTts.setLanguage(language);
await _flutterTts.setSpeechRate(speechRate);
_flutterTts.setCompletionHandler(() {
if (onComplete != null) onComplete!();
});
_flutterTts.setErrorHandler((msg) {
if (onError != null) onError!(msg);
});
_isInitialized = true;
}
Future<void> speak(String text, {String? language, double? speechRate}) async {
if (!_isInitialized) {
await init(language: language ?? 'zh-CN', speechRate: speechRate ?? 0.5);
}
if (language != null) await _flutterTts.setLanguage(language);
if (speechRate != null) await _flutterTts.setSpeechRate(speechRate);
await _flutterTts.speak(text);
}
Future<void> stop() async {
await _flutterTts.stop();
}
Future<void> setLanguage(String language) async {
await _flutterTts.setLanguage(language);
}
Future<void> setSpeechRate(double rate) async {
await _flutterTts.setSpeechRate(rate);
}
void dispose() {
_flutterTts.stop();
_isInitialized = false;
}
}