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

@@ -172,7 +172,6 @@ impl EntryFieldDraft {
pub fn field(name: impl Into<String>, value: Vec<u8>) -> Result<Self, DocumentError> {
let name = name.into();
validate_name(&name)?;
validate_line(&value)?;
let value = SecretBytes::new(value);
let mut contents = Vec::with_capacity(name.len() + value.expose().len() + 2);
contents.extend_from_slice(name.as_bytes());
@@ -197,6 +196,12 @@ impl EntryFieldDraft {
}
}
pub fn multiline(contents: Vec<u8>) -> Self {
Self {
contents: SecretBytes::new(contents),
}
}
fn render(self) -> SecretBytes {
self.contents
}
@@ -283,9 +288,8 @@ impl EntryDocument {
let field = self.field(id).ok_or(DocumentError::UnknownField { id })?;
let draft = match field.metadata().kind() {
EntryFieldKind::OtpUri => EntryFieldDraft::otp_uri(value)?,
EntryFieldKind::Password | EntryFieldKind::Note | EntryFieldKind::Blank => {
EntryFieldDraft::line(value)?
}
EntryFieldKind::Password => EntryFieldDraft::line(value)?,
EntryFieldKind::Note | EntryFieldKind::Blank => EntryFieldDraft::multiline(value),
EntryFieldKind::Username
| EntryFieldKind::Email
| EntryFieldKind::Url
@@ -540,7 +544,32 @@ fn parse_lines(contents: &[u8]) -> Vec<EntryField> {
});
start = end;
}
fields
let mut logical = Vec::<EntryField>::new();
for field in fields {
let continuation = classify(logical.len(), field.contents.expose()).kind
== EntryFieldKind::Note
&& logical.last().is_some_and(|previous| {
let metadata =
classify(logical.len().saturating_sub(1), previous.contents.expose());
metadata.name.is_some() && metadata.kind != EntryFieldKind::OtpUri
});
if continuation {
let previous = logical
.last_mut()
.expect("continuation has a previous field");
let mut contents = previous.contents.expose().to_vec();
contents.extend_from_slice(&previous.ending);
contents.extend_from_slice(field.contents.expose());
previous.contents = SecretBytes::new(contents);
previous.ending = field.ending;
} else {
logical.push(field);
}
}
for (id, field) in logical.iter_mut().enumerate() {
field.id = EntryFieldId(id as u64);
}
logical
}
fn classify_all(fields: &mut [EntryField]) {
@@ -595,8 +624,13 @@ fn classify(index: usize, line: &[u8]) -> EntryFieldMetadata {
value: 0..line.len(),
};
}
if let Some(colon) = line.iter().position(|byte| *byte == b':') {
let raw_name = trim_ascii(&line[..colon]);
let first_line_end = line
.iter()
.position(|byte| *byte == b'\n')
.unwrap_or(line.len());
let first_line = &line[..first_line_end];
if let Some(colon) = first_line.iter().position(|byte| *byte == b':') {
let raw_name = trim_ascii(&first_line[..colon]);
if !raw_name.is_empty()
&& let Ok(name) = std::str::from_utf8(raw_name)
{