Add visible styled keyboard quick reference in bottom panel

This commit is contained in:
2026-10-26 22:59:11 +02:00
parent 7d0f006601
commit e141c03c2c
3 changed files with 677 additions and 16 deletions
+127
View File
@@ -39,6 +39,14 @@ pub enum FormAction {
Feedback(String),
}
/// Public, secret-free summary of keyboard capabilities for the active form.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FormKeyContext {
pub enter_action: Option<&'static str>,
pub supports_tab: bool,
pub supports_selection: bool,
}
enum Form {
Setup {
password: SecretInput,
@@ -153,6 +161,67 @@ impl FormController {
matches!(self.active, Some(Form::DraftReview))
}
/// Describes only keys handled by the active form; never exposes form data.
pub fn key_context(&self) -> Option<FormKeyContext> {
self.active.as_ref().map(|form| match form {
Form::Setup { .. } => FormKeyContext {
enter_action: Some("next / submit"),
supports_tab: true,
supports_selection: false,
},
Form::Create { .. } => FormKeyContext {
enter_action: Some("next / submit"),
supports_tab: true,
supports_selection: false,
},
Form::Rename { .. } => FormKeyContext {
enter_action: Some("submit"),
supports_tab: false,
supports_selection: false,
},
Form::Import { .. } => FormKeyContext {
enter_action: Some("submit"),
supports_tab: false,
supports_selection: false,
},
Form::Confirm(_) => FormKeyContext {
enter_action: Some("confirm"),
supports_tab: false,
supports_selection: false,
},
Form::DraftReview => FormKeyContext {
enter_action: None,
supports_tab: false,
supports_selection: false,
},
Form::DeliveryPath { .. } => FormKeyContext {
enter_action: Some("prepare file"),
supports_tab: true,
supports_selection: false,
},
Form::ReissuePassword { .. } => FormKeyContext {
enter_action: Some("submit password"),
supports_tab: false,
supports_selection: false,
},
Form::Backups { items, .. } => FormKeyContext {
enter_action: (!items.is_empty()).then_some("preview restore"),
supports_tab: false,
supports_selection: !items.is_empty(),
},
Form::Restore(_) => FormKeyContext {
enter_action: Some("confirm restore"),
supports_tab: false,
supports_selection: false,
},
Form::Disclosure { .. } => FormKeyContext {
enter_action: None,
supports_tab: false,
supports_selection: false,
},
})
}
pub fn qr_output(&self) -> Option<&QrCodeOutput> {
match self.active.as_ref() {
Some(Form::Disclosure { qr, .. }) => Some(qr),
@@ -888,6 +957,64 @@ mod tests {
use super::*;
use crate::backup::{BackupReason, HistoricalApplyStatus};
#[test]
fn key_context_matches_tabbed_forms_and_inert_draft_review() {
let mut form = FormController::default();
form.open_setup();
assert_eq!(
form.key_context(),
Some(FormKeyContext {
enter_action: Some("next / submit"),
supports_tab: true,
supports_selection: false,
})
);
form.open_create();
assert!(form.key_context().unwrap().supports_tab);
assert_eq!(
form.key_context().unwrap().enter_action,
Some("next / submit")
);
form.open_draft_review();
assert_eq!(
form.key_context(),
Some(FormKeyContext {
enter_action: None,
supports_tab: false,
supports_selection: false,
})
);
assert!(matches!(form.handle_key(KeyCode::Enter), FormAction::None));
}
#[test]
fn delivery_path_and_backup_selection_expose_their_actual_keys() {
let identity = PublicKey::from_bytes([0x61; 32]);
let mut form = FormController::default();
form.open_delivery(identity, false);
assert!(form.key_context().unwrap().supports_tab);
assert_eq!(
form.key_context().unwrap().enter_action,
Some("prepare file")
);
form.open_backups(vec![ArchiveSummary {
file_name: "backup.zip".to_owned(),
created_at_utc: "2026-01-01T00:00:00Z".to_owned(),
reason: BackupReason::Manual,
historical_apply: HistoricalApplyStatus::Unknown,
}]);
assert_eq!(
form.key_context(),
Some(FormKeyContext {
enter_action: Some("preview restore"),
supports_tab: false,
supports_selection: true,
})
);
form.open_backups(Vec::new());
assert_eq!(form.key_context().unwrap().enter_action, None);
}
#[test]
fn setup_password_is_masked_confirmation_required_and_secret_command_has_no_debug() {
let mut form = FormController::default();