diff --git a/lib/app.dart b/lib/app.dart index 009d840..8c7f297 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -29,6 +29,8 @@ import 'screens/settings/settings_screen.dart'; import 'screens/setup/setup_screen.dart'; import 'screens/splash/splash_screen.dart'; import 'screens/tasks/task_edit_screen.dart'; +import 'providers/voice_provider.dart'; +import 'widgets/voice_mic_button.dart'; // ChangeNotifier that fires when auth or server URL changes, // used as GoRouter.refreshListenable so the router re-evaluates redirects @@ -399,6 +401,7 @@ class _QuickCaptureBarState extends ConsumerState<_QuickCaptureBar> { @override void dispose() { _controller.dispose(); + ref.read(voiceProvider.notifier).exitVoiceMode(); super.dispose(); } @@ -440,6 +443,25 @@ class _QuickCaptureBarState extends ConsumerState<_QuickCaptureBar> { } } + Future _toggleCaptureMic() async { + final voice = ref.read(voiceProvider); + if (voice.voiceModeActive) { + ref.read(voiceProvider.notifier).exitVoiceMode(); + return; + } + await ref.read(voiceProvider.notifier).enterVoiceMode( + onTranscript: (transcript) async { + ref.read(captureWorkQueueProvider.notifier).enqueue(transcript); + }, + enableTts: false, + onError: (msg) { + if (!mounted) return; + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(msg))); + }, + ); + } + String _hintForLocation(String location) { if (location.startsWith(Routes.knowledge)) return 'Capture a note…'; if (location.startsWith(Routes.projects)) return 'Capture a note…'; @@ -482,7 +504,9 @@ class _QuickCaptureBarState extends ConsumerState<_QuickCaptureBar> { onSubmitted: (_) => _submit(), onChanged: (_) => setState(() {}), decoration: InputDecoration( - hintText: _hintForLocation(location), + hintText: ref.watch(voiceProvider).voiceModeActive + ? 'Listening…' + : _hintForLocation(location), isDense: true, contentPadding: const EdgeInsets.symmetric( horizontal: 14, vertical: 10), @@ -512,6 +536,11 @@ class _QuickCaptureBarState extends ConsumerState<_QuickCaptureBar> { ), ), ), + VoiceMicButton( + mode: ref.watch(voiceProvider).mode, + voiceModeActive: ref.watch(voiceProvider).voiceModeActive, + onTap: _toggleCaptureMic, + ), IconButton( icon: const Icon(Icons.settings_outlined), tooltip: 'Settings',