Fix panic on non-ASCII thread titles in archive search

Richard Feldman created

The archive view's fuzzy_match_positions used chars().enumerate()
which produces character indices, not byte indices. When thread titles
contain multi-byte UTF-8 characters (emoji, CJK, etc.), these character
indices don't correspond to valid byte boundaries, causing a panic in
HighlightedLabel::new.

Switch to char_indices() and eq_ignore_ascii_case() to produce correct
byte positions, matching the approach used by the sidebar's version of
the same function.

Change summary

crates/agent_ui/src/threads_archive_view.rs | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)

Detailed changes

crates/agent_ui/src/threads_archive_view.rs 🔗

@@ -91,14 +91,16 @@ impl TimeBucket {
 }
 
 fn fuzzy_match_positions(query: &str, text: &str) -> Option<Vec<usize>> {
-    let query = query.to_lowercase();
-    let text_lower = text.to_lowercase();
     let mut positions = Vec::new();
     let mut query_chars = query.chars().peekable();
-    for (i, c) in text_lower.chars().enumerate() {
-        if query_chars.peek() == Some(&c) {
-            positions.push(i);
-            query_chars.next();
+    for (byte_idx, candidate_char) in text.char_indices() {
+        if let Some(&query_char) = query_chars.peek() {
+            if candidate_char.eq_ignore_ascii_case(&query_char) {
+                positions.push(byte_idx);
+                query_chars.next();
+            }
+        } else {
+            break;
         }
     }
     if query_chars.peek().is_none() {