use super::*;
impl MissionControlState {
pub(crate) fn prompt_text(&self) -> String {
prompt::prompt_text(
&self.input,
self.is_prompt_focused(),
self.prompt_cursor,
self.prompt_cursor_visible,
)
}
pub(crate) fn prompt_text_position_for_cell(
&self,
row: u16,
column: u16,
visible_rows: u16,
wrap_width: u16,
) -> TextPosition {
let visual_row = usize::from(self.prompt_render_scroll(visible_rows, wrap_width))
.saturating_add(usize::from(row));
let byte = prompt::prompt_byte_index_for_visual_position_with_cursor(
&self.input,
visual_row,
usize::from(column),
wrap_width,
(self.is_prompt_focused() && self.prompt_cursor_visible).then_some(self.prompt_cursor),
);
TextPosition::new(byte, visual_row, usize::from(column))
}
pub(crate) fn move_prompt_cursor_to(
&mut self,
byte: usize,
visible_rows: u16,
wrap_width: u16,
) {
self.prompt_cursor = prompt::clamped_byte_index(&self.input, byte);
self.prompt_target_column = None;
self.focus_prompt();
let mut scroll = self.prompt_offset();
prompt::ensure_cursor_visible(
&self.input,
self.prompt_cursor,
&mut scroll,
visible_rows,
wrap_width,
);
self.set_scroll_offset(&self.scroll_views.prompt, scroll);
}
fn with_prompt_edit<R>(&mut self, f: impl FnOnce(&mut prompt::PromptEdit<'_>) -> R) -> R {
let mut scroll = self.prompt_offset();
let result = {
let mut edit = prompt::PromptEdit {
input: &mut self.input,
cursor: &mut self.prompt_cursor,
target_column: &mut self.prompt_target_column,
scroll: &mut scroll,
cursor_visible: &mut self.prompt_cursor_visible,
};
f(&mut edit)
};
self.set_scroll_offset(&self.scroll_views.prompt, scroll);
result
}
pub(crate) fn move_prompt_cursor_left(&mut self, visible_rows: u16, wrap_width: u16) {
let mut scroll = self.prompt_offset();
prompt::move_cursor_left(
&self.input,
&mut self.prompt_cursor,
&mut self.prompt_target_column,
&mut scroll,
&mut self.prompt_cursor_visible,
visible_rows,
wrap_width,
);
self.set_scroll_offset(&self.scroll_views.prompt, scroll);
}
pub(crate) fn move_prompt_cursor_right(&mut self, visible_rows: u16, wrap_width: u16) {
let mut scroll = self.prompt_offset();
prompt::move_cursor_right(
&self.input,
&mut self.prompt_cursor,
&mut self.prompt_target_column,
&mut scroll,
&mut self.prompt_cursor_visible,
visible_rows,
wrap_width,
);
self.set_scroll_offset(&self.scroll_views.prompt, scroll);
}
pub(crate) fn move_prompt_cursor_up(&mut self, visible_rows: u16, wrap_width: u16) {
self.with_prompt_edit(|edit| {
prompt::move_cursor_vertical(edit, -1, visible_rows, wrap_width)
});
}
pub(crate) fn move_prompt_cursor_down(&mut self, visible_rows: u16, wrap_width: u16) {
self.with_prompt_edit(|edit| {
prompt::move_cursor_vertical(edit, 1, visible_rows, wrap_width)
});
}
pub(crate) fn insert_prompt_char(&mut self, ch: char, visible_rows: u16, wrap_width: u16) {
self.with_prompt_edit(|edit| prompt::insert_char(edit, ch, visible_rows, wrap_width));
}
pub(crate) fn insert_prompt_str(&mut self, text: &str, visible_rows: u16, wrap_width: u16) {
self.with_prompt_edit(|edit| prompt::insert_str(edit, text, visible_rows, wrap_width));
}
pub(crate) fn backspace_prompt_char(&mut self, visible_rows: u16, wrap_width: u16) {
let mut scroll = self.prompt_offset();
prompt::backspace_char(
&mut self.input,
&mut self.prompt_cursor,
&mut self.prompt_target_column,
&mut scroll,
&mut self.prompt_cursor_visible,
visible_rows,
wrap_width,
);
self.set_scroll_offset(&self.scroll_views.prompt, scroll);
}
pub(crate) fn delete_prompt_char(&mut self, visible_rows: u16, wrap_width: u16) {
let mut scroll = self.prompt_offset();
prompt::delete_char(
&mut self.input,
&mut self.prompt_cursor,
&mut self.prompt_target_column,
&mut scroll,
&mut self.prompt_cursor_visible,
visible_rows,
wrap_width,
);
self.set_scroll_offset(&self.scroll_views.prompt, scroll);
}
pub(crate) fn clear_prompt_input(&mut self) {
let mut scroll = self.prompt_offset();
prompt::reset_input(
&mut self.input,
&mut self.prompt_cursor,
&mut scroll,
&mut self.prompt_target_column,
&mut self.prompt_cursor_visible,
);
self.set_scroll_offset(&self.scroll_views.prompt, scroll);
self.clear_autocomplete();
}
pub(crate) fn take_prompt_for_submit(&mut self) -> String {
let mut scroll = self.prompt_offset();
let submitted = prompt::take_for_submit(
&mut self.input,
&mut self.prompt_cursor,
&mut scroll,
&mut self.prompt_target_column,
&mut self.prompt_cursor_visible,
);
self.set_scroll_offset(&self.scroll_views.prompt, scroll);
self.clear_autocomplete();
submitted
}
pub(crate) fn autocomplete_visible(&self) -> bool {
autocomplete::visible(&self.autocomplete)
}
pub(crate) fn clear_autocomplete(&mut self) {
autocomplete::clear(
&mut self.autocomplete,
&mut self.autocomplete_dismissed_token,
);
}
pub(crate) fn reset_for_new_session(&mut self) {
self.nodes.clear();
self.roots.clear();
self.children.clear();
self.expanded.clear();
self.transcript.clear();
self.transcript_activity_links.clear();
self.active_tool_transcripts.clear();
self.running_prompt = None;
self.assistant_streaming = false;
self.active_assistant_text.clear();
self.active_assistant_redactor = StreamingRedactor::default();
self.active_assistant_transcript_index = None;
self.active_thinking_text.clear();
self.active_thinking_redactor = StreamingRedactor::default();
self.active_thinking_control_sanitizer.reset();
self.active_thinking_transcript_index = None;
self.context_usage = None;
self.pending_steering_count = 0;
self.selected = 0;
self.reset_core_scroll_views();
self.activity_follow_tail = false;
self.modals.system_prompt_modal = None;
self.modals.skills_modal = None;
self.modals.session_picker = None;
self.selection = None;
self.toast = None;
self.clear_autocomplete();
self.invalidate_transcript_cache();
self.invalidate_activity_render_cache();
}
pub(crate) fn hide_autocomplete(&mut self) {
autocomplete::hide(&mut self.autocomplete);
}
pub(crate) fn recompute_autocomplete(&mut self, candidates: &[AutocompleteCandidate]) {
autocomplete::recompute(
&self.input,
self.prompt_cursor,
&mut self.autocomplete,
&self.autocomplete_dismissed_token,
candidates,
);
}
pub(crate) fn dismiss_autocomplete_for_current_token(&mut self) {
autocomplete::dismiss_for_current_token(
&mut self.autocomplete,
&mut self.autocomplete_dismissed_token,
);
}
pub(crate) fn move_autocomplete_selection_down(&mut self) {
autocomplete::move_selection_down(&mut self.autocomplete);
}
pub(crate) fn move_autocomplete_selection_up(&mut self) {
autocomplete::move_selection_up(&mut self.autocomplete);
}
pub(crate) fn accept_autocomplete(&mut self, visible_rows: u16, wrap_width: u16) -> bool {
let mut autocomplete = self.autocomplete.take();
let mut dismissed = self.autocomplete_dismissed_token.take();
let accepted = self.with_prompt_edit(|edit| {
autocomplete::accept(
edit,
&mut autocomplete,
&mut dismissed,
visible_rows,
wrap_width,
)
});
self.autocomplete = autocomplete;
self.autocomplete_dismissed_token = dismissed;
accepted
}
pub(crate) fn scroll_prompt_up_by(
&mut self,
rows: u16,
visible_rows: u16,
wrap_width: u16,
) -> bool {
let prompt_text = self.prompt_text();
let before = self.prompt_offset();
let mut scroll = before;
let changed =
prompt::scroll_up_by(&prompt_text, &mut scroll, rows, visible_rows, wrap_width);
self.set_scroll_offset(&self.scroll_views.prompt, scroll);
changed && self.prompt_offset() != before
}
pub(crate) fn scroll_prompt_down_by(
&mut self,
rows: u16,
visible_rows: u16,
wrap_width: u16,
) -> bool {
let prompt_text = self.prompt_text();
let before = self.prompt_offset();
let mut scroll = before;
let changed =
prompt::scroll_down_by(&prompt_text, &mut scroll, rows, visible_rows, wrap_width);
self.set_scroll_offset(&self.scroll_views.prompt, scroll);
changed && self.prompt_offset() != before
}
#[cfg(test)]
pub(crate) fn ensure_prompt_cursor_visible(&mut self, visible_rows: u16, wrap_width: u16) {
let mut scroll = self.prompt_offset();
prompt::ensure_cursor_visible(
&self.input,
self.prompt_cursor,
&mut scroll,
visible_rows,
wrap_width,
);
self.set_scroll_offset(&self.scroll_views.prompt, scroll);
}
pub(crate) fn prompt_visible_rows(&self, wrap_width: u16) -> u16 {
let prompt_text = self.prompt_text();
prompt::visible_rows(&prompt_text, wrap_width)
}
pub(crate) fn prompt_render_scroll(&self, _visible_rows: u16, _wrap_width: u16) -> u16 {
self.prompt_offset().min(u16::MAX as usize) as u16
}
#[cfg(test)]
pub(crate) fn prompt_visual_rows(&self, wrap_width: u16) -> usize {
let prompt_text = self.prompt_text();
prompt::visual_rows(&prompt_text, wrap_width)
}
}