@tduncan / Cortana / commits / 3331b30

feat: integrate speech-to-text functionality into chat input bar with UI animations and localization support

abdulmominsakib committed Jun 15, 2026 at 10:40 UTC 3331b30b30ddd801c436e9d41325cc7935baeebf
32 files changed +642 -167
android/app/build.gradle.kts
+1 -1
@@ -76,5 +76,5 @@ flutter {
76 }
77
78 dependencies {
79 - coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
79 + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
80 }
android/app/src/main/AndroidManifest.xml
+4
@@ -6,6 +6,7 @@
6 <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
7 <uses-permission android:name="android.permission.WAKE_LOCK" />
8 <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
9 + <uses-permission android:name="android.permission.RECORD_AUDIO" />
10
11 <application
12 android:label="LocalMind"
@@ -86,5 +87,8 @@
87 <action android:name="android.intent.action.PROCESS_TEXT"/>
88 <data android:mimeType="text/plain"/>
89 </intent>
90 + <intent>
91 + <action android:name="android.speech.RecognitionService" />
92 + </intent>
93 </queries>
94 </manifest>
ios/Runner/Info.plist
+4
@@ -84,6 +84,10 @@
84 <string>This app requires local network access for model inference services.</string>
85 <key>NSPhotoLibraryUsageDescription</key>
86 <string>This app requires access to your photo library to let you select and import files, documents, or images into your chats.</string>
87 + <key>NSMicrophoneUsageDescription</key>
88 + <string>This app requires microphone access for offline voice interaction and speech-to-text features.</string>
89 + <key>NSSpeechRecognitionUsageDescription</key>
90 + <string>This app requires speech recognition access to convert your voice messages into text.</string>
91 <key>ITSAppUsesNonExemptEncryption</key>
92 <false/>
93 </dict>
lib/core/models/enums.dart
+1 -1
@@ -206,7 +206,7 @@ enum ConnectionStatus { connected, disconnected, checking, error }
206
207 enum MessageRole { user, assistant, system, tool }
208
209 -enum MessageStatus { sending, streaming, complete, error }
209 +enum MessageStatus { sending, streaming, complete, error, cancelled }
210
211 enum ModelStatus { unloaded, loading, loaded, preloaded, thinking }
212
lib/features/chat/data/chat_service.dart
+24
@@ -640,6 +640,18 @@ class OpenAICompatibleChatService implements ChatService {
640 }
641 }
642 }
643 +
644 + // Flush any tool calls accumulated without a [DONE] marker (e.g.
645 + // connection drop or server crash before sending [DONE]).
646 + for (final call in toolAdapter.takeCompletedCalls()) {
647 + yield ChatResponse(
648 + type: ChatResponseType.toolCall,
649 + toolCall: ToolCallData(
650 + tool: call.name,
651 + arguments: call.arguments,
652 + ),
653 + );
654 + }
655 } catch (e) {
656 Log.error('OpenAICompatible connection error: $e');
657 yield ChatResponse(
@@ -963,6 +975,18 @@ class OpenRouterChatService implements ChatService {
975 }
976 }
977 }
978 +
979 + // Flush any tool calls accumulated without a [DONE] marker (e.g.
980 + // connection drop or server crash before sending [DONE]).
981 + for (final call in toolAdapter.takeCompletedCalls()) {
982 + yield ChatResponse(
983 + type: ChatResponseType.toolCall,
984 + toolCall: ToolCallData(
985 + tool: call.name,
986 + arguments: call.arguments,
987 + ),
988 + );
989 + }
990 } catch (e) {
991 Log.error('OpenRouter connection error: $e');
992 yield ChatResponse(
lib/features/chat/data/mcp_client.dart
+24
@@ -10,6 +10,7 @@ class McpClient {
10 final Dio _dio;
11 bool _initialized = false;
12 McpCapabilities? _capabilities;
13 + Future<McpCapabilities>? _initializationFuture;
14
15 bool _useSse = false;
16 StreamSubscription? _sseSubscription;
@@ -87,6 +88,14 @@ class McpClient {
88 _handleSseDisconnect(e);
89 },
90 onDone: () {
91 + // Process any remaining data in the buffer before disconnecting.
92 + if (buffer.isNotEmpty || currentData.isNotEmpty) {
93 + if (currentData.isNotEmpty) {
94 + _handleSseMessage(currentEvent, currentData, handshakeCompleter);
95 + } else if (buffer.trim().isNotEmpty) {
96 + _handleSseMessage(currentEvent, buffer.trim(), handshakeCompleter);
97 + }
98 + }
99 if (!handshakeCompleter.isCompleted) {
100 handshakeCompleter.completeError(McpException('SSE stream closed before handshake completed'));
101 }
@@ -235,6 +244,21 @@ class McpClient {
244 return _capabilities!;
245 }
246
247 + // If initialization is already in progress, wait for it.
248 + if (_initializationFuture != null) {
249 + return _initializationFuture!;
250 + }
251 +
252 + _initializationFuture = _doInitialize();
253 + try {
254 + return await _initializationFuture!;
255 + } finally {
256 + _initializationFuture = null;
257 + }
258 + }
259 +
260 + Future<McpCapabilities> _doInitialize() async {
261 +
262 try {
263 try {
264 await _connectSse();
lib/features/chat/data/mcp_server_manager.dart
+32 -19
@@ -9,38 +9,51 @@ class McpServerManager {
9 final Map<String, List<McpTool>> _tools = {};
10 final Map<String, String> _serverUrls = {};
11 final Set<String> _localExampleServers = {};
12 + final Set<String> _pendingLabels = {};
13
14 Future<void> addServer(
15 String label,
16 String url, {
17 Map<String, String>? headers,
18 }) async {
18 - if (_clients.containsKey(label)) {
19 - removeServer(label);
19 + // Guard against concurrent addServer calls for the same label.
20 + if (_pendingLabels.contains(label)) return;
21 + _pendingLabels.add(label);
22 +
23 + try {
24 + if (_clients.containsKey(label)) {
25 + final oldClient = _clients.remove(label);
26 + await oldClient?.close();
27 + _capabilities.remove(label);
28 + _tools.remove(label);
29 + _serverUrls.remove(label);
30 + }
31 +
32 + final client = McpClient(serverUrl: url, headers: headers);
33 +
34 + final capabilities = await client.initialize();
35 + final tools = await client.listTools();
36 +
37 + _clients[label] = client;
38 + _capabilities[label] = capabilities;
39 + _tools[label] = tools;
40 + _serverUrls[label] = url;
41 + } finally {
42 + _pendingLabels.remove(label);
43 }
21 -
22 - final client = McpClient(serverUrl: url, headers: headers);
23 -
24 - final capabilities = await client.initialize();
25 - final tools = await client.listTools();
26 -
27 - _clients[label] = client;
28 - _capabilities[label] = capabilities;
29 - _tools[label] = tools;
30 - _serverUrls[label] = url;
44 }
45
33 - void removeServer(String label) {
34 - _clients[label]?.close();
35 - _clients.remove(label);
46 + Future<void> removeServer(String label) async {
47 + final client = _clients.remove(label);
48 + await client?.close();
49 _capabilities.remove(label);
50 _tools.remove(label);
51 _serverUrls.remove(label);
52 _localExampleServers.remove(label);
53 }
54
42 - void addExampleServer() {
43 - removeServer(exampleMcpServerLabel);
55 + Future<void> addExampleServer() async {
56 + await removeServer(exampleMcpServerLabel);
57
58 _capabilities[exampleMcpServerLabel] = const McpCapabilities(tools: true);
59 _tools[exampleMcpServerLabel] = const [
@@ -125,9 +138,9 @@ class McpServerManager {
138 return client.readResource(uri);
139 }
140
128 - void clear() {
141 + Future<void> clear() async {
142 for (final client in _clients.values) {
130 - client.close();
143 + await client.close();
144 }
145 _clients.clear();
146 _capabilities.clear();
lib/features/chat/providers/chat_providers.dart
+14 -4
@@ -881,7 +881,8 @@ class ChatNotifier extends Notifier<ChatState> {
881 // Check if content is empty - this happens when free models refuse or fail
882 final hasContent =
883 streamingMessage.content.isNotEmpty ||
884 - (streamingMessage.reasoningContent?.isNotEmpty ?? false);
884 + (streamingMessage.reasoningContent?.isNotEmpty ?? false) ||
885 + collectedToolCalls.isNotEmpty;
886
887 if (!hasContent) {
888 // Mark as error if no content received
@@ -917,7 +918,16 @@ class ChatNotifier extends Notifier<ChatState> {
918 final registry = ref.read(toolRegistryProvider);
919 final adapter = createAdapterForServerType(server.type);
920
920 - final preParsedCalls = collectedToolCalls
921 + // Deduplicate tool calls by name — LM Studio emits
922 + // tool_call.start, tool_call.arguments, and
923 + // tool_call.success as separate events. Keep only the
924 + // last event per tool (most complete arguments/output).
925 + final dedupedCalls = <String, ToolCallData>{};
926 + for (final tc in collectedToolCalls) {
927 + dedupedCalls[tc.tool] = tc;
928 + }
929 +
930 + final preParsedCalls = dedupedCalls.values
931 .map(
932 (tc) => ParsedToolCall(
933 id: tc.tool,
@@ -1173,7 +1183,7 @@ class ChatNotifier extends Notifier<ChatState> {
1183 _uiUpdateTimer?.cancel();
1184 _uiUpdateTimer = null;
1185 _clearPendingApproval();
1176 - _streamSubscription?.cancel();
1186 + await _streamSubscription?.cancel();
1187 _streamSubscription = null;
1188 ref.read(chatServiceProvider)?.cancelStream();
1189 ref.read(chatBackgroundServiceProvider).stop();
@@ -1189,7 +1199,7 @@ class ChatNotifier extends Notifier<ChatState> {
1199 final streamingMessage = _latestStreamingMessage ?? state.streamingMessage;
1200 if (streamingMessage != null) {
1201 final finalMessage = streamingMessage.copyWith(
1192 - status: MessageStatus.complete,
1202 + status: MessageStatus.cancelled,
1203 isProcessing: false,
1204 );
1205 await _saveMessage(finalMessage);
lib/features/chat/views/components/chat_bubble.dart
+2 -1
@@ -304,7 +304,8 @@ class _AssistantBubble extends StatelessWidget {
304 const Spacer(),
305 if (!isStreaming &&
306 (message.status == MessageStatus.complete ||
307 - message.status == MessageStatus.error))
307 + message.status == MessageStatus.error ||
308 + message.status == MessageStatus.cancelled))
309 MessageActionBar(
310 content: message.content,
311 onCopy: onCopy,
lib/features/chat/views/components/chat_input_bar.dart
+140 -3
@@ -9,6 +9,7 @@ import 'package:localmind/l10n/app_localizations.dart';
9
10 import '../../../../core/models/enums.dart';
11 import '../../../servers/providers/server_providers.dart';
12 +import '../../../stt/providers/stt_providers.dart';
13
14 class ChatInputBar extends ConsumerStatefulWidget {
15 const ChatInputBar({
@@ -33,12 +34,14 @@ class ChatInputBar extends ConsumerStatefulWidget {
34 }
35
36 class _ChatInputBarState extends ConsumerState<ChatInputBar>
36 - with SingleTickerProviderStateMixin {
37 + with TickerProviderStateMixin {
38 final _controller = TextEditingController();
39 late final FocusNode _focusNode;
40 final List<File> _attachedFiles = [];
41 late AnimationController _sendButtonAnimController;
42 late Animation<double> _sendButtonScale;
43 + late AnimationController _micAnimController;
44 + String _preSpeechText = '';
45
46 @override
47 void initState() {
@@ -51,6 +54,10 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
54 _sendButtonScale = Tween<double>(begin: 1.0, end: 1.1).animate(
55 CurvedAnimation(parent: _sendButtonAnimController, curve: Curves.easeOut),
56 );
57 + _micAnimController = AnimationController(
58 + duration: const Duration(milliseconds: 800),
59 + vsync: this,
60 + );
61 }
62
63 @override
@@ -60,13 +67,89 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
67 _focusNode.dispose();
68 }
69 _sendButtonAnimController.dispose();
70 + _micAnimController.dispose();
71 super.dispose();
72 }
73
74 + Future<void> _handleMicToggle() async {
75 + final stt = ref.read(sttProvider.notifier);
76 + final isListening = ref.read(sttProvider).isListening;
77 +
78 + Haptics.vibrate(HapticsType.light);
79 +
80 + if (isListening) {
81 + await stt.stopListening();
82 + } else {
83 + _preSpeechText = _controller.text;
84 + await stt.startListening(
85 + onResult: (words) {
86 + if (words.isNotEmpty) {
87 + setState(() {
88 + if (_preSpeechText.isEmpty) {
89 + _controller.text = words;
90 + } else {
91 + _controller.text = '$_preSpeechText $words';
92 + }
93 + // Position the cursor at the end of the text
94 + _controller.selection = TextSelection.fromPosition(
95 + TextPosition(offset: _controller.text.length),
96 + );
97 + });
98 + }
99 + },
100 + );
101 + }
102 + }
103 +
104 + Widget _buildMicButton(bool isListening, ThemeData theme) {
105 + final l10n = AppLocalizations.of(context)!;
106 + return AnimatedBuilder(
107 + animation: _micAnimController,
108 + builder: (context, child) {
109 + final pulseValue = isListening ? _micAnimController.value : 0.0;
110 + return Container(
111 + decoration: BoxDecoration(
112 + shape: BoxShape.circle,
113 + boxShadow: [
114 + if (isListening)
115 + BoxShadow(
116 + color: Colors.red.withValues(alpha: 0.4 * pulseValue),
117 + blurRadius: 8 + (8 * pulseValue),
118 + spreadRadius: 1 + (3 * pulseValue),
119 + ),
120 + ],
121 + ),
122 + child: Container(
123 + width: 44,
124 + height: 44,
125 + decoration: BoxDecoration(
126 + color: isListening
127 + ? Colors.red
128 + : theme.colorScheme.onSurface.withValues(alpha: 0.05),
129 + shape: BoxShape.circle,
130 + ),
131 + child: IconButton(
132 + icon: HugeIcon(
133 + icon: isListening
134 + ? HugeIcons.strokeRoundedVoice
135 + : HugeIcons.strokeRoundedMic01,
136 + color: isListening
137 + ? Colors.white
138 + : theme.colorScheme.onSurface.withValues(alpha: 0.7),
139 + size: 20,
140 + ),
141 + onPressed: _handleMicToggle,
142 + tooltip: isListening ? l10n.stop_listening_tooltip : l10n.start_listening_tooltip,
143 + ),
144 + ),
145 + );
146 + },
147 + );
148 + }
149 +
150 Future<void> _handleAttach() async {
67 - final result = await FilePicker.platform.pickFiles(
151 + final result = await FilePicker.pickFiles(
152 type: FileType.image,
69 - allowMultiple: true,
153 );
154 if (result != null && result.files.isNotEmpty) {
155 setState(() {
@@ -151,6 +234,30 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
234 );
235 }
236
237 + String? _mapSttError(String error) {
238 + switch (error) {
239 + case 'error_no_match':
240 + return 'No speech recognized. Please try speaking again and check your microphone.';
241 + case 'error_speech_timeout':
242 + return 'No speech detected. Timed out.';
243 + case 'error_permission':
244 + return 'Microphone permission denied.';
245 + case 'error_busy':
246 + return 'Speech recognition is busy. Please try again.';
247 + case 'error_network':
248 + case 'error_network_timeout':
249 + return 'Network error. Please check your connection and try again.';
250 + case 'error_audio':
251 + return 'Audio recording error. Please check your microphone.';
252 + default:
253 + if (error.startsWith('error_')) {
254 + final cleanName = error.replaceFirst('error_', '').replaceAll('_', ' ');
255 + return 'Speech recognition error: $cleanName';
256 + }
257 + return error;
258 + }
259 + }
260 +
261 @override
262 Widget build(BuildContext context) {
263 final l10n = AppLocalizations.of(context)!;
@@ -159,6 +266,34 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
266 final connectionStatus = ref.watch(connectionStatusProvider);
267 final isConnected = connectionStatus == ConnectionStatus.connected;
268
269 + final sttState = ref.watch(sttProvider);
270 + final isListening = sttState.isListening;
271 +
272 + if (isListening) {
273 + if (!_micAnimController.isAnimating) {
274 + _micAnimController.repeat(reverse: true);
275 + }
276 + } else {
277 + if (_micAnimController.isAnimating) {
278 + _micAnimController.stop();
279 + _micAnimController.reset();
280 + }
281 + }
282 +
283 + ref.listen<String?>(
284 + sttProvider.select((s) => s.error),
285 + (previous, next) {
286 + if (next != null) {
287 + final message = _mapSttError(next);
288 + if (message != null) {
289 + ScaffoldMessenger.of(context).showSnackBar(
290 + SnackBar(content: Text(message)),
291 + );
292 + }
293 + }
294 + },
295 + );
296 +
297 final canSend =
298 widget.enabled &&
299 isConnected &&
@@ -303,6 +438,8 @@ class _ChatInputBarState extends ConsumerState<ChatInputBar>
438 ),
439 ),
440 const SizedBox(width: 8),
441 + _buildMicButton(isListening, theme),
442 + const SizedBox(width: 8),
443 _buildActionButton(canSend, theme),
444 ],
445 ),
lib/features/on_device/data/notification_permission_service.dart
+1 -1
@@ -12,7 +12,7 @@ class NotificationPermissionService {
12 iOS: darwinInit,
13 );
14
15 - await _notifications.initialize(initSettings);
15 + await _notifications.initialize(settings: initSettings);
16 }
17
18 Future<bool> isPermissionGranted() async {
lib/features/stt/providers/stt_providers.dart new
+128
@@ -0,0 +1,128 @@
1 +import 'dart:async';
2 +import 'package:flutter_riverpod/flutter_riverpod.dart';
3 +import 'package:speech_to_text/speech_to_text.dart';
4 +import '../../../core/logger/app_logger.dart';
5 +
6 +class SttState {
7 + final bool isListening;
8 + final bool isAvailable;
9 + final String recognizedWords;
10 + final String? error;
11 +
12 + const SttState({
13 + this.isListening = false,
14 + this.isAvailable = false,
15 + this.recognizedWords = '',
16 + this.error,
17 + });
18 +
19 + SttState copyWith({
20 + bool? isListening,
21 + bool? isAvailable,
22 + String? recognizedWords,
23 + String? error,
24 + bool clearError = false,
25 + }) {
26 + return SttState(
27 + isListening: isListening ?? this.isListening,
28 + isAvailable: isAvailable ?? this.isAvailable,
29 + recognizedWords: recognizedWords ?? this.recognizedWords,
30 + error: clearError ? null : (error ?? this.error),
31 + );
32 + }
33 +}
34 +
35 +class SttNotifier extends Notifier<SttState> {
36 + late SpeechToText _speech;
37 + bool _isInit = false;
38 +
39 + @override
40 + SttState build() {
41 + _speech = SpeechToText();
42 + ref.onDispose(() {
43 + try {
44 + _speech.cancel();
45 + } catch (e) {
46 + Log.error('STT dispose/cancel error: $e');
47 + }
48 + });
49 + return const SttState();
50 + }
51 +
52 + Future<bool> initSpeech() async {
53 + if (_isInit) return state.isAvailable;
54 + try {
55 + final available = await _speech.initialize(
56 + onError: (val) {
57 + Log.error('STT error: ${val.errorMsg} - permanent: ${val.permanent}');
58 + state = state.copyWith(error: val.errorMsg, isListening: false);
59 + },
60 + onStatus: (val) {
61 + Log.debug('STT status: $val');
62 + if (val == 'listening') {
63 + state = state.copyWith(isListening: true);
64 + } else if (val == 'notListening' || val == 'done') {
65 + state = state.copyWith(isListening: false);
66 + }
67 + },
68 + );
69 + _isInit = true;
70 + state = state.copyWith(isAvailable: available);
71 + return available;
72 + } catch (e) {
73 + Log.error('STT initialization failed: $e');
74 + state = state.copyWith(isAvailable: false, error: e.toString());
75 + return false;
76 + }
77 + }
78 +
79 + Future<void> startListening({required void Function(String) onResult}) async {
80 + final available = await initSpeech();
81 + if (!available) {
82 + state = state.copyWith(error: 'Speech recognition not available or permission denied');
83 + return;
84 + }
85 +
86 + state = state.copyWith(isListening: true, recognizedWords: '', clearError: true);
87 +
88 + try {
89 + await _speech.listen(
90 + onResult: (result) {
91 + state = state.copyWith(recognizedWords: result.recognizedWords);
92 + onResult(result.recognizedWords);
93 + },
94 + listenOptions: SpeechListenOptions(
95 + listenFor: const Duration(seconds: 30),
96 + pauseFor: const Duration(seconds: 5),
97 + partialResults: true,
98 + cancelOnError: true,
99 + ),
100 + );
101 + } catch (e) {
102 + Log.error('STT listen error: $e');
103 + state = state.copyWith(isListening: false, error: e.toString());
104 + }
105 + }
106 +
107 + Future<void> stopListening() async {
108 + try {
109 + await _speech.stop();
110 + state = state.copyWith(isListening: false);
111 + } catch (e) {
112 + Log.error('STT stop error: $e');
113 + }
114 + }
115 +
116 + Future<void> cancelListening() async {
117 + try {
118 + await _speech.cancel();
119 + state = state.copyWith(isListening: false);
120 + } catch (e) {
121 + Log.error('STT cancel error: $e');
122 + }
123 + }
124 +}
125 +
126 +final sttProvider = NotifierProvider<SttNotifier, SttState>(() {
127 + return SttNotifier();
128 +});
lib/features/tts/providers/tts_providers.dart
+4 -5
@@ -91,7 +91,6 @@ class TtsNotifier extends Notifier<TtsState> {
91 Completer<SendPort>? _workerPortCompleter;
92
93 bool _isPreview = false;
94 - final ConcatenatingAudioSource _playlist = ConcatenatingAudioSource(children: []);
94 final Map<int, AudioSource> _playlistBuffer = {};
95 int _nextPlaylistIndexToAdd = 0;
96 StreamSubscription<int?>? _currentIndexSubscription;
@@ -118,7 +117,7 @@ class TtsNotifier extends Notifier<TtsState> {
117 }
118 });
119
121 - _player.setAudioSource(_playlist);
120 + _player.setAudioSources([]);
121
122 _playerCompleteSubscription = _player.playerStateStream.listen((playerState) {
123 if (playerState.processingState == ProcessingState.completed) {
@@ -481,7 +480,7 @@ class TtsNotifier extends Notifier<TtsState> {
480 _isPreview = isPreview;
481 _playlistBuffer.clear();
482 _nextPlaylistIndexToAdd = 0;
484 - await _playlist.clear();
483 + await _player.clearAudioSources();
484
485 try {
486 AudioSession.instance.then((session) {
@@ -539,7 +538,7 @@ class TtsNotifier extends Notifier<TtsState> {
538 _playlistBuffer[chunkIndex] = source;
539 while (_playlistBuffer.containsKey(_nextPlaylistIndexToAdd)) {
540 final src = _playlistBuffer.remove(_nextPlaylistIndexToAdd)!;
542 - await _playlist.add(src);
541 + await _player.addAudioSource(src);
542
543 if (_nextPlaylistIndexToAdd == 0) {
544 if (!state.isPaused) {
@@ -605,7 +604,7 @@ class TtsNotifier extends Notifier<TtsState> {
604 if (_isPlayerDisposed) return;
605 try {
606 await _player.stop();
608 - await _playlist.clear();
607 + await _player.clearAudioSources();
608 } catch (_) {}
609 await _flutterTts?.stop();
610 _cleanupSessionFiles(_currentSessionId, _chunks.length);
lib/l10n/app_ar.arb
+3 -1
@@ -505,5 +505,7 @@
505 "test_notification_title": "إشعار تجريبي",
506 "test_notification_body": "هذا إشعار تجريبي لتقدم تنزيل النموذج.",
507 "tts_supports_background": "يدعم التشغيل في الخلفية كصوت أصلي",
508 - "tts_other_services_background_note": "ملاحظة: تدعم خدمات TTS الأخرى التشغيل في الخلفية كصوت أصلي."
508 + "tts_other_services_background_note": "ملاحظة: تدعم خدمات TTS الأخرى التشغيل في الخلفية كصوت أصلي.",
509 + "start_listening_tooltip": "بدء الاستماع",
510 + "stop_listening_tooltip": "إيقاف الاستماع"
511 }
\ No newline at end of file
lib/l10n/app_bn.arb
+3 -1
@@ -505,5 +505,7 @@
505 "waiting_selection": "নির্বাচনের জন্য অপেক্ষা করা হচ্ছে",
506 "yesterday_section": "গতকাল",
507 "tts_supports_background": "নেটিভ অডিও হিসাবে ব্যাকগ্রাউন্ড প্লেব্যাক সমর্থন করে",
508 - "tts_other_services_background_note": "দ্রষ্টব্য: অন্যান্য টিটিএস পরিষেবাগুলি নেটিভ অডিও হিসাবে ব্যাকগ্রাউন্ড প্লেব্যাক সমর্থন করে।"
508 + "tts_other_services_background_note": "দ্রষ্টব্য: অন্যান্য টিটিএস পরিষেবাগুলি নেটিভ অডিও হিসাবে ব্যাকগ্রাউন্ড প্লেব্যাক সমর্থন করে।",
509 + "start_listening_tooltip": "শোনা শুরু করুন",
510 + "stop_listening_tooltip": "শোনা বন্ধ করুন"
511 }
\ No newline at end of file
lib/l10n/app_en.arb
+8
@@ -828,6 +828,14 @@
828 "@attach_images_tooltip": {
829 "description": "Tooltip for attach button"
830 },
831 + "start_listening_tooltip": "Start listening",
832 + "@start_listening_tooltip": {
833 + "description": "Tooltip for start listening button"
834 + },
835 + "stop_listening_tooltip": "Stop listening",
836 + "@stop_listening_tooltip": {
837 + "description": "Tooltip for stop listening button"
838 + },
839 "tool_label": "Tool: {toolCallId}",
840 "@tool_label": {
841 "description": "Tool call label in chat bubble",
lib/l10n/app_es.arb
+3 -1
@@ -505,5 +505,7 @@
505 "waiting_selection": "ESPERANDO SELECCIÓN",
506 "yesterday_section": "AYER",
507 "tts_supports_background": "Soporta reproducción en segundo plano como audio nativo",
508 - "tts_other_services_background_note": "Nota: Los otros servicios de TTS soportan reproducción en segundo plano como audio nativo."
508 + "tts_other_services_background_note": "Nota: Los otros servicios de TTS soportan reproducción en segundo plano como audio nativo.",
509 + "start_listening_tooltip": "Iniciar escucha",
510 + "stop_listening_tooltip": "Detener escucha"
511 }
\ No newline at end of file
lib/l10n/app_hi.arb
+3 -1
@@ -505,5 +505,7 @@
505 "waiting_selection": "चयन की प्रतीक्षा की जा रही है",
506 "yesterday_section": "कल",
507 "tts_supports_background": "मूल ऑडियो के रूप में पृष्ठभूमि प्लेबैक का समर्थन करता है",
508 - "tts_other_services_background_note": "नोट: अन्य टीटीएस सेवाएं मूल ऑडियो के रूप में पृष्ठभूमि प्लेबैक का समर्थन करती हैं।"
508 + "tts_other_services_background_note": "नोट: अन्य टीटीएस सेवाएं मूल ऑडियो के रूप में पृष्ठभूमि प्लेबैक का समर्थन करती हैं।",
509 + "start_listening_tooltip": "सुनना शुरू करें",
510 + "stop_listening_tooltip": "सुनना बंद करें"
511 }
\ No newline at end of file
lib/l10n/app_it.arb
+3 -1
@@ -505,5 +505,7 @@
505 "test_notification_title": "Notifica di prova",
506 "test_notification_body": "Questa è una notifica di prova per l'avanzamento del download del modello.",
507 "tts_supports_background": "Supporta la riproduzione in background come audio nativo",
508 - "tts_other_services_background_note": "Nota: Gli altri servizi TTS supportano la riproduzione in background come audio nativo."
508 + "tts_other_services_background_note": "Nota: Gli altri servizi TTS supportano la riproduzione in background come audio nativo.",
509 + "start_listening_tooltip": "Avvia ascolto",
510 + "stop_listening_tooltip": "Interrompi ascolto"
511 }
\ No newline at end of file
lib/l10n/app_ja.arb
+3 -1
@@ -505,5 +505,7 @@
505 "test_notification_title": "テスト通知",
506 "test_notification_body": "これはモデルのダウンロード進捗用のテスト通知です。",
507 "tts_supports_background": "ネイティブオーディオとしてバックグラウンド再生をサポート",
508 - "tts_other_services_background_note": "注意: 他のTTSサービスはネイティブオーディオとしてバックグラウンド再生をサポートしています。"
508 + "tts_other_services_background_note": "注意: 他のTTSサービスはネイティブオーディオとしてバックグラウンド再生をサポートしています。",
509 + "start_listening_tooltip": "聞き取りを開始",
510 + "stop_listening_tooltip": "聞き取りを停止"
511 }
\ No newline at end of file
lib/l10n/app_localizations.dart
+12
@@ -1226,6 +1226,18 @@ abstract class AppLocalizations {
1226 /// **'Attach images'**
1227 String get attach_images_tooltip;
1228
1229 + /// Tooltip for start listening button
1230 + ///
1231 + /// In en, this message translates to:
1232 + /// **'Start listening'**
1233 + String get start_listening_tooltip;
1234 +
1235 + /// Tooltip for stop listening button
1236 + ///
1237 + /// In en, this message translates to:
1238 + /// **'Stop listening'**
1239 + String get stop_listening_tooltip;
1240 +
1241 /// Tool call label in chat bubble
1242 ///
1243 /// In en, this message translates to:
lib/l10n/app_localizations_ar.dart
+6
@@ -595,6 +595,12 @@ class AppLocalizationsAr extends AppLocalizations {
595 @override
596 String get attach_images_tooltip => 'إرفاق صور';
597
598 + @override
599 + String get start_listening_tooltip => 'بدء الاستماع';
600 +
601 + @override
602 + String get stop_listening_tooltip => 'إيقاف الاستماع';
603 +
604 @override
605 String tool_label(String toolCallId) {
606 return 'أداة: $toolCallId';
lib/l10n/app_localizations_bn.dart
+6
@@ -598,6 +598,12 @@ class AppLocalizationsBn extends AppLocalizations {
598 @override
599 String get attach_images_tooltip => 'ছবি সংযুক্ত করুন';
600
601 + @override
602 + String get start_listening_tooltip => 'শোনা শুরু করুন';
603 +
604 + @override
605 + String get stop_listening_tooltip => 'শোনা বন্ধ করুন';
606 +
607 @override
608 String tool_label(String toolCallId) {
609 return 'টুল: $toolCallId';
lib/l10n/app_localizations_en.dart
+6
@@ -596,6 +596,12 @@ class AppLocalizationsEn extends AppLocalizations {
596 @override
597 String get attach_images_tooltip => 'Attach images';
598
599 + @override
600 + String get start_listening_tooltip => 'Start listening';
601 +
602 + @override
603 + String get stop_listening_tooltip => 'Stop listening';
604 +
605 @override
606 String tool_label(String toolCallId) {
607 return 'Tool: $toolCallId';
lib/l10n/app_localizations_es.dart
+6
@@ -598,6 +598,12 @@ class AppLocalizationsEs extends AppLocalizations {
598 @override
599 String get attach_images_tooltip => 'Adjuntar imágenes';
600
601 + @override
602 + String get start_listening_tooltip => 'Iniciar escucha';
603 +
604 + @override
605 + String get stop_listening_tooltip => 'Detener escucha';
606 +
607 @override
608 String tool_label(String toolCallId) {
609 return 'Herramienta: $toolCallId';
lib/l10n/app_localizations_hi.dart
+6
@@ -598,6 +598,12 @@ class AppLocalizationsHi extends AppLocalizations {
598 @override
599 String get attach_images_tooltip => 'छवियाँ संलग्न करें';
600
601 + @override
602 + String get start_listening_tooltip => 'सुनना शुरू करें';
603 +
604 + @override
605 + String get stop_listening_tooltip => 'सुनना बंद करें';
606 +
607 @override
608 String tool_label(String toolCallId) {
609 return 'उपकरण: $toolCallId';
lib/l10n/app_localizations_it.dart
+6
@@ -599,6 +599,12 @@ class AppLocalizationsIt extends AppLocalizations {
599 @override
600 String get attach_images_tooltip => 'Allega immagini';
601
602 + @override
603 + String get start_listening_tooltip => 'Avvia ascolto';
604 +
605 + @override
606 + String get stop_listening_tooltip => 'Interrompi ascolto';
607 +
608 @override
609 String tool_label(String toolCallId) {
610 return 'Strumento: $toolCallId';
lib/l10n/app_localizations_ja.dart
+6
@@ -592,6 +592,12 @@ class AppLocalizationsJa extends AppLocalizations {
592 @override
593 String get attach_images_tooltip => '画像を添付';
594
595 + @override
596 + String get start_listening_tooltip => '聞き取りを開始';
597 +
598 + @override
599 + String get stop_listening_tooltip => '聞き取りを停止';
600 +
601 @override
602 String tool_label(String toolCallId) {
603 return 'ツール: $toolCallId';
lib/l10n/app_localizations_zh.dart
+6
@@ -590,6 +590,12 @@ class AppLocalizationsZh extends AppLocalizations {
590 @override
591 String get attach_images_tooltip => '附加图片';
592
593 + @override
594 + String get start_listening_tooltip => '开始聆听';
595 +
596 + @override
597 + String get stop_listening_tooltip => '停止聆听';
598 +
599 @override
600 String tool_label(String toolCallId) {
601 return '工具: $toolCallId';
lib/l10n/app_zh.arb
+3 -1
@@ -505,5 +505,7 @@
505 "waiting_selection": "等待选择",
506 "yesterday_section": "昨天",
507 "tts_supports_background": "支持后台播放(原生音频格式)",
508 - "tts_other_services_background_note": "注意:其他TTS服务支持后台播放(原生音频格式)。"
508 + "tts_other_services_background_note": "注意:其他TTS服务支持后台播放(原生音频格式)。",
509 + "start_listening_tooltip": "开始聆听",
510 + "stop_listening_tooltip": "停止聆听"
511 }
\ No newline at end of file
pubspec.lock
+163 -115
@@ -21,10 +21,10 @@ packages:
21 dependency: "direct main"
22 description:
23 name: animations
24 - sha256: a120785be876b24177e8af387929e786e7761d6574e63cad6c2ca28545b30186
24 + sha256: "9cb469212ea51be27097f23b519d594c01171721347b55df9334fff653659e7f"
25 url: "https://pub.dev"
26 source: hosted
27 - version: "2.1.2"
27 + version: "2.2.0"
28 archive:
29 dependency: "direct main"
30 description:
@@ -77,18 +77,18 @@ packages:
77 dependency: "direct main"
78 description:
79 name: audio_session
80 - sha256: "2b7fff16a552486d078bfc09a8cde19f426dc6d6329262b684182597bec5b1ac"
80 + sha256: "7217b229db57cc4dc577a8abb56b7429a5a212b978517a5be578704bfe5e568b"
81 url: "https://pub.dev"
82 source: hosted
83 - version: "0.1.25"
83 + version: "0.2.3"
84 audioplayers:
85 dependency: "direct main"
86 description:
87 name: audioplayers
88 - sha256: a72dd459d1a48f61a6fb9c0134dba26597c9236af40639ff0eb70eb4e0baab70
88 + sha256: f16640453cc47487b7de72a2b28d37c7df1ac97999849f4a46d92b1d2b0f093d
89 url: "https://pub.dev"
90 source: hosted
91 - version: "6.6.0"
91 + version: "6.7.1"
92 audioplayers_android:
93 dependency: transitive
94 description:
@@ -125,26 +125,26 @@ packages:
125 dependency: transitive
126 description:
127 name: audioplayers_web
128 - sha256: faa8fa6587f996a6f604433b53af44c57a1407d4fe8dff5766cf63d6875e8de9
128 + sha256: "24a6f258062bd7da8cb2157e83fccb9816a08dd306cbaaa24f9813d071470545"
129 url: "https://pub.dev"
130 source: hosted
131 - version: "5.2.0"
131 + version: "5.2.1"
132 audioplayers_windows:
133 dependency: transitive
134 description:
135 name: audioplayers_windows
136 - sha256: bafff2b38b6f6d331887558ba6e0a01c9c208d9dbb3ad0005234db065122a734
136 + sha256: "95f875a96c88c3dbbcb608d4f8288e300b0113d256a81d0b3197fcc18f0dc91a"
137 url: "https://pub.dev"
138 source: hosted
139 - version: "4.3.0"
139 + version: "4.3.1"
140 background_downloader:
141 dependency: transitive
142 description:
143 name: background_downloader
144 - sha256: "4cb23d9ad4f5060944f38164e7b90d4bf99b57b2472a3bd4676e59b2db4afd06"
144 + sha256: aceacec2b2a72ec3a8862ab5895fcbbc71ab33765f3619d57963f3110dd268e3
145 url: "https://pub.dev"
146 source: hosted
147 - version: "9.5.4"
147 + version: "9.5.5"
148 boolean_selector:
149 dependency: transitive
150 description:
@@ -285,10 +285,10 @@ packages:
285 dependency: transitive
286 description:
287 name: coverage
288 - sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d"
288 + sha256: "956a3de0725ca232ad353565a8290d3357592bf4250f6f298a185e2d949c5d3d"
289 url: "https://pub.dev"
290 source: hosted
291 - version: "1.15.0"
291 + version: "1.15.1"
292 cross_file:
293 dependency: transitive
294 description:
@@ -341,26 +341,26 @@ packages:
341 dependency: transitive
342 description:
343 name: dbus
344 - sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
344 + sha256: "0ce9b0a839e6dee59a37a623d2fc26a35bbbe6404213e419b0d6411023d62645"
345 url: "https://pub.dev"
346 source: hosted
347 - version: "0.7.12"
347 + version: "0.7.14"
348 device_info_plus:
349 dependency: "direct main"
350 description:
351 name: device_info_plus
352 - sha256: b4fed1b2835da9d670d7bed7db79ae2a94b0f5ad6312268158a9b5479abbacdd
352 + sha256: "6a642e1daa10190af89ba6cb6386c0df7d071a3592080bfe1e44faa63ae1df65"
353 url: "https://pub.dev"
354 source: hosted
355 - version: "12.4.0"
355 + version: "13.1.0"
356 device_info_plus_platform_interface:
357 dependency: transitive
358 description:
359 name: device_info_plus_platform_interface
360 - sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
360 + sha256: "04b173a92e2d9161dfead145667037c8d834db725ce2e7b942bfe18fd2f45a46"
361 url: "https://pub.dev"
362 source: hosted
363 - version: "7.0.3"
363 + version: "8.1.0"
364 dio:
365 dependency: "direct main"
366 description:
@@ -409,6 +409,14 @@ packages:
409 url: "https://pub.dev"
410 source: hosted
411 version: "2.2.0"
412 + ffi_leak_tracker:
413 + dependency: transitive
414 + description:
415 + name: ffi_leak_tracker
416 + sha256: "4093d4ef9ca06ffe2786e73bfb25e22aa92112b9bb4ec941f11e3e6b61489a97"
417 + url: "https://pub.dev"
418 + source: hosted
419 + version: "0.1.2"
420 file:
421 dependency: transitive
422 description:
@@ -421,10 +429,10 @@ packages:
429 dependency: "direct main"
430 description:
431 name: file_picker
424 - sha256: "57d9a1dd5063f85fa3107fb42d1faffda52fdc948cefd5fe5ea85267a5fc7343"
432 + sha256: fc83774ce5bd7ce08168333b5e53dbe9090ec04eb21e7aa7cd7bac921032c934
433 url: "https://pub.dev"
434 source: hosted
427 - version: "10.3.10"
435 + version: "12.0.0-beta.5"
436 fixnum:
437 dependency: transitive
438 description:
@@ -466,10 +474,10 @@ packages:
474 dependency: "direct main"
475 description:
476 name: flutter_gemma
469 - sha256: a56c1745a6218fac9c3456d7449a8dbdd684a870cb73f407fcf47f38f1e3633e
477 + sha256: "984960b54bbc0ff7e36cf568a02652b1d4bc016d6c75575b027fb6102fab48c3"
478 url: "https://pub.dev"
479 source: hosted
472 - version: "0.16.1"
480 + version: "0.16.5"
481 flutter_lints:
482 dependency: "direct dev"
483 description:
@@ -482,26 +490,42 @@ packages:
490 dependency: "direct main"
491 description:
492 name: flutter_local_notifications
485 - sha256: ef41ae901e7529e52934feba19ed82827b11baa67336829564aeab3129460610
493 + sha256: a0d7141f14cabcee42967470a858dfc99dd6cfb70d3cab404bacfcafa9e84e70
494 url: "https://pub.dev"
495 source: hosted
488 - version: "18.0.1"
496 + version: "22.0.1"
497 flutter_local_notifications_linux:
498 dependency: transitive
499 description:
500 name: flutter_local_notifications_linux
493 - sha256: "8f685642876742c941b29c32030f6f4f6dacd0e4eaecb3efbb187d6a3812ca01"
501 + sha256: "9ca97e63776f29ab1b955725c09999fc2c150523269db150c39274f2a43c5a8b"
502 url: "https://pub.dev"
503 source: hosted
496 - version: "5.0.0"
504 + version: "8.0.1"
505 flutter_local_notifications_platform_interface:
506 dependency: transitive
507 description:
508 name: flutter_local_notifications_platform_interface
501 - sha256: "6c5b83c86bf819cdb177a9247a3722067dd8cc6313827ce7c77a4b238a26fd52"
509 + sha256: ff0013eae795e8dc8fad4a8992a209e64d3ba2fbd8bf5e43c36bf448f95bd814
510 url: "https://pub.dev"
511 source: hosted
504 - version: "8.0.0"
512 + version: "12.0.0"
513 + flutter_local_notifications_web:
514 + dependency: transitive
515 + description:
516 + name: flutter_local_notifications_web
517 + sha256: "516afaf97a2d1e67a036c6617321b00d205d72f7a67b6eccf936cd565f985878"
518 + url: "https://pub.dev"
519 + source: hosted
520 + version: "1.0.0"
521 + flutter_local_notifications_windows:
522 + dependency: transitive
523 + description:
524 + name: flutter_local_notifications_windows
525 + sha256: "6f43bdd03b171b7a90f22647506fea33e2bb12294b7c7c7a3d690e960a382945"
526 + url: "https://pub.dev"
527 + source: hosted
528 + version: "3.1.1"
529 flutter_localizations:
530 dependency: "direct main"
531 description: flutter
@@ -519,66 +543,66 @@ packages:
543 dependency: transitive
544 description:
545 name: flutter_plugin_android_lifecycle
522 - sha256: "38d1c268de9097ff59cf0e844ac38759fc78f76836d37edad06fa21e182055a0"
546 + sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785"
547 url: "https://pub.dev"
548 source: hosted
525 - version: "2.0.34"
549 + version: "2.0.35"
550 flutter_riverpod:
551 dependency: "direct main"
552 description:
553 name: flutter_riverpod
530 - sha256: "4e166be88e1dbbaa34a280bdb744aeae73b7ef25fdf8db7a3bb776760a3648e2"
554 + sha256: "9255e1e3ad6e38906a1b4f8287678f95f378744c5b46b1985588543f3f19046e"
555 url: "https://pub.dev"
556 source: hosted
533 - version: "3.3.1"
557 + version: "3.3.2"
558 flutter_secure_storage:
559 dependency: "direct main"
560 description:
561 name: flutter_secure_storage
538 - sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea"
562 + sha256: "7686b1d6a29985dcbb808c59518226e603e3bfa7c0ddfd1a0d00e4cda77c868e"
563 url: "https://pub.dev"
564 source: hosted
541 - version: "9.2.4"
542 - flutter_secure_storage_linux:
565 + version: "10.3.1"
566 + flutter_secure_storage_darwin:
567 dependency: transitive
568 description:
545 - name: flutter_secure_storage_linux
546 - sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688
569 + name: flutter_secure_storage_darwin
570 + sha256: "82329fa5cdf343773b1b6897dea959105a29f092454259edff92f9f6637e8149"
571 url: "https://pub.dev"
572 source: hosted
549 - version: "1.2.3"
550 - flutter_secure_storage_macos:
573 + version: "0.3.2"
574 + flutter_secure_storage_linux:
575 dependency: transitive
576 description:
553 - name: flutter_secure_storage_macos
554 - sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247"
577 + name: flutter_secure_storage_linux
578 + sha256: a5f35ddab43cf5c8215d2feb4ce1957851f28c5c37e6f04335066a0602087bf5
579 url: "https://pub.dev"
580 source: hosted
557 - version: "3.1.3"
581 + version: "3.0.1"
582 flutter_secure_storage_platform_interface:
583 dependency: transitive
584 description:
585 name: flutter_secure_storage_platform_interface
562 - sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
586 + sha256: "8ceea1223bee3c6ac1a22dabd8feefc550e4729b3675de4b5900f55afcb435d6"
587 url: "https://pub.dev"
588 source: hosted
565 - version: "1.1.2"
589 + version: "2.0.1"
590 flutter_secure_storage_web:
591 dependency: transitive
592 description:
593 name: flutter_secure_storage_web
570 - sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
594 + sha256: "073a62b3aeb866ab4ce795f960413948e51e5a42a9b0c8333b6daf5bb3208a1c"
595 url: "https://pub.dev"
596 source: hosted
573 - version: "1.2.1"
597 + version: "2.1.1"
598 flutter_secure_storage_windows:
599 dependency: transitive
600 description:
601 name: flutter_secure_storage_windows
578 - sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
602 + sha256: "471951813a97006d899db4948acc654a4f28c440083ea08178935ce20b173ec1"
603 url: "https://pub.dev"
604 source: hosted
581 - version: "3.1.2"
605 + version: "4.2.2"
606 flutter_shaders:
607 dependency: transitive
608 description:
@@ -633,18 +657,18 @@ packages:
657 dependency: "direct main"
658 description:
659 name: go_router
636 - sha256: "92d8cee7c57dff0a6c409c05597b460002434eccf7424a712283225b3962d03f"
660 + sha256: "5922b2861e2235a3504896f0d6fa07d84141b480cf52eecd2f42cd25585a9e8a"
661 url: "https://pub.dev"
662 source: hosted
639 - version: "17.2.3"
663 + version: "17.3.0"
664 google_fonts:
665 dependency: "direct main"
666 description:
667 name: google_fonts
644 - sha256: ba03d03bcaa2f6cb7bd920e3b5027181db75ab524f8891c8bc3aa603885b8055
668 + sha256: "4e9391085e524954a51e3625b7c9c7e9851dc3f376603208bb45c24b9a66255d"
669 url: "https://pub.dev"
670 source: hosted
647 - version: "6.3.3"
671 + version: "8.1.0"
672 gpt_markdown:
673 dependency: "direct main"
674 description:
@@ -769,10 +793,10 @@ packages:
793 dependency: "direct main"
794 description:
795 name: just_audio
772 - sha256: f978d5b4ccea08f267dae0232ec5405c1b05d3f3cd63f82097ea46c015d5c09e
796 + sha256: "9694e4734f515f2a052493d1d7e0d6de219ee0427c7c29492e246ff32a219908"
797 url: "https://pub.dev"
798 source: hosted
775 - version: "0.9.46"
799 + version: "0.10.5"
800 just_audio_background:
801 dependency: "direct main"
802 description:
@@ -801,10 +825,10 @@ packages:
825 dependency: transitive
826 description:
827 name: large_file_handler
804 - sha256: c2ecaf552e8039119bbe3a094d15aecbafe833899f30b2dcfbb513eddf76b36f
828 + sha256: "1657db12b4591242b186c23eee437997be91e655ab0d803fd74829074460757f"
829 url: "https://pub.dev"
830 source: hosted
807 - version: "0.3.1"
831 + version: "0.4.0"
832 leak_tracker:
833 dependency: transitive
834 description:
@@ -873,18 +897,18 @@ packages:
897 dependency: transitive
898 description:
899 name: lucide_icons_flutter
876 - sha256: "32d30fd06a84308204e0a2f7725b846a4b289d0ffefd880120989045c760cf94"
900 + sha256: "7c5dc01a32a9905ae34e2d84224e92d6d0c42acf8926df9e01c35a1446bf1b69"
901 url: "https://pub.dev"
902 source: hosted
879 - version: "3.1.14"
903 + version: "3.1.14+2"
904 matcher:
905 dependency: transitive
906 description:
907 name: matcher
884 - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
908 + sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
909 url: "https://pub.dev"
910 source: hosted
887 - version: "0.12.19"
911 + version: "0.12.18"
912 material_color_utilities:
913 dependency: transitive
914 description:
@@ -909,14 +933,22 @@ packages:
933 url: "https://pub.dev"
934 source: hosted
935 version: "2.0.0"
936 + mutex:
937 + dependency: transitive
938 + description:
939 + name: mutex
940 + sha256: "8827da25de792088eb33e572115a5eb0d61d61a3c01acbc8bcbe76ed78f1a1f2"
941 + url: "https://pub.dev"
942 + source: hosted
943 + version: "3.1.0"
944 native_toolchain_c:
945 dependency: transitive
946 description:
947 name: native_toolchain_c
916 - sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572"
948 + sha256: "8aaead321425bd3f03bd5894aa27c8ea6993eab95531da7e59f5d39c6e5708ec"
949 url: "https://pub.dev"
950 source: hosted
919 - version: "0.17.6"
951 + version: "0.18.0"
952 nested:
953 dependency: transitive
954 description:
@@ -937,34 +969,26 @@ packages:
969 dependency: "direct main"
970 description:
971 name: objectbox
940 - sha256: "83d58e0ab5c4180a2f67086c449a4f2d1475932b31819271923dfc82a76f73c6"
972 + sha256: f68d74bfdfa6758cebf4cd319db1f4a4386c79ba8896d19e2e999faf041bac78
973 url: "https://pub.dev"
974 source: hosted
943 - version: "5.3.1"
975 + version: "5.3.2"
976 objectbox_flutter_libs:
977 dependency: "direct main"
978 description:
979 name: objectbox_flutter_libs
948 - sha256: fd4e8ed03e2b2bfeb8aa965435d7c5523ec7e962f1ffecf6e7da6c1f4d170419
980 + sha256: "604d26fac4548693449c9ea802e007d4adf7f31e361ae3ebf362ac142295078d"
981 url: "https://pub.dev"
982 source: hosted
951 - version: "5.3.1"
983 + version: "5.3.2"
984 objectbox_generator:
985 dependency: "direct dev"
986 description:
987 name: objectbox_generator
956 - sha256: daa95f21c7140c619ffc1abee2465541d4f0317b8c3bbf2141be1a9e5c507a1d
957 - url: "https://pub.dev"
958 - source: hosted
959 - version: "5.3.1"
960 - objective_c:
961 - dependency: transitive
962 - description:
963 - name: objective_c
964 - sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52"
988 + sha256: b5f6b9e6062635689d777ba2b3a16548991eb1e61007baf607022537e3bc0436
989 url: "https://pub.dev"
990 source: hosted
967 - version: "9.3.0"
991 + version: "5.3.2"
992 package_config:
993 dependency: transitive
994 description:
@@ -977,18 +1001,18 @@ packages:
1001 dependency: transitive
1002 description:
1003 name: package_info_plus
980 - sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20"
1004 + sha256: "4bf625947f6c7713ee242296a682e23e44823c09cf9d79e4f1238923c92db852"
1005 url: "https://pub.dev"
1006 source: hosted
983 - version: "9.0.1"
1007 + version: "10.1.0"
1008 package_info_plus_platform_interface:
1009 dependency: transitive
1010 description:
1011 name: package_info_plus_platform_interface
988 - sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
1012 + sha256: db762cb2f4f25ee60fb6359773861b0f199e00b90d237bd85a76a1e806b46ef4
1013 url: "https://pub.dev"
1014 source: hosted
991 - version: "3.2.1"
1015 + version: "4.1.0"
1016 path:
1017 dependency: "direct main"
1018 description:
@@ -1025,10 +1049,10 @@ packages:
1049 dependency: transitive
1050 description:
1051 name: path_provider_foundation
1028 - sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
1052 + sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4"
1053 url: "https://pub.dev"
1054 source: hosted
1031 - version: "2.6.0"
1055 + version: "2.5.1"
1056 path_provider_linux:
1057 dependency: transitive
1058 description:
@@ -1137,10 +1161,10 @@ packages:
1161 dependency: transitive
1162 description:
1163 name: riverpod
1140 - sha256: "8c22216be8ad3ef2b44af3a329693558c98eca7b8bd4ef495c92db0bba279f83"
1164 + sha256: "17100416c51db7810c71a7bb2c34d1f881faa0074fd452afb0c4db6f8f126c76"
1165 url: "https://pub.dev"
1166 source: hosted
1143 - version: "3.2.1"
1167 + version: "3.3.2"
1168 rxdart:
1169 dependency: transitive
1170 description:
@@ -1161,10 +1185,10 @@ packages:
1185 dependency: "direct main"
1186 description:
1187 name: shadcn_ui
1164 - sha256: "6c06f2bcebd8734b9ed0bf3f63ef5c71981573d5664923589b0302f8280e7eaf"
1188 + sha256: "132d69a43145a9ead0687935b48224a6565d164ab718796fa344a179a69bed70"
1189 url: "https://pub.dev"
1190 source: hosted
1167 - version: "0.53.6"
1191 + version: "0.54.0"
1192 shared_preferences:
1193 dependency: "direct main"
1194 description:
@@ -1334,18 +1358,18 @@ packages:
1358 dependency: transitive
1359 description:
1360 name: slang
1337 - sha256: ea6702ed6b1c82065fb2de906fe34ac9298117342e3c2ea2567132efdc81bd17
1361 + sha256: fe12e1eeb1fae0dd8e4e453ef6bb39dabb1fa2460a33ebfcdd3f84c8748d28b6
1362 url: "https://pub.dev"
1363 source: hosted
1340 - version: "4.14.0"
1364 + version: "4.16.0"
1365 slang_flutter:
1366 dependency: transitive
1367 description:
1368 name: slang_flutter
1345 - sha256: dcc4e77527c91b12348fc8bdd43d3eb92d8cea37c12a23a1f9719cdc12c804c6
1369 + sha256: ee9dbb86bc58994eb0a5cd5f17de484daa70e7224c62cc69b8620065b213dc73
1370 url: "https://pub.dev"
1371 source: hosted
1348 - version: "4.14.0"
1372 + version: "4.16.0"
1373 source_gen:
1374 dependency: transitive
1375 description:
@@ -1378,6 +1402,30 @@ packages:
1402 url: "https://pub.dev"
1403 source: hosted
1404 version: "1.10.2"
1405 + speech_to_text:
1406 + dependency: "direct main"
1407 + description:
1408 + name: speech_to_text
1409 + sha256: "75587f7400f485fdf166beacd471549d98fe5d58e634f708916bb65dec05d6a4"
1410 + url: "https://pub.dev"
1411 + source: hosted
1412 + version: "7.4.0"
1413 + speech_to_text_platform_interface:
1414 + dependency: transitive
1415 + description:
1416 + name: speech_to_text_platform_interface
1417 + sha256: a7e16e02853853ed7534ac2bde9a1c4f39c8879970a7974ac6ff832d4bdaa4b0
1418 + url: "https://pub.dev"
1419 + source: hosted
1420 + version: "2.4.0"
1421 + speech_to_text_windows:
1422 + dependency: transitive
1423 + description:
1424 + name: speech_to_text_windows
1425 + sha256: "2d1d10565b23262386b453b33656299608dc7a66784453735d6c1318f13f44d7"
1426 + url: "https://pub.dev"
1427 + source: hosted
1428 + version: "1.0.1"
1429 sqflite:
1430 dependency: transitive
1431 description:
@@ -1422,10 +1470,10 @@ packages:
1470 dependency: transitive
1471 description:
1472 name: sqlite3
1425 - sha256: "56da3e13ed7d28a66f930aa2b2b29db6736a233f08283326e96321dd812030f5"
1473 + sha256: "37356bcb56ce0d9404d602c41e4bdb7765e7e9732a3e47adb3d98c556a6abdad"
1474 url: "https://pub.dev"
1475 source: hosted
1428 - version: "3.3.1"
1476 + version: "3.3.3"
1477 stack_trace:
1478 dependency: transitive
1479 description:
@@ -1495,26 +1543,26 @@ packages:
1543 dependency: transitive
1544 description:
1545 name: test
1498 - sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7"
1546 + sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a"
1547 url: "https://pub.dev"
1548 source: hosted
1501 - version: "1.30.0"
1549 + version: "1.29.0"
1550 test_api:
1551 dependency: transitive
1552 description:
1553 name: test_api
1506 - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
1554 + sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
1555 url: "https://pub.dev"
1556 source: hosted
1509 - version: "0.7.10"
1557 + version: "0.7.9"
1558 test_core:
1559 dependency: transitive
1560 description:
1561 name: test_core
1514 - sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51"
1562 + sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943"
1563 url: "https://pub.dev"
1564 source: hosted
1517 - version: "0.6.16"
1565 + version: "0.6.15"
1566 theme_extensions_builder_annotation:
1567 dependency: transitive
1568 description:
@@ -1527,10 +1575,10 @@ packages:
1575 dependency: transitive
1576 description:
1577 name: timezone
1530 - sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1
1578 + sha256: "784a5e34d2eb62e1326f24d6f600aaaee452eb8ca8ef2f384a59244e292d158b"
1579 url: "https://pub.dev"
1580 source: hosted
1533 - version: "0.10.1"
1581 + version: "0.11.0"
1582 tuple:
1583 dependency: transitive
1584 description:
@@ -1575,10 +1623,10 @@ packages:
1623 dependency: transitive
1624 description:
1625 name: url_launcher_android
1578 - sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572"
1626 + sha256: "17bc677f0b301615530dd1d67e0a9828cafa2d0b6b6eae4cd3679b7eac4a273c"
1627 url: "https://pub.dev"
1628 source: hosted
1581 - version: "6.3.29"
1629 + version: "6.3.30"
1630 url_launcher_ios:
1631 dependency: transitive
1632 description:
@@ -1639,10 +1687,10 @@ packages:
1687 dependency: transitive
1688 description:
1689 name: vector_graphics
1642 - sha256: "4d35a36400983c3457c289d4d553b5308f506ea84f7e51c7a564651b5525209a"
1690 + sha256: "2306c03da2ba81724afeb589c351ebbc0aa7d86005925be8f8735856dbe5e42d"
1691 url: "https://pub.dev"
1692 source: hosted
1645 - version: "1.2.1"
1693 + version: "1.2.2"
1694 vector_graphics_codec:
1695 dependency: transitive
1696 description:
@@ -1655,10 +1703,10 @@ packages:
1703 dependency: transitive
1704 description:
1705 name: vector_graphics_compiler
1658 - sha256: "98e7e94de127b46a86ef46197fff84ff99f3d3b80a708390d717ad731efef598"
1706 + sha256: "7ee12e6dffe0fc8e755179d6d91b3b34f5924223fc104d85572ef9180d73d172"
1707 url: "https://pub.dev"
1708 source: hosted
1661 - version: "1.2.2"
1709 + version: "1.2.5"
1710 vector_math:
1711 dependency: transitive
1712 description:
@@ -1679,10 +1727,10 @@ packages:
1727 dependency: "direct main"
1728 description:
1729 name: wakelock_plus
1682 - sha256: ddf3db70eaa10c37558ff817519b85d527dbd21034fd5d8e1c2e85f31588f1c1
1730 + sha256: "824c5bba0f800e86d32e57d3d1843c531f090005cc89d9a837933e6601093d53"
1731 url: "https://pub.dev"
1732 source: hosted
1685 - version: "1.5.2"
1733 + version: "1.6.1"
1734 wakelock_plus_platform_interface:
1735 dependency: transitive
1736 description:
@@ -1735,18 +1783,18 @@ packages:
1783 dependency: transitive
1784 description:
1785 name: win32
1738 - sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
1786 + sha256: ba6f4bba816c8d7e3c1580e170f3786d216951cc6b94babc3b814c08d2cb2738
1787 url: "https://pub.dev"
1788 source: hosted
1741 - version: "5.15.0"
1789 + version: "6.3.0"
1790 win32_registry:
1791 dependency: transitive
1792 description:
1793 name: win32_registry
1746 - sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
1794 + sha256: "73b1d78920a9d6e03f8b4e43e612b87bf3152a0e5c5e5150267762b7c4116904"
1795 url: "https://pub.dev"
1796 source: hosted
1749 - version: "2.1.0"
1797 + version: "3.0.3"
1798 xdg_directories:
1799 dependency: transitive
1800 description:
@@ -1773,4 +1821,4 @@ packages:
1821 version: "3.1.3"
1822 sdks:
1823 dart: ">=3.11.0 <4.0.0"
1776 - flutter: ">=3.38.4"
1824 + flutter: ">=3.41.0"
pubspec.yaml
+11 -10
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
16 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
17 # In Windows, build-name is used as the major, minor, and patch parts
18 # of the product and file versions while build-number is used as the build suffix.
19 -version: 1.3.8+40
19 +version: 1.3.9+42
20
21 environment:
22 sdk: ^3.11.0
@@ -39,20 +39,20 @@ dependencies:
39 cupertino_icons: ^1.0.8
40 flutter_riverpod: ^3.3.1
41 go_router: ^17.1.0
42 - shadcn_ui: ^0.53.3
42 + shadcn_ui: ^0.54.0
43 url_launcher: ^6.3.1
44 dio: ^5.9.2
45 - flutter_secure_storage: ^9.2.3
45 + flutter_secure_storage: ^10.3.1
46 flutter_tts: ^4.2.5
47 - file_picker: ^10.3.10
47 + file_picker: ^12.0.0-beta.5
48 path_provider: ^2.1.5
49 haptic_feedback: ^0.6.4+3
50 hugeicons: ^1.1.5
51 - google_fonts: ^6.2.1
51 + google_fonts: ^8.1.0
52 syntax_highlight:
53 git:
54 url: https://github.com/abdulmominsakib/syntax_highlight.git
55 - animations: 2.1.2
55 + animations: ^2.2.0
56 cue: ^0.3.1
57 logger: ^2.7.0
58 shared_preferences: ^2.5.5
@@ -61,16 +61,17 @@ dependencies:
61 path: ^1.9.1
62 flutter_gemma: ^0.16.1
63 wakelock_plus: ^1.2.8
64 - flutter_local_notifications: ^18.0.1
64 + flutter_local_notifications: ^22.0.1
65 audioplayers: ^6.4.0
66 - just_audio: ^0.9.43
66 + just_audio: ^0.10.5
67 just_audio_background: ^0.0.1-beta.17
68 - audio_session: ^0.1.25
69 - device_info_plus: ^12.4.0
68 + audio_session: ^0.2.3
69 + device_info_plus: ^13.1.0
70 sherpa_onnx: ^1.13.2
71 archive: ^4.0.2
72 loading_animation_widget: ^1.3.0
73 gpt_markdown: ^1.1.7
74 + speech_to_text: ^7.4.0
75
76 dev_dependencies:
77 flutter_test: