@@ -34,11 +34,14 @@ use warp::tui_export::{
3434use warp_editor:: model:: CoreEditorModel ;
3535use warpui:: SingletonEntity as _;
3636use warpui_core:: elements:: MouseStateHandle ;
37+ #[ cfg( feature = "voice_input" ) ]
3738use warpui_core:: elements:: animation:: AnimationClock ;
3839use warpui_core:: elements:: tui:: { TuiContainer , TuiElement , TuiFlex , TuiHoverable , TuiText } ;
40+ #[ cfg( feature = "voice_input" ) ]
3941use warpui_core:: event:: KeyState ;
4042use warpui_core:: keymap:: macros:: * ;
4143use warpui_core:: keymap:: { self , EditableBinding , FixedBinding , Keystroke } ;
44+ #[ cfg( feature = "voice_input" ) ]
4245use warpui_core:: platform:: keyboard:: KeyCode ;
4346use warpui_core:: text:: { byte_offset_for_char_offset, count_chars_up_to_byte} ;
4447use warpui_core:: {
@@ -64,6 +67,7 @@ use crate::mcp_install_flow::TuiMcpInstallFlowAction;
6467use crate :: read_only_menu:: TuiReadOnlyMenuKind ;
6568use crate :: terminal_session_view:: state:: TuiTerminalSessionStateModel ;
6669use crate :: tui_builder:: TuiUiBuilder ;
70+ #[ cfg( feature = "voice_input" ) ]
6771use crate :: voice_input:: {
6872 TuiVoiceInputEvent , TuiVoiceInputModel , TuiVoiceInputState , VoiceInputStartSource ,
6973} ;
@@ -248,6 +252,7 @@ pub struct TuiInputView {
248252 /// Consults the owner live before an inline-menu Enter can accept an item.
249253 can_accept_inline_menu : Rc < dyn Fn ( & AppContext ) -> bool > ,
250254 /// TUI voice state used for Escape routing and shell-gutter suppression.
255+ #[ cfg( feature = "voice_input" ) ]
251256 voice_input : ModelHandle < TuiVoiceInputModel > ,
252257 /// Vim model (shared FSA + event dispatch layer). Always present but only
253258 /// active when `AppEditorSettings::vim_mode_enabled()` returns `true`.
@@ -332,6 +337,7 @@ impl TuiInputView {
332337 session_state : ModelHandle < TuiTerminalSessionStateModel > ,
333338 ctx : & mut ViewContext < Self > ,
334339 ) -> Self {
340+ #[ cfg( feature = "voice_input" ) ]
335341 let voice_input = ctx. add_model ( |ctx| TuiVoiceInputModel :: new ( input_mode. clone ( ) , ctx) ) ;
336342 let vim_model = ctx. add_model ( |_| VimModel :: new ( ) ) ;
337343 // Subscribe to vim events: VimSubscriber blanket impl (TuiInputView: VimHandler)
@@ -346,15 +352,18 @@ impl TuiInputView {
346352 // on the config (shell-mode gutter/border), so every event re-renders.
347353 ctx. subscribe_to_model ( & input_mode, |_, _, _, ctx| ctx. notify ( ) ) ;
348354 ctx. subscribe_to_model ( & suggestions_mode, |_, _, _, ctx| ctx. notify ( ) ) ;
349- // Only the voice lifecycle state reaches this view's render (the
350- // suppressed shell gutter and the Escape keymap flag). Transcribed text
351- // arrives through `insert_text`, and failure or cancellation notices
352- // render in the session footer, so neither repaints the input.
353- ctx. subscribe_to_model ( & voice_input, |_, _, event, ctx| {
354- if matches ! ( event, TuiVoiceInputEvent :: StateChanged ( _) ) {
355- ctx. notify ( ) ;
356- }
357- } ) ;
355+ #[ cfg( feature = "voice_input" ) ]
356+ {
357+ // Only the voice lifecycle state reaches this view's render (the
358+ // suppressed shell gutter and the Escape keymap flag). Transcribed text
359+ // arrives through `insert_text`, and failure or cancellation notices
360+ // render in the session footer, so neither repaints the input.
361+ ctx. subscribe_to_model ( & voice_input, |_, _, event, ctx| {
362+ if matches ! ( event, TuiVoiceInputEvent :: StateChanged ( _) ) {
363+ ctx. notify ( ) ;
364+ }
365+ } ) ;
366+ }
358367
359368 Self {
360369 model,
@@ -368,6 +377,7 @@ impl TuiInputView {
368377 session_state,
369378 keyboard_enhancement_supported : false ,
370379 can_accept_inline_menu : Rc :: new ( |_| true ) ,
380+ #[ cfg( feature = "voice_input" ) ]
371381 voice_input,
372382 vim_model,
373383 yank_buffer : String :: new ( ) ,
@@ -397,6 +407,7 @@ impl TuiInputView {
397407 . resolve ( ctx)
398408 . is_ok_and ( |state| state. plan_available ( ) )
399409 }
410+
400411 /// Whether vim mode is enabled in settings.
401412 ///
402413 /// Returns `false` when [`AppEditorSettings`] has not been registered in
@@ -432,28 +443,32 @@ impl TuiInputView {
432443 input_mode_policy:: is_shell_mode ( self . input_mode . as_ref ( ctx) )
433444 }
434445
446+ #[ cfg( feature = "voice_input" ) ]
435447 pub ( crate ) fn voice_is_active ( & self , ctx : & AppContext ) -> bool {
436448 self . voice_input . as_ref ( ctx) . is_active ( )
437449 }
438-
450+ # [ cfg ( feature = "voice_input" ) ]
439451 pub ( crate ) fn voice_input_model ( & self ) -> & ModelHandle < TuiVoiceInputModel > {
440452 & self . voice_input
441453 }
442-
454+ # [ cfg ( feature = "voice_input" ) ]
443455 pub ( crate ) fn voice_state ( & self , ctx : & AppContext ) -> TuiVoiceInputState {
444456 self . voice_input . as_ref ( ctx) . state ( )
445457 }
446458
459+ #[ cfg( feature = "voice_input" ) ]
447460 pub ( crate ) fn voice_animation_clock ( & self , ctx : & AppContext ) -> AnimationClock {
448461 self . voice_input . as_ref ( ctx) . animation_clock ( )
449462 }
450463
451464 /// The physical modifier holding the current recording open, set only while
452465 /// a hold-to-talk press started it.
466+ #[ cfg( feature = "voice_input" ) ]
453467 pub ( crate ) fn voice_hold_key ( & self , ctx : & AppContext ) -> Option < KeyCode > {
454468 self . voice_input . as_ref ( ctx) . hold_key ( )
455469 }
456470
471+ #[ cfg( feature = "voice_input" ) ]
457472 pub ( crate ) fn start_voice_input (
458473 & mut self ,
459474 available : bool ,
@@ -465,16 +480,19 @@ impl TuiInputView {
465480 } )
466481 }
467482
483+ #[ cfg( feature = "voice_input" ) ]
468484 pub ( crate ) fn stop_voice_input ( & mut self , ctx : & mut ViewContext < Self > ) {
469485 self . voice_input
470486 . update ( ctx, |voice_input, ctx| voice_input. stop ( ctx) ) ;
471487 }
472488
489+ #[ cfg( feature = "voice_input" ) ]
473490 pub ( crate ) fn stop_active_voice_hold ( & mut self , ctx : & mut ViewContext < Self > ) {
474491 self . voice_input
475492 . update ( ctx, |voice_input, ctx| voice_input. stop_hold ( ctx) ) ;
476493 }
477494
495+ #[ cfg( feature = "voice_input" ) ]
478496 pub ( crate ) fn handle_voice_hold_key (
479497 & mut self ,
480498 key : KeyCode ,
@@ -512,6 +530,7 @@ impl TuiInputView {
512530 }
513531
514532 /// Inserts normalized text at the current cursor without submitting it.
533+ #[ cfg( feature = "voice_input" ) ]
515534 pub ( crate ) fn insert_text ( & mut self , text : & str , ctx : & mut ViewContext < Self > ) {
516535 let text = self . editor_behavior . normalize_text ( text) ;
517536 if !text. is_empty ( ) {
@@ -651,6 +670,7 @@ impl TuiView for TuiInputView {
651670 let inline_menu_owns_input = self
652671 . active_inline_menu_input_ownership ( ctx)
653672 . inline_menu_owns_input ( ) ;
673+ #[ cfg( feature = "voice_input" ) ]
654674 if self . voice_is_active ( ctx) && !inline_menu_owns_input {
655675 return self . render_input ( ctx) ;
656676 }
@@ -693,7 +713,16 @@ impl TuiView for TuiInputView {
693713 input_handles_escape : self . active_inline_menu ( ctx) . is_some ( )
694714 || suggestions_mode. read_only_menu ( ) . is_some ( )
695715 || self . is_shell_mode ( ctx)
696- || self . voice_is_active ( ctx)
716+ || {
717+ #[ cfg( feature = "voice_input" ) ]
718+ {
719+ self . voice_is_active ( ctx)
720+ }
721+ #[ cfg( not( feature = "voice_input" ) ) ]
722+ {
723+ false
724+ }
725+ }
697726 || ( vim_mode_enabled
698727 && ( !matches ! ( vim_state. mode, VimMode :: Normal )
699728 || !vim_state. showcmd . is_empty ( ) ) ) ,
@@ -833,7 +862,11 @@ impl TypedActionView for TuiInputView {
833862 self . close_read_only_menu ( ctx) ;
834863 // In vim normal/visual/replace mode, Enter still submits so the
835864 // prompt behaves like a command line (same as bash/zsh vi-mode).
836- if !self . handle_voice_submit ( ctx) {
865+ #[ cfg( feature = "voice_input" ) ]
866+ if self . handle_voice_submit ( ctx) {
867+ return ;
868+ }
869+ {
837870 self . submit ( ctx) ;
838871 }
839872 TuiEditorInteractionOutcome :: FollowCursor
@@ -1292,6 +1325,7 @@ impl TuiInputView {
12921325 ctx. emit ( TuiInputViewEvent :: Submitted ( text) ) ;
12931326 }
12941327
1328+ #[ cfg( feature = "voice_input" ) ]
12951329 fn handle_voice_submit ( & mut self , ctx : & mut ViewContext < Self > ) -> bool {
12961330 match self . voice_input . as_ref ( ctx) . state ( ) {
12971331 TuiVoiceInputState :: Listening => {
@@ -1406,18 +1440,21 @@ impl TuiInputView {
14061440 return true ;
14071441 }
14081442
1409- match self . voice_input . as_ref ( ctx) . state ( ) {
1410- TuiVoiceInputState :: Listening => {
1411- self . voice_input
1412- . update ( ctx, |voice_input, ctx| voice_input. stop ( ctx) ) ;
1413- return true ;
1414- }
1415- TuiVoiceInputState :: Transcribing => {
1416- self . voice_input
1417- . update ( ctx, |voice_input, ctx| voice_input. cancel ( ctx) ) ;
1418- return true ;
1443+ #[ cfg( feature = "voice_input" ) ]
1444+ {
1445+ match self . voice_input . as_ref ( ctx) . state ( ) {
1446+ TuiVoiceInputState :: Listening => {
1447+ self . voice_input
1448+ . update ( ctx, |voice_input, ctx| voice_input. stop ( ctx) ) ;
1449+ return true ;
1450+ }
1451+ TuiVoiceInputState :: Transcribing => {
1452+ self . voice_input
1453+ . update ( ctx, |voice_input, ctx| voice_input. cancel ( ctx) ) ;
1454+ return true ;
1455+ }
1456+ TuiVoiceInputState :: Idle => { }
14191457 }
1420- TuiVoiceInputState :: Idle => { }
14211458 }
14221459
14231460 // In vim mode, Escape transitions between modes (Insert→Normal,
0 commit comments