notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 35cd2a2ad41f20e101d024c1fc28cd4a1f1d50d7
parent 8a454b8e63aa90ecd69e43264ec304ac735083db
Author: William Casarin <jb55@jb55.com>
Date:   Thu, 26 Feb 2026 09:05:01 -0800

dave: clamp input box height to prevent overflow off-screen

Use allocate_ui with a line-count-based height (clamped to 1-8 lines),
matching the pattern used in the messages app composer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Diffstat:
Mcrates/notedeck_dave/src/ui/dave.rs | 159+++++++++++++++++++++++++++++++++++++++++--------------------------------------
1 file changed, 83 insertions(+), 76 deletions(-)

diff --git a/crates/notedeck_dave/src/ui/dave.rs b/crates/notedeck_dave/src/ui/dave.rs @@ -1194,100 +1194,107 @@ impl<'a> DaveUi<'a> { fn inputbox(&mut self, app_ctx: &mut AppContext, ui: &mut egui::Ui) -> DaveResponse { let i18n = &mut *app_ctx.i18n; - //ui.add_space(Self::chat_margin(ui.ctx()) as f32); - ui.horizontal(|ui| { - ui.with_layout(Layout::right_to_left(Align::Max), |ui| { - let mut dave_response = DaveResponse::none(); - - // Always show Ask button (messages queue while working) - if ui - .add( - egui::Button::new(tr!( - i18n, - "Ask", - "Button to send message to Dave AI assistant" - )) - .min_size(egui::vec2(60.0, 44.0)), - ) - .clicked() - { - dave_response = DaveResponse::send(); - } + // Constrain input height based on line count (min 1, max 8 lines) + let line_count = self.input.lines().count().max(1).clamp(1, 8); + let line_height = 20.0; + let base_height = 44.0; + let input_height = base_height + (line_count as f32 * line_height); + ui.allocate_ui(egui::vec2(ui.available_width(), input_height), |ui| { + ui.horizontal(|ui| { + ui.with_layout(Layout::right_to_left(Align::Max), |ui| { + let mut dave_response = DaveResponse::none(); - // Show Stop button alongside Ask for local working sessions - if self.flags.contains(DaveUiFlags::IsWorking) - && !self.flags.contains(DaveUiFlags::IsRemote) - { + // Always show Ask button (messages queue while working) if ui .add( egui::Button::new(tr!( i18n, - "Stop", - "Button to interrupt/stop the AI operation" + "Ask", + "Button to send message to Dave AI assistant" )) .min_size(egui::vec2(60.0, 44.0)), ) .clicked() { - dave_response = DaveResponse::new(DaveAction::Interrupt); + dave_response = DaveResponse::send(); } - // Show "Press Esc again" indicator when interrupt is pending - if self.flags.contains(DaveUiFlags::InterruptPending) { - ui.label( - egui::RichText::new("Press Esc again to stop") - .color(ui.visuals().warn_fg_color), - ); + // Show Stop button alongside Ask for local working sessions + if self.flags.contains(DaveUiFlags::IsWorking) + && !self.flags.contains(DaveUiFlags::IsRemote) + { + if ui + .add( + egui::Button::new(tr!( + i18n, + "Stop", + "Button to interrupt/stop the AI operation" + )) + .min_size(egui::vec2(60.0, 44.0)), + ) + .clicked() + { + dave_response = DaveResponse::new(DaveAction::Interrupt); + } + + // Show "Press Esc again" indicator when interrupt is pending + if self.flags.contains(DaveUiFlags::InterruptPending) { + ui.label( + egui::RichText::new("Press Esc again to stop") + .color(ui.visuals().warn_fg_color), + ); + } } - } - let r = ui.add( - egui::TextEdit::multiline(self.input) - .desired_width(f32::INFINITY) - .return_key(KeyboardShortcut::new( - Modifiers { - shift: true, - ..Default::default() - }, - Key::Enter, - )) - .hint_text( - egui::RichText::new(tr!( - i18n, - "Ask dave anything...", - "Placeholder text for Dave AI input field" + let r = ui.add( + egui::TextEdit::multiline(self.input) + .desired_width(f32::INFINITY) + .return_key(KeyboardShortcut::new( + Modifiers { + shift: true, + ..Default::default() + }, + Key::Enter, )) - .weak(), - ) - .frame(false), - ); - notedeck_ui::context_menu::input_context( - ui, - &r, - app_ctx.clipboard, - self.input, - notedeck_ui::context_menu::PasteBehavior::Append, - ); + .hint_text( + egui::RichText::new(tr!( + i18n, + "Ask dave anything...", + "Placeholder text for Dave AI input field" + )) + .weak(), + ) + .frame(false), + ); + notedeck_ui::context_menu::input_context( + ui, + &r, + app_ctx.clipboard, + self.input, + notedeck_ui::context_menu::PasteBehavior::Append, + ); - // Request focus if flagged (e.g., after spawning a new agent or entering tentative state) - if *self.focus_requested { - r.request_focus(); - *self.focus_requested = false; - } + // Request focus if flagged (e.g., after spawning a new agent or entering tentative state) + if *self.focus_requested { + r.request_focus(); + *self.focus_requested = false; + } - // Unfocus text input when there's a pending permission request - // UNLESS we're in tentative state (user needs to type message) - let in_tentative_state = - self.permission_message_state != PermissionMessageState::None; - if self.flags.contains(DaveUiFlags::HasPendingPerm) && !in_tentative_state { - r.surrender_focus(); - } + // Unfocus text input when there's a pending permission request + // UNLESS we're in tentative state (user needs to type message) + let in_tentative_state = + self.permission_message_state != PermissionMessageState::None; + if self.flags.contains(DaveUiFlags::HasPendingPerm) && !in_tentative_state { + r.surrender_focus(); + } - if r.has_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { - DaveResponse::send() - } else { - dave_response - } + if r.has_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { + DaveResponse::send() + } else { + dave_response + } + }) + .inner }) .inner })