feat: obscure input component for inputting passwords

This commit is contained in:
sxyazi 2025-04-25 07:04:58 +08:00
parent bfad57d86f
commit 5c527cd5b7
No known key found for this signature in database
11 changed files with 17 additions and 43 deletions

View file

@ -53,6 +53,7 @@ impl Brand {
("WEZTERM_EXECUTABLE", B::WezTerm), ("WEZTERM_EXECUTABLE", B::WezTerm),
("GHOSTTY_RESOURCES_DIR", B::Ghostty), ("GHOSTTY_RESOURCES_DIR", B::Ghostty),
("WT_Session", B::Microsoft), ("WT_Session", B::Microsoft),
("WARP_HONOR_PS1", B::Warp),
("VSCODE_INJECTION", B::VSCode), ("VSCODE_INJECTION", B::VSCode),
("TABBY_CONFIG_DIRECTORY", B::Tabby), ("TABBY_CONFIG_DIRECTORY", B::Tabby),
]; ];

View file

@ -9,10 +9,10 @@ pub struct InputCfg {
pub title: String, pub title: String,
pub value: String, pub value: String,
pub cursor: Option<usize>, pub cursor: Option<usize>,
pub obscure: bool,
pub position: Position, pub position: Position,
pub realtime: bool, pub realtime: bool,
pub completion: bool, pub completion: bool,
pub highlight: bool,
} }
#[derive(Default)] #[derive(Default)]
@ -86,7 +86,6 @@ impl InputCfg {
Self { Self {
title: YAZI.input.shell_title[block as usize].to_owned(), title: YAZI.input.shell_title[block as usize].to_owned(),
position: Position::new(YAZI.input.shell_origin, YAZI.input.shell_offset), position: Position::new(YAZI.input.shell_origin, YAZI.input.shell_offset),
highlight: true,
..Default::default() ..Default::default()
} }
} }

View file

