···145 if cleaned.len() <= max_len {
146 cleaned
147 } else {
148+ // Use char boundary-safe truncation to avoid panic on multibyte chars
149+ let truncated: String = cleaned.chars().take(max_len - 3).collect();
150+ format!("{}...", truncated)
151 }
152}
153
+3-2
crates/weaver-app/src/components/record_editor.rs
···1350 if let Some(new_collection_str) = data.type_discriminator() {
1351 let new_collection = Nsid::new(new_collection_str).ok();
1352 if let Some(new_collection) = new_collection {
1353- // Create new record
01354 let create_req = CreateRecord::new()
1355 .repo(AtIdentifier::Did(did.clone()))
1356 .collection(new_collection)
···1360 match fetcher.send(create_req).await {
1361 Ok(response) => {
1362 if let Ok(create_output) = response.into_output() {
1363- // Delete old record
1364 if let (Some(old_collection_str), Some(old_rkey)) = (uri.collection(), uri.rkey()) {
1365 let old_collection = Nsid::new(old_collection_str.as_str()).ok();
1366 if let Some(old_collection) = old_collection {
···1350 if let Some(new_collection_str) = data.type_discriminator() {
1351 let new_collection = Nsid::new(new_collection_str).ok();
1352 if let Some(new_collection) = new_collection {
1353+ // Create new record first - if this fails, user keeps their old record
1354+ // If delete fails after, user has duplicates (recoverable) rather than data loss
1355 let create_req = CreateRecord::new()
1356 .repo(AtIdentifier::Did(did.clone()))
1357 .collection(new_collection)
···1361 match fetcher.send(create_req).await {
1362 Ok(response) => {
1363 if let Ok(create_output) = response.into_output() {
1364+ // Delete old record after successful create
1365 if let (Some(old_collection_str), Some(old_rkey)) = (uri.collection(), uri.rkey()) {
1366 let old_collection = Nsid::new(old_collection_str.as_str()).ok();
1367 if let Some(old_collection) = old_collection {