Small tweaks

This commit is contained in:
Charles Cunningham
2026-01-15 17:38:37 -08:00
parent 2821aef611
commit ef828ac65a
2 changed files with 48 additions and 70 deletions

View File

@@ -1495,7 +1495,8 @@ impl ChatComposer {
original_text_elements,
original_local_image_paths,
);
self.textarea.set_cursor(original_input.len());
let cursor = original_input.len().min(self.textarea.text().len());
self.textarea.set_cursor(cursor);
return None;
}
}
@@ -1512,7 +1513,8 @@ impl ChatComposer {
original_text_elements,
original_local_image_paths,
);
self.textarea.set_cursor(original_input.len());
let cursor = original_input.len().min(self.textarea.text().len());
self.textarea.set_cursor(cursor);
return None;
}
};
@@ -1612,7 +1614,8 @@ impl ChatComposer {
original_text_elements,
original_local_image_paths,
);
self.textarea.set_cursor(original_input.len());
let cursor = original_input.len().min(self.textarea.text().len());
self.textarea.set_cursor(cursor);
(InputResult::None, true)
}
}
@@ -1860,15 +1863,6 @@ impl ChatComposer {
{
self.handle_paste(pasted);
}
// Backspace at the start of an image placeholder should delete that placeholder (rather
// than deleting content before it). Do this without scanning the full text by consulting
// the textarea's element list.
if matches!(input.code, KeyCode::Backspace)
&& self.try_remove_image_element_at_cursor_start()
{
return (InputResult::None, true);
}
// For non-char inputs (or after flushing), handle normally.
// Track element removals so we can drop any corresponding placeholders without scanning
// the full text. (Placeholders are atomic elements; when deleted, the element disappears.)
@@ -1907,29 +1901,6 @@ impl ChatComposer {
(InputResult::None, true)
}
fn try_remove_image_element_at_cursor_start(&mut self) -> bool {
if self.attached_images.is_empty() {
return false;
}
let p = self.textarea.cursor();
let Some(payload) = self.textarea.element_payload_starting_at(p) else {
return false;
};
let Some(idx) = self
.attached_images
.iter()
.position(|img| img.placeholder == payload)
else {
return false;
};
self.textarea.replace_range(p..p + payload.len(), "");
self.attached_images.remove(idx);
self.relabel_attached_images_and_update_placeholders();
true
}
fn reconcile_deleted_elements(&mut self, elements_before: Vec<String>) {
let elements_after: HashSet<String> =
self.textarea.element_payloads().into_iter().collect();
@@ -4144,6 +4115,24 @@ mod tests {
composer.attach_image(path.clone());
let placeholder = composer.attached_images[0].placeholder.clone();
// Case 0: backspace at start should remove the preceding character, not the placeholder.
composer.set_text_content("A".to_string(), Vec::new(), Vec::new());
composer.attach_image(path.clone());
let placeholder0 = composer.attached_images[0].placeholder.clone();
let start0 = composer
.textarea
.text()
.find(&placeholder0)
.expect("placeholder present");
composer.textarea.set_cursor(start0);
composer.handle_key_event(KeyEvent::new(KeyCode::Backspace, KeyModifiers::NONE));
assert_eq!(composer.textarea.text(), placeholder0);
assert_eq!(composer.attached_images.len(), 1);
composer.set_text_content(String::new(), Vec::new(), Vec::new());
composer.attach_image(path.clone());
let placeholder = composer.attached_images[0].placeholder.clone();
// Case 1: backspace at end
composer.textarea.move_cursor_to_end_of_line(false);
composer.handle_key_event(KeyEvent::new(KeyCode::Backspace, KeyModifiers::NONE));

View File

@@ -1428,7 +1428,8 @@ impl ChatComposer {
original_text_elements,
original_local_image_paths,
);
self.textarea.set_cursor(original_input.len());
let cursor = original_input.len().min(self.textarea.text().len());
self.textarea.set_cursor(cursor);
return None;
}
}
@@ -1445,7 +1446,8 @@ impl ChatComposer {
original_text_elements,
original_local_image_paths,
);
self.textarea.set_cursor(original_input.len());
let cursor = original_input.len().min(self.textarea.text().len());
self.textarea.set_cursor(cursor);
return None;
}
};
@@ -1545,7 +1547,8 @@ impl ChatComposer {
original_text_elements,
original_local_image_paths,
);
self.textarea.set_cursor(original_input.len());
let cursor = original_input.len().min(self.textarea.text().len());
self.textarea.set_cursor(cursor);
(InputResult::None, true)
}
}
@@ -1794,15 +1797,6 @@ impl ChatComposer {
self.handle_paste(pasted);
}
// Backspace at the start of an image placeholder should delete that placeholder (rather
// than deleting content before it). Do this without scanning the full text by consulting
// the textarea's element list.
if matches!(input.code, KeyCode::Backspace)
&& self.try_remove_image_element_at_cursor_start()
{
return (InputResult::None, true);
}
// Track element removals so we can drop any corresponding placeholders without scanning
// the full text. (Placeholders are atomic elements; when deleted, the element disappears.)
let elements_before = if self.pending_pastes.is_empty() && self.attached_images.is_empty() {
@@ -1840,29 +1834,6 @@ impl ChatComposer {
(InputResult::None, true)
}
fn try_remove_image_element_at_cursor_start(&mut self) -> bool {
if self.attached_images.is_empty() {
return false;
}
let p = self.textarea.cursor();
let Some(payload) = self.textarea.element_payload_starting_at(p) else {
return false;
};
let Some(idx) = self
.attached_images
.iter()
.position(|img| img.placeholder == payload)
else {
return false;
};
self.textarea.replace_range(p..p + payload.len(), "");
self.attached_images.remove(idx);
self.relabel_attached_images_and_update_placeholders();
true
}
fn reconcile_deleted_elements(&mut self, elements_before: Vec<String>) {
let elements_after: HashSet<String> =
self.textarea.element_payloads().into_iter().collect();
@@ -4112,6 +4083,24 @@ mod tests {
composer.attach_image(path.clone());
let placeholder = composer.attached_images[0].placeholder.clone();
// Case 0: backspace at start should remove the preceding character, not the placeholder.
composer.set_text_content("A".to_string(), Vec::new(), Vec::new());
composer.attach_image(path.clone());
let placeholder0 = composer.attached_images[0].placeholder.clone();
let start0 = composer
.textarea
.text()
.find(&placeholder0)
.expect("placeholder present");
composer.textarea.set_cursor(start0);
composer.handle_key_event(KeyEvent::new(KeyCode::Backspace, KeyModifiers::NONE));
assert_eq!(composer.textarea.text(), placeholder0);
assert_eq!(composer.attached_images.len(), 1);
composer.set_text_content(String::new(), Vec::new(), Vec::new());
composer.attach_image(path.clone());
let placeholder = composer.attached_images[0].placeholder.clone();
// Case 1: backspace at end
composer.textarea.move_cursor_to_end_of_line(false);
composer.handle_key_event(KeyEvent::new(KeyCode::Backspace, KeyModifiers::NONE));