Revamp desktop usability and multiline fields

This commit is contained in:
2026-08-10 22:06:16 +02:00
parent 40b15a9713
commit aeb18b488f
12 changed files with 1283 additions and 545 deletions

View File

@@ -12,9 +12,10 @@ use ironstorage::{
/// Presentation state for editing one storage-owned document.
///
/// The focused buffer contains one complete pass entry line. Committing that
/// buffer delegates classification back to `EntryDocument`, so names, duplicate
/// fields, notes, and OTP URI recognition remain storage behavior.
/// The focused buffer contains one complete storage-owned logical field.
/// Committing it delegates classification back to `EntryDocument`, so names,
/// multiline values, duplicate fields, notes, and OTP URI recognition remain
/// storage behavior.
pub struct EntryEditor {
document: EntryDocument,
focused: usize,
@@ -261,6 +262,22 @@ impl EntryEditor {
let Some(field) = self.focused_field() else {
return Ok(());
};
if matches!(
field.metadata().kind(),
EntryFieldKind::Username
| EntryFieldKind::Email
| EntryFieldKind::Url
| EntryFieldKind::Field
| EntryFieldKind::Note
) {
let mut replacement = Vec::with_capacity(self.buffer.expose().len() + 1);
replacement.extend_from_slice(&self.buffer.expose()[..self.cursor]);
replacement.push(b'\n');
replacement.extend_from_slice(&self.buffer.expose()[self.cursor..]);
self.cursor += 1;
self.replace_buffer(replacement);
return Ok(());
}
let id = field.id();
let left = self.buffer.expose()[..self.cursor].to_vec();
let right = self.buffer.expose()[self.cursor..].to_vec();
@@ -281,8 +298,10 @@ impl EntryEditor {
return Ok(());
}
let id = field.id();
self.document
.update(id, EntryFieldDraft::line(self.buffer.expose().to_vec())?)?;
self.document.update(
id,
EntryFieldDraft::multiline(self.buffer.expose().to_vec()),
)?;
self.dirty = true;
Ok(())
}
@@ -303,8 +322,7 @@ impl EntryEditor {
let Some(id) = self.focused_field().map(EntryField::id) else {
return;
};
let draft = EntryFieldDraft::line(self.buffer.expose().to_vec())
.expect("the single-line editor never inserts line endings into its buffer");
let draft = EntryFieldDraft::multiline(self.buffer.expose().to_vec());
self.document
.update(id, draft)
.expect("the focused field identifier belongs to the document");
@@ -376,7 +394,7 @@ mod tests {
}
#[test]
fn add_split_remove_and_reorder_keep_stable_focus() {
fn add_multiline_remove_and_reorder_keep_stable_focus() {
let mut editor = EntryEditor::new(fixture_document("email/personal"));
let initial = editor.document().fields().len();
editor.add_after_focused().expect("add");
@@ -385,11 +403,15 @@ mod tests {
editor.insert_character('b');
editor.move_cursor_left();
editor.split_line().expect("split");
assert_eq!(editor.document().fields().len(), initial + 2);
editor.move_focused_up().expect("move up");
assert_eq!(editor.focused_index(), Some(1));
editor.remove_focused().expect("remove");
assert_eq!(editor.document().fields().len(), initial + 1);
assert_eq!(
editor.focused_contents(editor.focused_field().expect("field").id()),
Some(b"a\nb".as_slice())
);
editor.move_focused_up().expect("move up");
assert_eq!(editor.focused_index(), Some(0));
editor.remove_focused().expect("remove");
assert_eq!(editor.document().fields().len(), initial);
}
#[test]