@ -32,9 +32,6 @@ impl Input {
self.tx = Some(opt.tx.clone()); self.tx = Some(opt.tx.clone());
let ticket = self.ticket.clone(); let ticket = self.ticket.clone();
// Shell
self.highlight = opt.cfg.highlight;
// Reset input // Reset input
let cb: InputCallback = Box::new(move |before, after| { let cb: InputCallback = Box::new(move |before, after| {
if opt.cfg.realtime { if opt.cfg.realtime {
@ -46,6 +43,7 @@ impl Input {
self.inner = yazi_widgets::input::Input::new( self.inner = yazi_widgets::input::Input::new(
opt.cfg.value, opt.cfg.value,
opt.cfg.position.offset.width.saturating_sub(YAZI.input.border()) as usize, opt.cfg.position.offset.width.saturating_sub(YAZI.input.border()) as usize,
opt.cfg.obscure,
cb, cb,
); );

View file

@ -15,9 +15,6 @@ pub struct Input {
// Typing // Typing
pub(super) tx: Option<UnboundedSender<Result<String, InputError>>>, pub(super) tx: Option<UnboundedSender<Result<String, InputError>>>,
pub(super) ticket: Rc<Ids>, pub(super) ticket: Rc<Ids>,
// Shell
pub highlight: bool,
} }
impl Deref for Input { impl Deref for Input {

View file

@ -2,7 +2,7 @@ use ratatui::layout::Rect;
use yazi_adapter::Dimension; use yazi_adapter::Dimension;
use yazi_config::popup::{Origin, Position}; use yazi_config::popup::{Origin, Position};
use yazi_fs::File; use yazi_fs::File;
use yazi_shared::{Id, url::Url}; use yazi_shared::url::Url;
use super::{Mimetype, Tabs, Watcher, Yanked}; use super::{Mimetype, Tabs, Watcher, Yanked};
use crate::tab::{Folder, Tab}; use crate::tab::{Folder, Tab};
@ -47,26 +47,12 @@ impl Mgr {
#[inline] #[inline]
pub fn active_mut(&mut self) -> &mut Tab { self.tabs.active_mut() } pub fn active_mut(&mut self) -> &mut Tab { self.tabs.active_mut() }
#[inline]
pub fn active_or(&self, id: Option<Id>) -> &Tab { self.tabs.active_or(id) }
#[inline]
pub fn active_or_mut(&mut self, id: Option<Id>) -> &mut Tab { self.tabs.active_or_mut(id) }
#[inline] #[inline]
pub fn current(&self) -> &Folder { &self.active().current } pub fn current(&self) -> &Folder { &self.active().current }
#[inline] #[inline]
pub fn current_mut(&mut self) -> &mut Folder { &mut self.active_mut().current } pub fn current_mut(&mut self) -> &mut Folder { &mut self.active_mut().current }
#[inline]
pub fn current_or(&self, id: Option<Id>) -> &Folder { &self.active_or(id).current }
#[inline]
pub fn current_or_mut(&mut self, id: Option<Id>) -> &mut Folder {
&mut self.active_or_mut(id).current
}
#[inline] #[inline]
pub fn parent(&self) -> Option<&Folder> { self.active().parent.as_ref() } pub fn parent(&self) -> Option<&Folder> { self.active().parent.as_ref() }

View file

@ -49,20 +49,6 @@ impl Tabs {
#[inline] #[inline]
pub(super) fn active_mut(&mut self) -> &mut Tab { &mut self.items[self.cursor] } pub(super) fn active_mut(&mut self) -> &mut Tab { &mut self.items[self.cursor] }
#[inline]
pub fn active_or(&self, id: Option<Id>) -> &Tab {
id.and_then(|id| self.iter().find(|&t| t.id == id)).unwrap_or(self.active())
}
#[inline]
pub(super) fn active_or_mut(&mut self, id: Option<Id>) -> &mut Tab {
if let Some(i) = id.and_then(|id| self.iter().position(|t| t.id == id)) {
&mut self.items[i]
} else {
self.active_mut()
}
}
#[inline] #[inline]
pub fn current(&self) -> &Folder { &self.active().current } pub fn current(&self) -> &Folder { &self.active().current }

View file

@ -28,6 +28,7 @@ function M:entry(job)
local value, event = ya.input { local value, event = ya.input {
title = string.format('Password for "%s":', from.name), title = string.format('Password for "%s":', from.name),
obscure = true,
position = { "center", w = 50 }, position = { "center", w = 50 },
} }
if event == 1 then if event == 1 then

View file

@ -45,10 +45,10 @@ impl Utils {
title: t.raw_get("title")?, title: t.raw_get("title")?,
value: t.raw_get("value").unwrap_or_default(), value: t.raw_get("value").unwrap_or_default(),
cursor: None, // TODO cursor: None, // TODO
obscure: t.raw_get("obscure").unwrap_or_default(),
position: Pos::new_input(t.raw_get::<Table>("position")?)?.into(), position: Pos::new_input(t.raw_get::<Table>("position")?)?.into(),
realtime, realtime,
completion: false, completion: false,
highlight: false,
})); }));
if !realtime { if !realtime {

View file

@ -13,12 +13,13 @@ pub type InputCallback = Box<dyn Fn(&str, &str)>;
pub struct Input { pub struct Input {
pub snaps: InputSnaps, pub snaps: InputSnaps,
pub limit: usize, pub limit: usize,
pub obscure: bool,
pub callback: Option<InputCallback>, pub callback: Option<InputCallback>,
} }
impl Input { impl Input {
pub fn new(value: String, limit: usize, callback: InputCallback) -> Self { pub fn new(value: String, limit: usize, obscure: bool, callback: InputCallback) -> Self {
Self { snaps: InputSnaps::new(value, limit), limit, callback: Some(callback) } Self { snaps: InputSnaps::new(value, limit), limit, obscure, callback: Some(callback) }
} }
pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool { pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool {
@ -75,7 +76,13 @@ impl Input {
pub fn value(&self) -> &str { &self.snap().value } pub fn value(&self) -> &str { &self.snap().value }
#[inline] #[inline]
pub fn visible_value(&self) -> &str { self.snap().slice(self.snap().window(self.limit)) } pub fn display(&self) -> Cow<str> {
if self.obscure {
"".repeat(self.window(self.limit).len()).into()
} else {
self.snap().slice(self.window(self.limit)).into()
}
}
#[inline] #[inline]
pub fn mode(&self) -> InputMode { self.snap().mode } pub fn mode(&self) -> InputMode { self.snap().mode }

View file

@ -66,7 +66,6 @@ impl InputSnap {
Self::find_window(self.value.chars(), self.offset, limit) Self::find_window(self.value.chars(), self.offset, limit)
} }
#[inline]
pub(super) fn find_window<T>(it: T, offset: usize, limit: usize) -> Range<usize> pub(super) fn find_window<T>(it: T, offset: usize, limit: usize) -> Range<usize>
where where
T: Iterator<Item = char>, T: Iterator<Item = char>,

View file

@ -12,7 +12,7 @@ impl Widget for &Input {
{ {
yazi_plugin::elements::Clear::default().render(area, buf); yazi_plugin::elements::Clear::default().render(area, buf);
Line::styled(self.visible_value(), THEME.input.value).render(area, buf); Line::styled(self.display(), THEME.input.value).render(area, buf);
if let Some(Range { start, end }) = self.selected() { if let Some(Range { start, end }) = self.selected() {
let s = start.min(area.width); let s = start.min(area.width);