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: