Refactor: simplify word navigation logic, improve line wrapping, and enhance parameter initialization across core and TUI modules.
This commit is contained in:
@@ -106,13 +106,11 @@ impl Router {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if pattern.ends_with('*') {
|
if let Some(prefix) = pattern.strip_suffix('*') {
|
||||||
let prefix = &pattern[..pattern.len() - 1];
|
|
||||||
return model.starts_with(prefix);
|
return model.starts_with(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
if pattern.starts_with('*') {
|
if let Some(suffix) = pattern.strip_prefix('*') {
|
||||||
let suffix = &pattern[1..];
|
|
||||||
return model.ends_with(suffix);
|
return model.ends_with(suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ pub struct ChatRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parameters for chat completion
|
/// Parameters for chat completion
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
pub struct ChatParameters {
|
pub struct ChatParameters {
|
||||||
/// Temperature for randomness (0.0 to 2.0)
|
/// Temperature for randomness (0.0 to 2.0)
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@@ -85,20 +85,10 @@ pub struct ChatParameters {
|
|||||||
pub stream: bool,
|
pub stream: bool,
|
||||||
/// Additional provider-specific parameters
|
/// Additional provider-specific parameters
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
#[serde(default)]
|
||||||
pub extra: HashMap<String, serde_json::Value>,
|
pub extra: HashMap<String, serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ChatParameters {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
temperature: None,
|
|
||||||
max_tokens: None,
|
|
||||||
stream: false,
|
|
||||||
extra: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Response from a chat completion request
|
/// Response from a chat completion request
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ChatResponse {
|
pub struct ChatResponse {
|
||||||
|
|||||||
@@ -305,9 +305,7 @@ pub fn find_word_end(line: &str, col: usize) -> Option<usize> {
|
|||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
// Move back one to be ON the last character
|
// Move back one to be ON the last character
|
||||||
if pos > 0 {
|
pos = pos.saturating_sub(1);
|
||||||
pos -= 1;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Skip non-word characters
|
// Skip non-word characters
|
||||||
while pos < chars.len() && !is_word_char(chars[pos]) {
|
while pos < chars.len() && !is_word_char(chars[pos]) {
|
||||||
@@ -317,9 +315,7 @@ pub fn find_word_end(line: &str, col: usize) -> Option<usize> {
|
|||||||
while pos < chars.len() && is_word_char(chars[pos]) {
|
while pos < chars.len() && is_word_char(chars[pos]) {
|
||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
if pos > 0 {
|
pos = pos.saturating_sub(1);
|
||||||
pos -= 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(pos)
|
Some(pos)
|
||||||
@@ -336,9 +332,7 @@ pub fn find_prev_word_boundary(line: &str, col: usize) -> Option<usize> {
|
|||||||
let is_word_char = |c: char| c.is_alphanumeric() || c == '_';
|
let is_word_char = |c: char| c.is_alphanumeric() || c == '_';
|
||||||
|
|
||||||
// Move back one position first
|
// Move back one position first
|
||||||
if pos > 0 {
|
pos = pos.saturating_sub(1);
|
||||||
pos -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip non-word characters
|
// Skip non-word characters
|
||||||
while pos > 0 && !is_word_char(chars[pos]) {
|
while pos > 0 && !is_word_char(chars[pos]) {
|
||||||
|
|||||||
@@ -52,23 +52,21 @@ pub fn build_cursor_map(text: &str, width: u16) -> Vec<ScreenPos> {
|
|||||||
word_start_col = col;
|
word_start_col = col;
|
||||||
word_start_idx = byte_offset + grapheme.len();
|
word_start_idx = byte_offset + grapheme.len();
|
||||||
}
|
}
|
||||||
} else {
|
} else if col + grapheme_width > width {
|
||||||
if col + grapheme_width > width {
|
if word_start_col > 0 && byte_offset == word_start_idx {
|
||||||
if word_start_col > 0 && byte_offset == word_start_idx {
|
// This is the first character of a new word that won't fit, wrap it
|
||||||
// This is the first character of a new word that won't fit, wrap it
|
row += 1;
|
||||||
row += 1;
|
col = grapheme_width;
|
||||||
col = grapheme_width;
|
} else if word_start_col == 0 {
|
||||||
} else if word_start_col == 0 {
|
// No previous word boundary, hard break
|
||||||
// No previous word boundary, hard break
|
row += 1;
|
||||||
row += 1;
|
col = grapheme_width;
|
||||||
col = grapheme_width;
|
|
||||||
} else {
|
|
||||||
// This is part of a word already on the line, let it extend beyond width
|
|
||||||
col += grapheme_width;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
// This is part of a word already on the line, let it extend beyond width
|
||||||
col += grapheme_width;
|
col += grapheme_width;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
col += grapheme_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set position for the end of this grapheme and any intermediate bytes
|
// Set position for the end of this grapheme and any intermediate bytes
|
||||||
|
|||||||
@@ -1320,8 +1320,10 @@ impl ChatApp {
|
|||||||
self.status = format!("Loading model '{}'...", self.controller.selected_model());
|
self.status = format!("Loading model '{}'...", self.controller.selected_model());
|
||||||
self.start_loading_animation();
|
self.start_loading_animation();
|
||||||
|
|
||||||
let mut parameters = ChatParameters::default();
|
let parameters = ChatParameters {
|
||||||
parameters.stream = self.controller.config().general.enable_streaming;
|
stream: self.controller.config().general.enable_streaming,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
// Step 2: Start the actual request
|
// Step 2: Start the actual request
|
||||||
match self
|
match self
|
||||||
@@ -1392,7 +1394,7 @@ impl ChatApp {
|
|||||||
} else {
|
} else {
|
||||||
// If the current model is not in the filtered list, select the first one
|
// If the current model is not in the filtered list, select the first one
|
||||||
self.selected_model = Some(0);
|
self.selected_model = Some(0);
|
||||||
if let Some(model) = filtered_models.get(0) {
|
if let Some(model) = filtered_models.first() {
|
||||||
self.controller.set_model(model.id.clone());
|
self.controller.set_model(model.id.clone());
|
||||||
// Also update the config with the new model and provider
|
// Also update the config with the new model and provider
|
||||||
self.controller.config_mut().general.default_model = Some(model.id.clone());
|
self.controller.config_mut().general.default_model = Some(model.id.clone());
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ pub fn render_chat(frame: &mut Frame<'_>, app: &mut ChatApp) {
|
|||||||
} else {
|
} else {
|
||||||
buffer_text.lines().collect()
|
buffer_text.lines().collect()
|
||||||
};
|
};
|
||||||
let visual_lines = calculate_wrapped_line_count(lines.into_iter(), available_width);
|
let visual_lines = calculate_wrapped_line_count(lines, available_width);
|
||||||
(visual_lines as u16).min(10) + 2 // +2 for borders
|
(visual_lines as u16).min(10) + 2 // +2 for borders
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -283,7 +283,7 @@ fn compute_cursor_metrics(
|
|||||||
|
|
||||||
for (row_idx, line) in lines.iter().enumerate() {
|
for (row_idx, line) in lines.iter().enumerate() {
|
||||||
let display_owned = mask_char.map(|mask| mask_line(line, mask));
|
let display_owned = mask_char.map(|mask| mask_line(line, mask));
|
||||||
let display_line = display_owned.as_deref().unwrap_or_else(|| line.as_str());
|
let display_line = display_owned.as_deref().unwrap_or(line.as_str());
|
||||||
|
|
||||||
let mut segments = if wrap_lines {
|
let mut segments = if wrap_lines {
|
||||||
wrap_line_segments(display_line, content_width)
|
wrap_line_segments(display_line, content_width)
|
||||||
@@ -889,7 +889,7 @@ fn render_input(frame: &mut Frame<'_>, area: Rect, app: &mut ChatApp) {
|
|||||||
let lines: Vec<Line> = if input_text.is_empty() {
|
let lines: Vec<Line> = if input_text.is_empty() {
|
||||||
vec![Line::from("Press 'i' to start typing")]
|
vec![Line::from("Press 'i' to start typing")]
|
||||||
} else {
|
} else {
|
||||||
input_text.lines().map(|line| Line::from(line)).collect()
|
input_text.lines().map(Line::from).collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
let paragraph = Paragraph::new(lines)
|
let paragraph = Paragraph::new(lines)
|
||||||
|
|||||||
Reference in New Issue
Block a user