fix: ignore NumLock state when comparing keybindings (#4071)

This commit is contained in:
三咲雅 misaki masa 2026-06-20 23:53:21 +08:00 committed by sxyazi
parent 0ba1811af3
commit 4f45ddb514
No known key found for this signature in database
40 changed files with 139 additions and 116 deletions

23
Cargo.lock generated
View file

@ -543,9 +543,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.64"
version = "1.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f"
checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96"
dependencies = [
"find-msvc-tools",
"jobserver",
@ -2880,14 +2880,14 @@ checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "ratatui-core"
version = "0.1.1"
source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbb175c433c8e28a809d1f5773a2ae96e68c0ce40db865cbab1020bf33ae479c"
dependencies = [
"bitflags",
"compact_str",
"critical-section",
"hashbrown 0.17.1",
"indoc",
"itertools",
"kasuari",
"lru",
@ -2902,8 +2902,9 @@ dependencies = [
[[package]]
name = "ratatui-widgets"
version = "0.3.1"
source = "git+https://github.com/yazi-rs/ratatui.git?rev=a1c4922a242861d755163ae85d7e41b85ed277d2#a1c4922a242861d755163ae85d7e41b85ed277d2"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66e3d19bcc9130ca376277d93b60767ff121ace3be06f5f95f81dd68956407d1"
dependencies = [
"bitflags",
"hashbrown 0.17.1",
@ -4705,10 +4706,8 @@ dependencies = [
"indexmap 2.14.0",
"libc",
"mlua",
"parking_lot",
"paste",
"ratatui-core",
"ratatui-widgets",
"scopeguard",
"tokio",
"tokio-stream",
@ -4867,7 +4866,6 @@ dependencies = [
"regex",
"serde",
"serde_with",
"tokio",
"toml",
"tracing",
"yazi-binding",
@ -5343,11 +5341,13 @@ dependencies = [
"mlua",
"parking_lot",
"russh",
"serde",
"tokio",
"toml",
"tracing",
"typed-path",
"yazi-binding",
"yazi-config",
"yazi-codegen",
"yazi-fs",
"yazi-macro",
"yazi-sftp",
@ -5396,6 +5396,7 @@ dependencies = [
"tracing",
"unicode-width",
"yazi-binding",
"yazi-dds",
"yazi-macro",
"yazi-shared",
"yazi-shim",

View file

@ -62,8 +62,8 @@ parking_lot = "0.12.5"
paste = "1.0.15"
percent-encoding = "2.3.2"
rand = { version = "0.10.1", default-features = false, features = [ "std", "sys_rng" ] }
ratatui-core = { version = "0.1.1", default-features = false, features = [ "std", "layout-cache", "serde", "underline-color" ] }
ratatui-widgets = { version = "0.3.1", default-features = false, features = [ "std", "unstable-rendered-line-info" ] }
ratatui-core = { version = "0.1.2", default-features = false, features = [ "std", "layout-cache", "serde", "underline-color" ] }
ratatui-widgets = { version = "0.3.2", default-features = false, features = [ "std", "unstable-rendered-line-info" ] }
regex = "1.12.4"
russh = { version = "0.61.2", default-features = false, features = [ "ring", "rsa" ] }
scopeguard = "1.2.0"
@ -93,7 +93,3 @@ module_inception = "allow"
option_map_unit_fn = "allow"
unit_arg = "allow"
use_self = "warn"
[patch.crates-io]
ratatui-core = { git = "https://github.com/yazi-rs/ratatui.git", package = "ratatui-core", rev = "a1c4922a242861d755163ae85d7e41b85ed277d2" }
ratatui-widgets = { git = "https://github.com/yazi-rs/ratatui.git", package = "ratatui-widgets", rev = "a1c4922a242861d755163ae85d7e41b85ed277d2" }

View file

@ -28,10 +28,6 @@ rustPlatform.buildRustPackage (finalAttrs: {
cargoLock = {
lockFile = "${src}/Cargo.lock";
outputHashes = {
"ratatui-core-0.1.1" = "sha256-wkEOgwAhW0ObtPDlJmOOoY5qf9c/P79ktN4b43jtcGw=";
"ratatui-widgets-0.3.1" = "sha256-wkEOgwAhW0ObtPDlJmOOoY5qf9c/P79ktN4b43jtcGw=";
};
};
env = {

View file

@ -46,10 +46,8 @@ futures = { workspace = true }
hashbrown = { workspace = true }
indexmap = { workspace = true }
mlua = { workspace = true }
parking_lot = { workspace = true }
paste = { workspace = true }
ratatui-core = { workspace = true }
ratatui-widgets = { workspace = true }
scopeguard = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }

View file

@ -1,11 +1,11 @@
use std::ops::DerefMut;
use anyhow::Result;
use ratatui_core::layout::Margin;
use yazi_config::{THEME, YAZI};
use yazi_macro::{act, render, succ};
use yazi_parser::input::ShowForm;
use yazi_shared::data::Data;
use yazi_shim::ratatui::Padable;
use crate::{Actor, Ctx};
@ -19,7 +19,7 @@ impl Actor for Show {
fn act(cx: &mut Ctx, Self::Form { mut opt }: Self::Form) -> Result<Data> {
act!(input:close, cx)?;
let area = cx.mgr.area(opt.position);
let area = cx.mgr.area(opt.position).padding(cx.input.padding());
let input = &mut cx.input;
input.main.visible = true;
input.main.title = opt.title.clone();
@ -28,7 +28,7 @@ impl Actor for Show {
opt.styles = (&THEME.input).into();
opt.blinking = YAZI.input.cursor_blink;
*input.main.deref_mut() = yazi_widgets::input::Input::new(opt)?;
input.main.repos(area.inner(Margin::new(1, 1)));
input.main.repos(area);
succ!(render!());
}

View file

@ -71,7 +71,7 @@ impl From<ratatui_core::layout::Rect> for Area {
}
impl From<Position> for Area {
fn from(value: Position) -> Self { Self::Pos(value.into()) }
fn from(value: Position) -> Self { Self::Pos(value) }
}
impl TryFrom<&AnyUserData> for Area {

View file

@ -19,7 +19,7 @@ impl TryFrom<Table> for Position {
type Error = mlua::Error;
fn try_from(t: Table) -> Result<Self, Self::Error> {
Ok(Self::from(Position {
Ok(Self {
origin: Origin::from_str(&t.raw_get::<mlua::String>(1)?.to_str()?).into_lua_err()?,
offset: Offset {
x: t.raw_get("x").unwrap_or_default(),
@ -28,7 +28,7 @@ impl TryFrom<Table> for Position {
height: t.raw_get("h").unwrap_or_default(),
},
padding: Default::default(),
}))
})
}
}

View file

@ -37,6 +37,5 @@ ratatui-widgets = { workspace = true }
regex = { workspace = true }
serde = { workspace = true }
serde_with = { workspace = true }
tokio = { workspace = true }
toml = { workspace = true }
tracing = { workspace = true }

View file

@ -7,9 +7,8 @@ use serde_with::{DeserializeAs, DisplayFromStr, OneOrMany};
use yazi_binding::Iter;
use yazi_codegen::DeserializeOver2;
use yazi_shared::{Layer, event::{Actions, deserialize_actions}, id::Id};
use yazi_term::event::KeyEvent;
use super::ids::chord_id;
use super::{Key, ids::chord_id};
use crate::{Mixable, Platform, keymap::{ChordArc, Chords}};
static RE: OnceLock<Regex> = OnceLock::new();
@ -19,7 +18,7 @@ pub struct Chord<const L: u8 = { Layer::Null as u8 }> {
#[serde(skip, default = "chord_id")]
pub id: Id,
#[serde(deserialize_with = "deserialize_on")]
pub on: Vec<KeyEvent>,
pub on: Vec<Key>,
#[serde(deserialize_with = "deserialize_actions::<L, _>")]
pub run: Actions,
#[serde(default)]
@ -84,11 +83,11 @@ impl<const L: u8> Mixable for Chord<L> {
fn filter(&self) -> bool { self.r#for.matches() && !self.noop() }
}
fn deserialize_on<'de, D>(deserializer: D) -> Result<Vec<KeyEvent>, D::Error>
fn deserialize_on<'de, D>(deserializer: D) -> Result<Vec<Key>, D::Error>
where
D: Deserializer<'de>,
{
let keys: Vec<KeyEvent> = OneOrMany::<DisplayFromStr>::deserialize_as(deserializer)?;
let keys: Vec<Key> = OneOrMany::<DisplayFromStr>::deserialize_as(deserializer)?;
if keys.is_empty() {
return Err(de::Error::custom("'on' cannot be empty"));
}

View file

@ -1,10 +1,44 @@
use std::{fmt::{Display, Write}, str::FromStr};
use anyhow::bail;
use mlua::{IntoLua, Lua, LuaSerdeExt, Value};
use serde::{Deserialize, Serialize};
use yazi_shim::mlua::SER_OPT;
use yazi_term::event::{KeyCode, KeyEvent, Modifiers};
use crate::event::{KeyCode, KeyEvent, Modifiers};
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Key {
#[serde(flatten)]
pub code: KeyCode,
pub shift: bool,
pub ctrl: bool,
pub alt: bool,
#[serde(rename = "super")]
pub super_: bool,
}
impl FromStr for KeyEvent {
impl Key {
pub fn plain(&self) -> Option<char> {
match self.code {
KeyCode::Char(c) if !self.ctrl && !self.alt && !self.super_ => Some(c),
_ => None,
}
}
}
impl From<KeyEvent> for Key {
fn from(value: KeyEvent) -> Self {
Self {
code: value.code,
shift: value.modifiers.contains(Modifiers::SHIFT),
ctrl: value.modifiers.contains(Modifiers::CONTROL),
alt: value.modifiers.contains(Modifiers::ALT),
super_: value.modifiers.contains(Modifiers::SUPER),
}
}
}
impl FromStr for Key {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
@ -12,19 +46,20 @@ impl FromStr for KeyEvent {
bail!("empty key")
}
let mut key = Self::default();
if !s.starts_with('<') || !s.ends_with('>') {
let c = s.chars().next().unwrap();
return Ok(Self::new(KeyCode::Char(c), Modifiers::for_char(c)));
key.code = KeyCode::Char(s.chars().next().unwrap());
key.shift = matches!(key.code, KeyCode::Char(c) if c.is_ascii_uppercase());
return Ok(key);
}
let mut key: Self = KeyCode::Null.into();
let mut it = s[1..s.len() - 1].split_inclusive('-').peekable();
while let Some(next) = it.next() {
match next.to_ascii_lowercase().as_str() {
"s-" => key.modifiers |= Modifiers::SHIFT,
"c-" => key.modifiers |= Modifiers::CONTROL,
"a-" => key.modifiers |= Modifiers::ALT,
"d-" => key.modifiers |= Modifiers::SUPER,
"s-" => key.shift = true,
"c-" => key.ctrl = true,
"a-" => key.alt = true,
"d-" => key.super_ = true,
"space" => key.code = KeyCode::Char(' '),
"backspace" => key.code = KeyCode::Backspace,
@ -64,12 +99,8 @@ impl FromStr for KeyEvent {
_ => match next {
s if it.peek().is_none() => {
let c = s.chars().next().unwrap();
key.modifiers |= Modifiers::for_char(c);
key.code = KeyCode::Char(if key.modifiers.contains(Modifiers::SHIFT) {
c.to_ascii_uppercase()
} else {
c
});
key.shift |= c.is_ascii_uppercase();
key.code = KeyCode::Char(if key.shift { c.to_ascii_uppercase() } else { c });
}
s => bail!("unknown key: {s}"),
},
@ -83,23 +114,23 @@ impl FromStr for KeyEvent {
}
}
impl Display for KeyEvent {
impl Display for Key {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(c) = self.plain() {
return if c == ' ' { write!(f, "<Space>") } else { f.write_char(c) };
}
write!(f, "<")?;
if self.modifiers.contains(Modifiers::SUPER) {
if self.super_ {
write!(f, "D-")?;
}
if self.modifiers.contains(Modifiers::CONTROL) {
if self.ctrl {
write!(f, "C-")?;
}
if self.modifiers.contains(Modifiers::ALT) {
if self.alt {
write!(f, "A-")?;
}
if self.modifiers.contains(Modifiers::SHIFT) && !matches!(self.code, KeyCode::Char(_)) {
if self.shift && !matches!(self.code, KeyCode::Char(_)) {
write!(f, "S-")?;
}
@ -149,3 +180,7 @@ impl Display for KeyEvent {
write!(f, "{code}>")
}
}
impl IntoLua for Key {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { lua.to_value_with(&self, SER_OPT) }
}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(chord chord_arc chords ids keymap section);
yazi_macro::mod_flat!(chord chord_arc chords ids key keymap section);

View file

@ -6,9 +6,8 @@ use serde::Deserialize;
use yazi_codegen::DeserializeOver2;
use yazi_shared::Layer;
use yazi_shim::{mlua::UserDataFieldsExt, toml::DeserializeOverHook};
use yazi_term::event::KeyEvent;
use super::{Chord, Chords, chords::layer_default};
use super::{Key, Chord, Chords, chords::layer_default};
use crate::{keymap::ChordArc, mix};
#[derive(Default, Deserialize, DeserializeOver2)]
@ -37,7 +36,7 @@ impl<const L: u8> KeymapSection<L> {
impl<const L: u8> DeserializeOverHook for KeymapSection<L> {
fn deserialize_over_hook(self) -> Result<Self, toml::de::Error> {
#[inline]
fn on<const L: u8>(Chord { on, .. }: &Chord<L>) -> [KeyEvent; 2] {
fn on<const L: u8>(Chord { on, .. }: &Chord<L>) -> [Key; 2] {
[on.first().copied().unwrap_or_default(), on.get(1).copied().unwrap_or_default()]
}

View file

@ -1,6 +1,6 @@
yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme vfs which);
yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme which);
yazi_macro::mod_flat!(icon inject layout mixing pattern platform preset priority selectable selector utils yazi);
yazi_macro::mod_flat!(icon inject layout mixing pattern platform preset priority selectable selector yazi);
use std::io::{Read, Write};

View file

@ -1,4 +1,4 @@
use crate::{Yazi, keymap::Keymap, theme::Theme, vfs::Vfs};
use crate::{Yazi, keymap::Keymap, theme::Theme};
pub(crate) struct Preset;
@ -18,8 +18,4 @@ impl Preset {
yazi_macro::theme_preset!("dark")
})
}
pub(super) fn vfs() -> Result<Vfs, toml::de::Error> {
toml::from_str(&yazi_macro::config_preset!("vfs"))
}
}

View file

@ -3,12 +3,11 @@ use std::path::PathBuf;
use anyhow::{Context, Result};
use serde::{Deserialize, Deserializer, Serialize};
use yazi_codegen::DeserializeOver2;
use yazi_fs::{Xdg, create_owned_dir_blocking};
use yazi_fs::{Xdg, create_owned_dir_blocking, path::sanitize_path};
use yazi_shared::timestamp_us;
use yazi_shim::{SStr, toml::DeserializeOverHook};
use super::PreviewWrap;
use crate::normalize_path;
#[derive(Debug, Deserialize, DeserializeOver2, Serialize)]
pub struct Preview {
@ -65,7 +64,7 @@ where
if path.as_os_str().is_empty() {
Ok(Xdg::temp_dir().to_owned())
} else {
normalize_path(path).ok_or_else(|| {
sanitize_path(path).ok_or_else(|| {
serde::de::Error::custom("cache_dir must be either empty or an absolute path.")
})
}

View file

@ -5,11 +5,10 @@ use arc_swap::ArcSwap;
use serde::{Deserialize, Deserializer, de};
use yazi_binding::style::StyleFlat;
use yazi_codegen::{DeserializeOver, DeserializeOver1, DeserializeOver2, Overlay};
use yazi_fs::{Xdg, ok_or_not_found};
use yazi_fs::{Xdg, ok_or_not_found, path::sanitize_path};
use yazi_shim::{arc_swap::IntoPointee, cell::SyncCell};
use super::{Custom, Filetype, Flavor, Icon};
use crate::normalize_path;
#[derive(Deserialize, DeserializeOver, DeserializeOver1, Overlay)]
pub struct Theme {
@ -274,7 +273,7 @@ where
{
let mut path = PathBuf::deserialize(deserializer)?;
if !path.as_os_str().is_empty() {
path = normalize_path(path).ok_or_else(|| {
path = sanitize_path(path).ok_or_else(|| {
de::Error::custom("syntect_theme must be either empty or an absolute path.")
})?;
}

View file

@ -1,5 +1,6 @@
use ratatui_core::layout::{Margin, Position, Rect};
use ratatui_core::layout::{Position, Rect};
use yazi_shared::Layer;
use yazi_shim::ratatui::Padable;
use yazi_tty::sequence::SetCursorStyle;
use crate::{cmp::Cmp, confirm::Confirm, help::Help, input::{Input, InputGuard}, mgr::Mgr, notify::Notify, pick::Pick, tab::{Folder, Tab}, tasks::Tasks, which::Which};
@ -34,7 +35,7 @@ impl Core {
pub fn cursor(&self) -> Option<(Position, SetCursorStyle)> {
if let Some(guard) = self.input.lock() {
let Rect { x, y, .. } = match &guard {
InputGuard::Main(_) => self.mgr.area(self.input.position()?).inner(Margin::new(1, 1)),
InputGuard::Main(_) => self.mgr.area(self.input.position()?).padding(self.input.padding()),
InputGuard::Alt(_) => self.mgr.area(self.input.position()?),
};
return Some((Position { x: x + guard.cursor(), y }, guard.cursor_shape()));

View file

@ -1,6 +1,7 @@
use std::{ops::{Deref, DerefMut}, sync::Arc};
use parking_lot::Mutex;
use ratatui_widgets::block::Padding;
use yazi_binding::{elements::Spatial, position::Position};
use crate::input::{InputGuard, InputMutGuard};
@ -14,6 +15,8 @@ pub struct Input {
impl Input {
pub fn focus(&self) -> bool { self.main.visible || self.alt.is_some() }
pub fn padding(&self) -> Padding { Padding::new(1, 1, 1, 1) }
pub fn position(&self) -> Option<Position> {
if self.main.visible {
Some(self.main.position)

View file

@ -1,9 +1,8 @@
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value};
use tokio::sync::mpsc;
use yazi_config::{KEYMAP, keymap::ChordArc};
use yazi_config::{KEYMAP, keymap::{ChordArc, Key}};
use yazi_macro::impl_data_any;
use yazi_shared::{Layer, event::ActionCow};
use yazi_term::event::KeyEvent;
#[derive(Clone, Debug)]
pub struct WhichOpt {
@ -32,8 +31,8 @@ impl TryFrom<ActionCow> for WhichOpt {
}
}
impl From<(Layer, KeyEvent)> for WhichOpt {
fn from((layer, key): (Layer, KeyEvent)) -> Self {
impl From<(Layer, Key)> for WhichOpt {
fn from((layer, key): (Layer, Key)) -> Self {
Self {
tx: None,
cands: KEYMAP

View file

@ -1,7 +1,6 @@
use tokio::sync::mpsc;
use yazi_config::keymap::ChordArc;
use yazi_config::keymap::{ChordArc, Key};
use yazi_macro::{emit, render_and};
use yazi_term::event::KeyEvent;
#[derive(Default)]
pub struct Which {
@ -15,7 +14,7 @@ pub struct Which {
}
impl Which {
pub fn r#type(&mut self, key: KeyEvent) -> bool {
pub fn r#type(&mut self, key: Key) -> bool {
self.cands.retain(|c| c.on.len() > self.times && c.on[self.times] == key);
self.times += 1;

View file

@ -1,6 +1,6 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_config::{KEYMAP, keymap::Chord};
use yazi_config::{KEYMAP, keymap::{Chord, Key}};
use yazi_macro::act;
use yazi_shared::Layer;
use yazi_term::event::KeyEvent;
@ -29,6 +29,7 @@ impl<'a> Router<'a> {
}
let layer = core.layer();
let key = Key::from(key);
Ok(match layer {
L::Null | L::App | L::Notify => unreachable!(),
L::Mgr | L::Tasks | L::Spot | L::Pick | L::Input | L::Confirm | L::Help => {
@ -39,7 +40,7 @@ impl<'a> Router<'a> {
})
}
fn matches(&mut self, layer: Layer, key: KeyEvent) -> bool {
fn matches(&mut self, layer: Layer, key: Key) -> bool {
for chord in &*KEYMAP.chords(layer) {
let Chord { on, .. } = chord.as_ref();
if on.is_empty() || on[0] != key {

View file

@ -1 +1 @@
yazi_macro::mod_flat!(clean expand path percent relative);
yazi_macro::mod_flat!(clean expand normalize path percent relative);

View file

@ -1,9 +1,10 @@
use std::path::PathBuf;
use yazi_fs::path::{clean_url, expand_url};
use yazi_shared::url::UrlBuf;
pub(crate) fn normalize_path(path: PathBuf) -> Option<PathBuf> {
use crate::path::{clean_url, expand_url};
pub fn sanitize_path(path: PathBuf) -> Option<PathBuf> {
clean_url(yazi_fs::provider::local::try_absolute(expand_url(UrlBuf::from(path)))?)
.into_loc()
.into_os()

View file

@ -6,7 +6,7 @@ macro_rules! config_preset {
std::borrow::Cow::from(
std::fs::read_to_string(concat!(
env!("CARGO_MANIFEST_DIR"),
"/preset/",
"/../yazi-config/preset/",
$name,
"-default.toml"
))
@ -17,7 +17,7 @@ macro_rules! config_preset {
{
std::borrow::Cow::from(include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/preset/",
"/../yazi-config/preset/",
$name,
"-default.toml"
)))

View file

@ -1,15 +1,15 @@
use mlua::{FromLua, IntoLua, Lua, Value};
use yazi_config::keymap::Key;
use yazi_core::which::WhichOpt;
use yazi_shared::{Layer, event::ActionCow};
use yazi_term::event::KeyEvent;
#[derive(Clone, Debug)]
pub struct ActivateForm {
pub opt: WhichOpt,
}
impl From<(Layer, KeyEvent)> for ActivateForm {
fn from(value: (Layer, KeyEvent)) -> Self { Self { opt: value.into() } }
impl From<(Layer, Key)> for ActivateForm {
fn from(value: (Layer, Key)) -> Self { Self { opt: value.into() } }
}
impl TryFrom<ActionCow> for ActivateForm {

View file

@ -3,12 +3,11 @@ use std::{str::FromStr, time::Duration};
use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, Table, Value};
use tokio_stream::wrappers::UnboundedReceiverStream;
use yazi_binding::{elements::{Line, Text}, runtime};
use yazi_config::{Platform, keymap::{Chord, ChordArc}, popup::ConfirmCfg};
use yazi_config::{Platform, keymap::{Chord, ChordArc, Key}, popup::ConfirmCfg};
use yazi_core::notify::MessageOpt;
use yazi_macro::relay;
use yazi_proxy::{ConfirmProxy, InputProxy, NotifyProxy, WhichProxy};
use yazi_shared::{Debounce, Layer};
use yazi_term::event::KeyEvent;
use yazi_widgets::input::{InputOpt, InputStream};
use super::Utils;
@ -93,15 +92,15 @@ impl Utils {
lua.create_function(|_, opt: MessageOpt| Ok(NotifyProxy::push(opt)))
}
fn parse_keys(value: Value) -> mlua::Result<Vec<KeyEvent>> {
fn parse_keys(value: Value) -> mlua::Result<Vec<Key>> {
Ok(match value {
Value::String(s) => {
vec![KeyEvent::from_str(&s.to_str()?).into_lua_err()?]
vec![Key::from_str(&s.to_str()?).into_lua_err()?]
}
Value::Table(t) => {
let mut v = Vec::with_capacity(10);
for s in t.sequence_values::<mlua::String>() {
v.push(KeyEvent::from_str(&s?.to_str()?).into_lua_err()?);
v.push(Key::from_str(&s?.to_str()?).into_lua_err()?);
}
v
}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(conversion dnd event keyboard lua modifiers mouse);
yazi_macro::mod_flat!(dnd event keyboard lua modifiers mouse);

View file

@ -14,7 +14,7 @@ workspace = true
[dependencies]
yazi-binding = { path = "../yazi-binding", version = "26.5.6" }
yazi-config = { path = "../yazi-config", version = "26.5.6" }
yazi-codegen = { path = "../yazi-codegen", version = "26.5.6" }
yazi-fs = { path = "../yazi-fs", version = "26.5.6" }
yazi-macro = { path = "../yazi-macro", version = "26.5.6" }
yazi-sftp = { path = "../yazi-sftp", version = "26.5.6" }
@ -31,6 +31,8 @@ hashbrown = { workspace = true }
mlua = { workspace = true }
parking_lot = { workspace = true }
russh = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
toml = { workspace = true }
tracing = { workspace = true }
typed-path = { workspace = true }

View file

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
use crate::vfs::ServiceSftp;
use crate::config::ServiceSftp;
#[derive(Deserialize, Serialize)]
#[serde(tag = "type", rename_all = "kebab-case")]

View file

@ -6,7 +6,7 @@ use serde::{Deserialize, Deserializer, Serialize, de, de::{MapAccess, Visitor}};
use yazi_codegen::DeserializeOver;
use yazi_shim::toml::DeserializeOverWith;
use crate::vfs::Service;
use crate::config::Service;
#[derive(Serialize, Deserialize, DeserializeOver)]
pub struct Services(HashMap<String, Service>);

View file

@ -1,8 +1,7 @@
use std::{env, path::PathBuf};
use serde::{Deserialize, Deserializer, Serialize, de};
use crate::normalize_path;
use yazi_fs::path::sanitize_path;
#[derive(Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ServiceSftp {
@ -27,7 +26,7 @@ where
{
let mut path = PathBuf::deserialize(deserializer)?;
if !path.as_os_str().is_empty() {
path = normalize_path(path)
path = sanitize_path(path)
.ok_or_else(|| de::Error::custom("path must be either empty or an absolute path"))?;
}
@ -46,7 +45,7 @@ where
if path.as_os_str().is_empty() {
Ok(default_identity_agent())
} else {
normalize_path(path)
sanitize_path(path)
.ok_or_else(|| de::Error::custom("identity_agent must be either empty or an absolute path"))
}
}

View file

@ -8,7 +8,7 @@ use yazi_fs::{Xdg, ok_or_not_found};
use yazi_shim::toml::DeserializeOver;
use super::Service;
use crate::{Preset, vfs::Services};
use crate::config::Services;
#[derive(Deserialize, Serialize, DeserializeOver, DeserializeOver1)]
pub struct Vfs {
@ -21,7 +21,8 @@ impl Vfs {
async fn init() -> io::Result<Vfs> {
tokio::task::spawn_blocking(|| -> anyhow::Result<Vfs> {
Ok(Preset::vfs()?.deserialize_over(&Vfs::read()?)?)
let vfs: Vfs = toml::from_str(&yazi_macro::config_preset!("vfs"))?;
Ok(vfs.deserialize_over(&Vfs::read()?)?)
})
.await?
.map_err(io::Error::other)

View file

@ -1,4 +1,4 @@
yazi_macro::mod_pub!(provider);
yazi_macro::mod_pub!(config provider);
yazi_macro::mod_flat!(cha file files fns op);

View file

@ -2,9 +2,10 @@ use std::{io, sync::Arc, time::{Duration, SystemTime}};
use chrono::DateTime;
use russh::keys::{PrivateKeyWithHashAlg, agent::AgentIdentity};
use yazi_config::vfs::ServiceSftp;
use yazi_fs::provider::local::Local;
use crate::config::ServiceSftp;
#[derive(Clone, Copy)]
pub(super) struct Conn {
pub(super) name: &'static str,

View file

@ -1,11 +1,10 @@
use std::io;
use yazi_config::vfs::{ServiceSftp, Vfs};
use yazi_fs::provider::{Attrs, FileBuilder};
use yazi_sftp::fs::Flags;
use yazi_shared::url::{AsUrl, Url};
use crate::provider::sftp::Conn;
use crate::{config::{ServiceSftp, Vfs}, provider::sftp::Conn};
#[derive(Clone, Copy, Default)]
pub struct Gate(crate::provider::Gate);

View file

@ -2,10 +2,7 @@ yazi_macro::mod_flat!(absolute conn gate metadata read_dir sftp);
static CONN: yazi_shim::cell::RoCell<
parking_lot::Mutex<
hashbrown::HashMap<
&'static yazi_config::vfs::ServiceSftp,
&'static deadpool::managed::Pool<Conn>,
>,
hashbrown::HashMap<&'static crate::config::ServiceSftp, &'static deadpool::managed::Pool<Conn>>,
>,
> = yazi_shim::cell::RoCell::new();

View file

@ -1,13 +1,12 @@
use std::{io, sync::Arc};
use tokio::{io::{AsyncWriteExt, BufReader, BufWriter}, sync::mpsc::Receiver};
use yazi_config::vfs::{ServiceSftp, Vfs};
use yazi_fs::{cha::ChaMode, provider::{Capabilities, DirReader, FileHolder, Provider}};
use yazi_sftp::fs::{Attrs, Flags};
use yazi_shared::{loc::LocBuf, path::{AsPath, PathBufDyn}, pool::InternStr, scheme::SchemeKind, strand::AsStrand, url::{Url, UrlBuf, UrlCow, UrlLike}};
use super::Cha;
use crate::provider::sftp::Conn;
use crate::{config::{ServiceSftp, Vfs}, provider::sftp::Conn};
#[derive(Clone)]
pub struct Sftp<'a> {

View file

@ -18,6 +18,7 @@ vendored-lua = [ "mlua/vendored" ]
[dependencies]
yazi-binding = { path = "../yazi-binding", version = "26.5.6" }
yazi-dds = { path = "../yazi-dds", version = "26.5.6" }
yazi-macro = { path = "../yazi-macro", version = "26.5.6" }
yazi-shared = { path = "../yazi-shared", version = "26.5.6" }
yazi-shim = { path = "../yazi-shim", version = "26.5.6" }

View file

@ -4,6 +4,9 @@ use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMetho
use parking_lot::Mutex;
use ratatui_core::widgets::Widget;
use yazi_binding::{elements::{Area, Spatial}, impl_area_method};
use yazi_dds::Pubsub;
use yazi_macro::err;
use yazi_shim::strum::IntoStr;
use crate::input::{Input, InputOpt, InputStyles};
@ -26,6 +29,7 @@ impl InputArc {
let new = lua.create_function(move |_, (_, mut opt): (Table, InputOpt)| {
opt.styles.normal = opt.styles.normal.or(styles.normal);
opt.styles.selected = opt.styles.selected.or(styles.selected);
opt = opt.with_cb(|e| err!(Pubsub::pub_after_input((&e).into_str(), e.value())));
Ok(Self::from(Input::new(opt)?))
})?;