mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: subscribe to and intercept app-level events through DDS (#3632)
This commit is contained in:
parent
583345296f
commit
6d942ea011
82 changed files with 815 additions and 537 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
|
@ -5542,6 +5542,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"mlua",
|
"mlua",
|
||||||
"paste",
|
"paste",
|
||||||
|
"ratatui",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
|
|
@ -5551,6 +5552,7 @@ dependencies = [
|
||||||
"yazi-config",
|
"yazi-config",
|
||||||
"yazi-core",
|
"yazi-core",
|
||||||
"yazi-dds",
|
"yazi-dds",
|
||||||
|
"yazi-emulator",
|
||||||
"yazi-fs",
|
"yazi-fs",
|
||||||
"yazi-macro",
|
"yazi-macro",
|
||||||
"yazi-parser",
|
"yazi-parser",
|
||||||
|
|
@ -5558,6 +5560,7 @@ dependencies = [
|
||||||
"yazi-proxy",
|
"yazi-proxy",
|
||||||
"yazi-scheduler",
|
"yazi-scheduler",
|
||||||
"yazi-shared",
|
"yazi-shared",
|
||||||
|
"yazi-term",
|
||||||
"yazi-tty",
|
"yazi-tty",
|
||||||
"yazi-vfs",
|
"yazi-vfs",
|
||||||
"yazi-watcher",
|
"yazi-watcher",
|
||||||
|
|
@ -5704,6 +5707,7 @@ dependencies = [
|
||||||
"indexmap 2.13.0",
|
"indexmap 2.13.0",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
|
@ -5868,6 +5872,7 @@ dependencies = [
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"mlua",
|
"mlua",
|
||||||
"ordered-float 5.1.0",
|
"ordered-float 5.1.0",
|
||||||
|
"ratatui",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ function bugReportBody(creator, content, hash) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return `Hey @${creator}, thank you for opening this issue to help us improve Yazi, appreciate it!
|
return `Hey @${creator}, thank you for opening the issue to help improve Yazi, appreciate it!
|
||||||
|
|
||||||
I noticed that you did not correctly follow the issue template. Please ensure that:
|
I noticed that you did not correctly follow the issue template. Please ensure that:
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ I noticed that you did not correctly follow the issue template. Please ensure th
|
||||||
|
|
||||||
Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity.
|
Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity.
|
||||||
|
|
||||||
Our maintainers work on Yazi in a personal capacity, and debug info helps them work efficiently, understand your setup quickly, and find a more appropriate solution. Thank you for the understanding!
|
Our maintainers work on Yazi in their free time, this helps them work efficiently, understand your setup quickly, and find a more appropriate solution. Thanks for your understanding!
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,7 +27,7 @@ function featureRequestBody(creator, content) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return `Hey @${creator}, thank you for opening this issue to help us improve Yazi, appreciate it!
|
return `Hey @${creator}, thank you for opening the issue to help improve Yazi, appreciate it!
|
||||||
|
|
||||||
I noticed that you did not correctly follow the issue template. Please ensure that:
|
I noticed that you did not correctly follow the issue template. Please ensure that:
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ I noticed that you did not correctly follow the issue template. Please ensure th
|
||||||
|
|
||||||
Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity.
|
Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity.
|
||||||
|
|
||||||
Our maintainers work on Yazi in a personal capacity, and debug info helps them work efficiently, understand your setup quickly, and find a more appropriate solution. Thank you for the understanding!
|
Our maintainers work on Yazi in their free time, this helps them work efficiently, understand your setup quickly, and find a more appropriate solution. Thanks for your understanding!
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ yazi-boot = { path = "../yazi-boot", version = "26.1.22" }
|
||||||
yazi-config = { path = "../yazi-config", version = "26.1.22" }
|
yazi-config = { path = "../yazi-config", version = "26.1.22" }
|
||||||
yazi-core = { path = "../yazi-core", version = "26.1.22" }
|
yazi-core = { path = "../yazi-core", version = "26.1.22" }
|
||||||
yazi-dds = { path = "../yazi-dds", version = "26.1.22" }
|
yazi-dds = { path = "../yazi-dds", version = "26.1.22" }
|
||||||
|
yazi-emulator = { path = "../yazi-emulator", version = "26.1.22" }
|
||||||
yazi-fs = { path = "../yazi-fs", version = "26.1.22" }
|
yazi-fs = { path = "../yazi-fs", version = "26.1.22" }
|
||||||
yazi-macro = { path = "../yazi-macro", version = "26.1.22" }
|
yazi-macro = { path = "../yazi-macro", version = "26.1.22" }
|
||||||
yazi-parser = { path = "../yazi-parser", version = "26.1.22" }
|
yazi-parser = { path = "../yazi-parser", version = "26.1.22" }
|
||||||
|
|
@ -28,6 +29,7 @@ yazi-plugin = { path = "../yazi-plugin", version = "26.1.22" }
|
||||||
yazi-proxy = { path = "../yazi-proxy", version = "26.1.22" }
|
yazi-proxy = { path = "../yazi-proxy", version = "26.1.22" }
|
||||||
yazi-scheduler = { path = "../yazi-scheduler", version = "26.1.22" }
|
yazi-scheduler = { path = "../yazi-scheduler", version = "26.1.22" }
|
||||||
yazi-shared = { path = "../yazi-shared", version = "26.1.22" }
|
yazi-shared = { path = "../yazi-shared", version = "26.1.22" }
|
||||||
|
yazi-term = { path = "../yazi-term", version = "26.1.22" }
|
||||||
yazi-tty = { path = "../yazi-tty", version = "26.1.22" }
|
yazi-tty = { path = "../yazi-tty", version = "26.1.22" }
|
||||||
yazi-vfs = { path = "../yazi-vfs", version = "26.1.22" }
|
yazi-vfs = { path = "../yazi-vfs", version = "26.1.22" }
|
||||||
yazi-watcher = { path = "../yazi-watcher", version = "26.1.22" }
|
yazi-watcher = { path = "../yazi-watcher", version = "26.1.22" }
|
||||||
|
|
@ -40,6 +42,7 @@ futures = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
mlua = { workspace = true }
|
mlua = { workspace = true }
|
||||||
paste = { workspace = true }
|
paste = { workspace = true }
|
||||||
|
ratatui = { workspace = true }
|
||||||
scopeguard = { workspace = true }
|
scopeguard = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
tokio-stream = { workspace = true }
|
tokio-stream = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use anyhow::{Result, bail};
|
use anyhow::Result;
|
||||||
use mlua::IntoLua;
|
use mlua::IntoLua;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use yazi_actor::lives::Lives;
|
use yazi_actor::lives::Lives;
|
||||||
|
|
@ -6,16 +6,18 @@ use yazi_binding::runtime_mut;
|
||||||
use yazi_dds::{LOCAL, Payload, REMOTE};
|
use yazi_dds::{LOCAL, Payload, REMOTE};
|
||||||
use yazi_macro::succ;
|
use yazi_macro::succ;
|
||||||
use yazi_plugin::LUA;
|
use yazi_plugin::LUA;
|
||||||
use yazi_shared::{data::Data, event::CmdCow};
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
impl App {
|
pub struct AcceptPayload;
|
||||||
pub(crate) fn accept_payload(&self, mut c: CmdCow) -> Result<Data> {
|
|
||||||
let Some(payload) = c.take_any2::<Payload>("payload").transpose()? else {
|
|
||||||
bail!("'payload' is required for accept_payload");
|
|
||||||
};
|
|
||||||
|
|
||||||
|
impl Actor for AcceptPayload {
|
||||||
|
type Options = Payload<'static>;
|
||||||
|
|
||||||
|
const NAME: &str = "accept_payload";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, payload: Payload) -> Result<Data> {
|
||||||
let kind = payload.body.kind().to_owned();
|
let kind = payload.body.kind().to_owned();
|
||||||
let lock = if payload.receiver == 0 || payload.receiver != payload.sender {
|
let lock = if payload.receiver == 0 || payload.receiver != payload.sender {
|
||||||
REMOTE.read()
|
REMOTE.read()
|
||||||
|
|
@ -26,7 +28,7 @@ impl App {
|
||||||
let Some(handlers) = lock.get(&kind).filter(|&m| !m.is_empty()).cloned() else { succ!() };
|
let Some(handlers) = lock.get(&kind).filter(|&m| !m.is_empty()).cloned() else { succ!() };
|
||||||
drop(lock);
|
drop(lock);
|
||||||
|
|
||||||
succ!(Lives::scope(&self.core, || {
|
succ!(Lives::scope(&cx.core, || {
|
||||||
let body = payload.body.into_lua(&LUA)?;
|
let body = payload.body.into_lua(&LUA)?;
|
||||||
for (id, cb) in handlers {
|
for (id, cb) in handlers {
|
||||||
let blocking = runtime_mut!(LUA)?.critical_push(&id, true);
|
let blocking = runtime_mut!(LUA)?.critical_push(&id, true);
|
||||||
|
|
@ -1,30 +1,33 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use yazi_actor::Ctx;
|
use yazi_actor::Ctx;
|
||||||
use yazi_boot::BOOT;
|
use yazi_boot::BOOT;
|
||||||
use yazi_macro::act;
|
use yazi_macro::{act, succ};
|
||||||
use yazi_parser::{VoidOpt, mgr::CdSource};
|
use yazi_parser::{VoidOpt, mgr::CdSource};
|
||||||
use yazi_shared::{data::Data, strand::StrandLike, url::UrlLike};
|
use yazi_shared::{data::Data, strand::StrandLike, url::UrlLike};
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::Actor;
|
||||||
|
|
||||||
impl App {
|
pub struct Bootstrap;
|
||||||
pub fn bootstrap(&mut self, _: VoidOpt) -> Result<Data> {
|
|
||||||
|
impl Actor for Bootstrap {
|
||||||
|
type Options = VoidOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "bootstrap";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
|
||||||
for (i, file) in BOOT.files.iter().enumerate() {
|
for (i, file) in BOOT.files.iter().enumerate() {
|
||||||
let tabs = &mut self.core.mgr.tabs;
|
let tabs = &mut cx.core.mgr.tabs;
|
||||||
if tabs.len() <= i {
|
if tabs.len() <= i {
|
||||||
tabs.push(Default::default());
|
tabs.push(Default::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
let cx = &mut Ctx::active(&mut self.core);
|
|
||||||
cx.tab = i;
|
cx.tab = i;
|
||||||
|
|
||||||
if file.is_empty() {
|
if file.is_empty() {
|
||||||
act!(mgr:cd, cx, (BOOT.cwds[i].clone(), CdSource::Tab))?;
|
act!(mgr:cd, cx, (BOOT.cwds[i].clone(), CdSource::Tab))?;
|
||||||
} else if let Ok(u) = BOOT.cwds[i].try_join(file) {
|
} else if let Ok(u) = BOOT.cwds[i].try_join(file) {
|
||||||
act!(mgr:reveal, cx, (u, CdSource::Tab))?;
|
act!(mgr:reveal, cx, (u, CdSource::Tab))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
succ!();
|
||||||
act!(render, self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
23
yazi-actor/src/app/deprecate.rs
Normal file
23
yazi-actor/src/app/deprecate.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use yazi_macro::act;
|
||||||
|
use yazi_parser::{app::DeprecateOpt, notify::{PushLevel, PushOpt}};
|
||||||
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
pub struct Deprecate;
|
||||||
|
|
||||||
|
impl Actor for Deprecate {
|
||||||
|
type Options = DeprecateOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "deprecate";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
|
act!(notify:push, cx, PushOpt {
|
||||||
|
title: "Deprecated API".to_owned(),
|
||||||
|
content: opt.content.into_owned(),
|
||||||
|
level: PushLevel::Warn,
|
||||||
|
timeout: std::time::Duration::from_secs(20),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
17
yazi-actor/src/app/focus.rs
Normal file
17
yazi-actor/src/app/focus.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use yazi_actor::Ctx;
|
||||||
|
use yazi_macro::act;
|
||||||
|
use yazi_parser::VoidOpt;
|
||||||
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
|
use crate::Actor;
|
||||||
|
|
||||||
|
pub struct Focus;
|
||||||
|
|
||||||
|
impl Actor for Focus {
|
||||||
|
type Options = VoidOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "focus";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> { act!(mgr:refresh, cx) }
|
||||||
|
}
|
||||||
|
|
@ -4,14 +4,12 @@ yazi_macro::mod_flat!(
|
||||||
deprecate
|
deprecate
|
||||||
focus
|
focus
|
||||||
mouse
|
mouse
|
||||||
notify
|
|
||||||
plugin
|
plugin
|
||||||
|
plugin_do
|
||||||
quit
|
quit
|
||||||
reflow
|
reflow
|
||||||
render
|
|
||||||
resize
|
resize
|
||||||
resume
|
resume
|
||||||
stop
|
stop
|
||||||
update_notify
|
|
||||||
update_progress
|
update_progress
|
||||||
);
|
);
|
||||||
|
|
@ -9,14 +9,20 @@ use yazi_parser::app::MouseOpt;
|
||||||
use yazi_plugin::LUA;
|
use yazi_plugin::LUA;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
impl App {
|
pub struct Mouse;
|
||||||
pub fn mouse(&mut self, opt: MouseOpt) -> Result<Data> {
|
|
||||||
|
impl Actor for Mouse {
|
||||||
|
type Options = MouseOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "mouse";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
let event = yazi_plugin::bindings::MouseEvent::from(opt.event);
|
let event = yazi_plugin::bindings::MouseEvent::from(opt.event);
|
||||||
let Some(size) = self.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
|
let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
|
||||||
|
|
||||||
let result = Lives::scope(&self.core, move || {
|
let result = Lives::scope(&cx.core, move || {
|
||||||
let area = yazi_binding::elements::Rect::from(size);
|
let area = yazi_binding::elements::Rect::from(size);
|
||||||
let root = LUA.globals().raw_get::<Table>("Root")?.call_method::<Table>("new", area)?;
|
let root = LUA.globals().raw_get::<Table>("Root")?.call_method::<Table>("new", area)?;
|
||||||
|
|
||||||
38
yazi-actor/src/app/plugin.rs
Normal file
38
yazi-actor/src/app/plugin.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use yazi_macro::{act, succ};
|
||||||
|
use yazi_parser::app::{PluginMode, PluginOpt};
|
||||||
|
use yazi_plugin::loader::LOADER;
|
||||||
|
use yazi_proxy::{AppProxy, NotifyProxy};
|
||||||
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
pub struct Plugin;
|
||||||
|
|
||||||
|
impl Actor for Plugin {
|
||||||
|
type Options = PluginOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "plugin";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result<Data> {
|
||||||
|
let mut hits = false;
|
||||||
|
if let Some(chunk) = LOADER.read().get(&*opt.id) {
|
||||||
|
hits = true;
|
||||||
|
opt.mode = opt.mode.auto_then(chunk.sync_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.mode == PluginMode::Async {
|
||||||
|
succ!(cx.core.tasks.scheduler.plugin_entry(opt));
|
||||||
|
} else if opt.mode == PluginMode::Sync && hits {
|
||||||
|
return act!(app:plugin_do, cx, opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
match LOADER.ensure(&opt.id, |_| ()).await {
|
||||||
|
Ok(()) => AppProxy::plugin_do(opt),
|
||||||
|
Err(e) => NotifyProxy::push_error("Plugin load failed", e),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
succ!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,52 +2,35 @@ use anyhow::Result;
|
||||||
use mlua::ObjectLike;
|
use mlua::ObjectLike;
|
||||||
use scopeguard::defer;
|
use scopeguard::defer;
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
use yazi_actor::lives::Lives;
|
|
||||||
use yazi_binding::runtime_mut;
|
use yazi_binding::runtime_mut;
|
||||||
use yazi_dds::Sendable;
|
use yazi_dds::Sendable;
|
||||||
use yazi_macro::succ;
|
use yazi_macro::succ;
|
||||||
use yazi_parser::app::{PluginMode, PluginOpt};
|
use yazi_parser::app::{PluginMode, PluginOpt};
|
||||||
use yazi_plugin::{LUA, loader::{LOADER, Loader}};
|
use yazi_plugin::{LUA, loader::{LOADER, Loader}};
|
||||||
use yazi_proxy::AppProxy;
|
use yazi_proxy::NotifyProxy;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::{Actor, Ctx, lives::Lives};
|
||||||
|
|
||||||
impl App {
|
pub struct PluginDo;
|
||||||
pub(crate) fn plugin(&mut self, mut opt: PluginOpt) -> Result<Data> {
|
|
||||||
let mut hits = false;
|
|
||||||
if let Some(chunk) = LOADER.read().get(&*opt.id) {
|
|
||||||
hits = true;
|
|
||||||
opt.mode = opt.mode.auto_then(chunk.sync_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.mode == PluginMode::Async {
|
impl Actor for PluginDo {
|
||||||
succ!(self.core.tasks.plugin_entry(opt));
|
type Options = PluginOpt;
|
||||||
} else if opt.mode == PluginMode::Sync && hits {
|
|
||||||
return self.plugin_do(opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
tokio::spawn(async move {
|
const NAME: &str = "plugin_do";
|
||||||
match LOADER.ensure(&opt.id, |_| ()).await {
|
|
||||||
Ok(()) => AppProxy::plugin_do(opt),
|
|
||||||
Err(e) => AppProxy::notify_error("Plugin load failed", e),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
succ!();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn plugin_do(&mut self, opt: PluginOpt) -> Result<Data> {
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
let loader = LOADER.read();
|
let loader = LOADER.read();
|
||||||
let Some(chunk) = loader.get(&*opt.id) else {
|
let Some(chunk) = loader.get(&*opt.id) else {
|
||||||
succ!(warn!("plugin `{}` not found", opt.id));
|
succ!(warn!("plugin `{}` not found", opt.id));
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = Loader::compatible_or_error(&opt.id, chunk) {
|
if let Err(e) = Loader::compatible_or_error(&opt.id, chunk) {
|
||||||
succ!(AppProxy::notify_error("Incompatible plugin", e));
|
succ!(NotifyProxy::push_error("Incompatible plugin", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
if opt.mode.auto_then(chunk.sync_entry) != PluginMode::Sync {
|
if opt.mode.auto_then(chunk.sync_entry) != PluginMode::Sync {
|
||||||
succ!(self.core.tasks.plugin_entry(opt));
|
succ!(cx.core.tasks.scheduler.plugin_entry(opt));
|
||||||
}
|
}
|
||||||
|
|
||||||
let blocking = runtime_mut!(LUA)?.critical_push(&opt.id, true);
|
let blocking = runtime_mut!(LUA)?.critical_push(&opt.id, true);
|
||||||
|
|
@ -59,7 +42,7 @@ impl App {
|
||||||
};
|
};
|
||||||
drop(loader);
|
drop(loader);
|
||||||
|
|
||||||
let result = Lives::scope(&self.core, || {
|
let result = Lives::scope(&cx.core, || {
|
||||||
if let Some(cb) = opt.callback {
|
if let Some(cb) = opt.callback {
|
||||||
cb(&LUA, plugin)
|
cb(&LUA, plugin)
|
||||||
} else {
|
} else {
|
||||||
47
yazi-actor/src/app/quit.rs
Normal file
47
yazi-actor/src/app/quit.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use yazi_boot::ARGS;
|
||||||
|
use yazi_fs::provider::{Provider, local::Local};
|
||||||
|
use yazi_parser::app::QuitOpt;
|
||||||
|
use yazi_shared::{data::Data, strand::{StrandBuf, StrandLike, ToStrand}};
|
||||||
|
use yazi_term::Term;
|
||||||
|
|
||||||
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
pub struct Quit;
|
||||||
|
|
||||||
|
impl Actor for Quit {
|
||||||
|
type Options = QuitOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "quit";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
|
cx.core.tasks.shutdown();
|
||||||
|
cx.core.mgr.shutdown();
|
||||||
|
|
||||||
|
futures::executor::block_on(async {
|
||||||
|
_ = futures::join!(
|
||||||
|
yazi_dds::shutdown(),
|
||||||
|
yazi_dds::STATE.drain(),
|
||||||
|
Self::cwd_to_file(cx, opt.no_cwd_file),
|
||||||
|
Self::selected_to_file(opt.selected)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Term::goodbye(|| opt.code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Quit {
|
||||||
|
async fn cwd_to_file(cx: &Ctx<'_>, no: bool) {
|
||||||
|
if let Some(p) = ARGS.cwd_file.as_ref().filter(|_| !no) {
|
||||||
|
let cwd = cx.core.mgr.cwd().to_strand();
|
||||||
|
Local::regular(p).write(cwd.encoded_bytes()).await.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn selected_to_file(selected: Option<StrandBuf>) {
|
||||||
|
if let (Some(s), Some(p)) = (selected, &ARGS.chooser_file) {
|
||||||
|
Local::regular(p).write(s.encoded_bytes()).await.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,18 +5,24 @@ use tracing::error;
|
||||||
use yazi_actor::lives::Lives;
|
use yazi_actor::lives::Lives;
|
||||||
use yazi_config::LAYOUT;
|
use yazi_config::LAYOUT;
|
||||||
use yazi_macro::{render, succ};
|
use yazi_macro::{render, succ};
|
||||||
use yazi_parser::VoidOpt;
|
use yazi_parser::app::ReflowOpt;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
use crate::{Root, app::App};
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
impl App {
|
pub struct Reflow;
|
||||||
pub fn reflow(&mut self, _: VoidOpt) -> Result<Data> {
|
|
||||||
let Some(size) = self.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
|
impl Actor for Reflow {
|
||||||
|
type Options = ReflowOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "reflow";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
|
let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
|
||||||
let mut layout = LAYOUT.get();
|
let mut layout = LAYOUT.get();
|
||||||
|
|
||||||
let result = Lives::scope(&self.core, || {
|
let result = Lives::scope(&cx.core, || {
|
||||||
let comps = Root::reflow((Position::ORIGIN, size).into())?;
|
let comps = (opt.reflow)((Position::ORIGIN, size).into())?;
|
||||||
|
|
||||||
for v in comps.sequence_values::<Value>() {
|
for v in comps.sequence_values::<Value>() {
|
||||||
let Value::Table(t) = v? else {
|
let Value::Table(t) = v? else {
|
||||||
25
yazi-actor/src/app/resize.rs
Normal file
25
yazi-actor/src/app/resize.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use yazi_actor::Ctx;
|
||||||
|
use yazi_macro::act;
|
||||||
|
use yazi_parser::app::ReflowOpt;
|
||||||
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
|
use crate::Actor;
|
||||||
|
|
||||||
|
pub struct Resize;
|
||||||
|
|
||||||
|
impl Actor for Resize {
|
||||||
|
type Options = ReflowOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "resize";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
|
act!(app:reflow, cx, opt)?;
|
||||||
|
|
||||||
|
cx.core.current_mut().arrow(0);
|
||||||
|
cx.core.parent_mut().map(|f| f.arrow(0));
|
||||||
|
cx.core.current_mut().sync_page(true);
|
||||||
|
|
||||||
|
act!(mgr:peek, cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
28
yazi-actor/src/app/resume.rs
Normal file
28
yazi-actor/src/app/resume.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use yazi_macro::{act, render, succ};
|
||||||
|
use yazi_parser::app::ResumeOpt;
|
||||||
|
use yazi_shared::data::Data;
|
||||||
|
use yazi_term::Term;
|
||||||
|
|
||||||
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
pub struct Resume;
|
||||||
|
|
||||||
|
impl Actor for Resume {
|
||||||
|
type Options = ResumeOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "resume";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
|
cx.core.active_mut().preview.reset();
|
||||||
|
*cx.term = Some(Term::start().unwrap());
|
||||||
|
|
||||||
|
// While the app resumes, it's possible that the terminal size has changed.
|
||||||
|
// We need to trigger a resize, and render the UI based on the resized area.
|
||||||
|
act!(app:resize, cx, opt.reflow)?;
|
||||||
|
|
||||||
|
opt.tx.send((true, opt.token))?;
|
||||||
|
|
||||||
|
succ!(render!());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,18 +3,24 @@ use yazi_macro::succ;
|
||||||
use yazi_parser::app::StopOpt;
|
use yazi_parser::app::StopOpt;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
impl App {
|
pub struct Stop;
|
||||||
pub fn stop(&mut self, opt: StopOpt) -> Result<Data> {
|
|
||||||
self.core.active_mut().preview.reset_image();
|
impl Actor for Stop {
|
||||||
|
type Options = StopOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "stop";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
|
cx.core.active_mut().preview.reset_image();
|
||||||
|
|
||||||
// We need to destroy the `term` first before stopping the `signals`
|
// We need to destroy the `term` first before stopping the `signals`
|
||||||
// to prevent any signal from triggering the term to render again
|
// to prevent any signal from triggering the term to render again
|
||||||
// while the app is being suspended.
|
// while the app is being suspended.
|
||||||
self.term = None;
|
*cx.term = None;
|
||||||
|
|
||||||
self.signals.stop(opt.token);
|
opt.tx.send((false, opt.token))?;
|
||||||
|
|
||||||
succ!();
|
succ!();
|
||||||
}
|
}
|
||||||
|
|
@ -1,15 +1,21 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use yazi_actor::Ctx;
|
use yazi_actor::Ctx;
|
||||||
use yazi_macro::{act, render, succ};
|
use yazi_macro::{act, render, render_partial, succ};
|
||||||
use yazi_parser::app::UpdateProgressOpt;
|
use yazi_parser::app::UpdateProgressOpt;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::Actor;
|
||||||
|
|
||||||
impl App {
|
pub struct UpdateProgress;
|
||||||
pub(crate) fn update_progress(&mut self, opt: UpdateProgressOpt) -> Result<Data> {
|
|
||||||
|
impl Actor for UpdateProgress {
|
||||||
|
type Options = UpdateProgressOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "update_progress";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
// Update the progress of all tasks.
|
// Update the progress of all tasks.
|
||||||
let tasks = &mut self.core.tasks;
|
let tasks = &mut cx.core.tasks;
|
||||||
let progressed = tasks.summary != opt.summary;
|
let progressed = tasks.summary != opt.summary;
|
||||||
tasks.summary = opt.summary;
|
tasks.summary = opt.summary;
|
||||||
|
|
||||||
|
|
@ -18,7 +24,6 @@ impl App {
|
||||||
let new = tasks.paginate();
|
let new = tasks.paginate();
|
||||||
if tasks.snaps != new {
|
if tasks.snaps != new {
|
||||||
tasks.snaps = new;
|
tasks.snaps = new;
|
||||||
let cx = &mut Ctx::active(&mut self.core);
|
|
||||||
act!(tasks:arrow, cx)?;
|
act!(tasks:arrow, cx)?;
|
||||||
succ!(render!());
|
succ!(render!());
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +34,7 @@ impl App {
|
||||||
} else if tasks.summary.total == 0 {
|
} else if tasks.summary.total == 0 {
|
||||||
succ!(render!())
|
succ!(render!())
|
||||||
} else {
|
} else {
|
||||||
act!(render_partially, self)
|
succ!(render_partial!())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,9 +4,11 @@ use anyhow::{Result, anyhow};
|
||||||
use yazi_core::{Core, mgr::Tabs, tab::{Folder, Tab}, tasks::Tasks};
|
use yazi_core::{Core, mgr::Tabs, tab::{Folder, Tab}, tasks::Tasks};
|
||||||
use yazi_fs::File;
|
use yazi_fs::File;
|
||||||
use yazi_shared::{Id, Source, event::Cmd, url::UrlBuf};
|
use yazi_shared::{Id, Source, event::Cmd, url::UrlBuf};
|
||||||
|
use yazi_term::Term;
|
||||||
|
|
||||||
pub struct Ctx<'a> {
|
pub struct Ctx<'a> {
|
||||||
pub core: &'a mut Core,
|
pub core: &'a mut Core,
|
||||||
|
pub term: &'a mut Option<Term>,
|
||||||
pub tab: usize,
|
pub tab: usize,
|
||||||
pub level: usize,
|
pub level: usize,
|
||||||
pub source: Source,
|
pub source: Source,
|
||||||
|
|
@ -26,7 +28,7 @@ impl DerefMut for Ctx<'_> {
|
||||||
|
|
||||||
impl<'a> Ctx<'a> {
|
impl<'a> Ctx<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(core: &'a mut Core, cmd: &Cmd) -> Result<Self> {
|
pub fn new(cmd: &Cmd, core: &'a mut Core, term: &'a mut Option<Term>) -> Result<Self> {
|
||||||
let tab = if let Ok(id) = cmd.get::<Id>("tab") {
|
let tab = if let Ok(id) = cmd.get::<Id>("tab") {
|
||||||
core
|
core
|
||||||
.mgr
|
.mgr
|
||||||
|
|
@ -40,6 +42,7 @@ impl<'a> Ctx<'a> {
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
core,
|
core,
|
||||||
|
term,
|
||||||
tab,
|
tab,
|
||||||
level: 0,
|
level: 0,
|
||||||
source: cmd.source,
|
source: cmd.source,
|
||||||
|
|
@ -53,6 +56,7 @@ impl<'a> Ctx<'a> {
|
||||||
let tab = cx.core.mgr.tabs.cursor;
|
let tab = cx.core.mgr.tabs.cursor;
|
||||||
Self {
|
Self {
|
||||||
core: cx.core,
|
core: cx.core,
|
||||||
|
term: cx.term,
|
||||||
tab,
|
tab,
|
||||||
level: cx.level,
|
level: cx.level,
|
||||||
source: cx.source,
|
source: cx.source,
|
||||||
|
|
@ -62,10 +66,11 @@ impl<'a> Ctx<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn active(core: &'a mut Core) -> Self {
|
pub fn active(core: &'a mut Core, term: &'a mut Option<Term>) -> Self {
|
||||||
let tab = core.mgr.tabs.cursor;
|
let tab = core.mgr.tabs.cursor;
|
||||||
Self {
|
Self {
|
||||||
core,
|
core,
|
||||||
|
term,
|
||||||
tab,
|
tab,
|
||||||
level: 0,
|
level: 0,
|
||||||
source: Source::Unknown,
|
source: Source::Unknown,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
extern crate self as yazi_actor;
|
extern crate self as yazi_actor;
|
||||||
|
|
||||||
yazi_macro::mod_pub!(cmp confirm core help input lives mgr pick spot tasks which);
|
yazi_macro::mod_pub!(app cmp confirm core help input lives mgr notify pick spot tasks which);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(actor context);
|
yazi_macro::mod_flat!(actor context);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use yazi_dds::Pubsub;
|
||||||
use yazi_fs::{File, FilesOp, Splatter, max_common_root, path::skip_url, provider::{FileBuilder, Provider, local::{Gate, Local}}};
|
use yazi_fs::{File, FilesOp, Splatter, max_common_root, path::skip_url, provider::{FileBuilder, Provider, local::{Gate, Local}}};
|
||||||
use yazi_macro::{err, succ};
|
use yazi_macro::{err, succ};
|
||||||
use yazi_parser::VoidOpt;
|
use yazi_parser::VoidOpt;
|
||||||
use yazi_proxy::{AppProxy, HIDER, TasksProxy};
|
use yazi_proxy::{AppProxy, HIDER, NotifyProxy, TasksProxy};
|
||||||
use yazi_shared::{data::Data, path::PathDyn, strand::{AsStrand, AsStrandJoin, Strand, StrandBuf, StrandLike}, terminal_clear, url::{AsUrl, UrlBuf, UrlCow, UrlLike}};
|
use yazi_shared::{data::Data, path::PathDyn, strand::{AsStrand, AsStrandJoin, Strand, StrandBuf, StrandLike}, terminal_clear, url::{AsUrl, UrlBuf, UrlCow, UrlLike}};
|
||||||
use yazi_tty::TTY;
|
use yazi_tty::TTY;
|
||||||
use yazi_vfs::{VfsFile, maybe_exists, provider};
|
use yazi_vfs::{VfsFile, maybe_exists, provider};
|
||||||
|
|
@ -28,12 +28,12 @@ impl Actor for BulkRename {
|
||||||
|
|
||||||
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
|
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
|
||||||
let Some(opener) = Self::opener() else {
|
let Some(opener) = Self::opener() else {
|
||||||
succ!(AppProxy::notify_warn("Bulk rename", "No text opener found"));
|
succ!(NotifyProxy::push_warn("Bulk rename", "No text opener found"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let selected: Vec<_> = cx.tab().selected_or_hovered().cloned().collect();
|
let selected: Vec<_> = cx.tab().selected_or_hovered().cloned().collect();
|
||||||
if selected.is_empty() {
|
if selected.is_empty() {
|
||||||
succ!(AppProxy::notify_warn("Bulk rename", "No files selected"));
|
succ!(NotifyProxy::push_warn("Bulk rename", "No files selected"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let root = max_common_root(&selected);
|
let root = max_common_root(&selected);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use yazi_macro::{act, render, render_and, succ};
|
use yazi_macro::{act, render, render_and, succ};
|
||||||
use yazi_parser::{VoidOpt, mgr::EscapeOpt};
|
use yazi_parser::{VoidOpt, mgr::EscapeOpt};
|
||||||
use yazi_proxy::AppProxy;
|
use yazi_proxy::NotifyProxy;
|
||||||
use yazi_shared::{data::Data, url::UrlLike};
|
use yazi_shared::{data::Data, url::UrlLike};
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
@ -76,7 +76,7 @@ impl Actor for EscapeVisual {
|
||||||
if !select {
|
if !select {
|
||||||
tab.selected.remove_many(urls);
|
tab.selected.remove_many(urls);
|
||||||
} else if urls.len() != tab.selected.add_many(urls) {
|
} else if urls.len() != tab.selected.add_many(urls) {
|
||||||
AppProxy::notify_warn(
|
NotifyProxy::push_warn(
|
||||||
"Escape visual mode",
|
"Escape visual mode",
|
||||||
"Some files cannot be selected, due to path nesting conflict.",
|
"Some files cannot be selected, due to path nesting conflict.",
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ use anyhow::Result;
|
||||||
use tokio::{select, time};
|
use tokio::{select, time};
|
||||||
use yazi_config::popup::ConfirmCfg;
|
use yazi_config::popup::ConfirmCfg;
|
||||||
use yazi_dds::spark::SparkKind;
|
use yazi_dds::spark::SparkKind;
|
||||||
use yazi_macro::{emit, succ};
|
use yazi_macro::{act, succ};
|
||||||
use yazi_parser::mgr::QuitOpt;
|
use yazi_parser::app::QuitOpt;
|
||||||
use yazi_proxy::ConfirmProxy;
|
use yazi_proxy::{AppProxy, ConfirmProxy};
|
||||||
use yazi_shared::{data::Data, event::EventQuit, strand::{Strand, StrandLike, ToStrandJoin}, url::AsUrl};
|
use yazi_shared::{data::Data, strand::{Strand, StrandLike, ToStrandJoin}, url::AsUrl};
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
|
@ -19,8 +19,6 @@ impl Actor for Quit {
|
||||||
const NAME: &str = "quit";
|
const NAME: &str = "quit";
|
||||||
|
|
||||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
let event = opt.into();
|
|
||||||
|
|
||||||
let ongoing = cx.tasks().ongoing().clone();
|
let ongoing = cx.tasks().ongoing().clone();
|
||||||
let (left, left_names) = {
|
let (left, left_names) = {
|
||||||
let ongoing = ongoing.lock();
|
let ongoing = ongoing.lock();
|
||||||
|
|
@ -28,7 +26,7 @@ impl Actor for Quit {
|
||||||
};
|
};
|
||||||
|
|
||||||
if left == 0 {
|
if left == 0 {
|
||||||
succ!(emit!(Quit(event)));
|
return act!(app:quit, cx, opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
|
@ -40,13 +38,13 @@ impl Actor for Quit {
|
||||||
i += 1;
|
i += 1;
|
||||||
if i > 40 { break }
|
if i > 40 { break }
|
||||||
else if ongoing.lock().is_empty() {
|
else if ongoing.lock().is_empty() {
|
||||||
emit!(Quit(event));
|
AppProxy::quit(opt);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b = token.future() => {
|
b = token.future() => {
|
||||||
if b {
|
if b {
|
||||||
emit!(Quit(event));
|
AppProxy::quit(opt);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -54,7 +52,7 @@ impl Actor for Quit {
|
||||||
}
|
}
|
||||||
|
|
||||||
if token.future().await {
|
if token.future().await {
|
||||||
emit!(Quit(event));
|
AppProxy::quit(opt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
succ!();
|
succ!();
|
||||||
|
|
@ -73,7 +71,7 @@ impl Quit {
|
||||||
{
|
{
|
||||||
let paths = selected.into_iter().join(Strand::Utf8("\n"));
|
let paths = selected.into_iter().join(Strand::Utf8("\n"));
|
||||||
if !paths.is_empty() {
|
if !paths.is_empty() {
|
||||||
emit!(Quit(EventQuit { selected: Some(paths), ..Default::default() }));
|
AppProxy::quit(QuitOpt { selected: Some(paths), ..Default::default() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use yazi_fs::{FilesOp, cha::Cha};
|
||||||
use yazi_macro::{act, succ};
|
use yazi_macro::{act, succ};
|
||||||
use yazi_parser::{VoidOpt, mgr::{CdSource, SearchOpt, SearchOptVia}};
|
use yazi_parser::{VoidOpt, mgr::{CdSource, SearchOpt, SearchOptVia}};
|
||||||
use yazi_plugin::external;
|
use yazi_plugin::external;
|
||||||
use yazi_proxy::{AppProxy, InputProxy, MgrProxy};
|
use yazi_proxy::{InputProxy, MgrProxy, NotifyProxy};
|
||||||
use yazi_shared::{data::Data, url::UrlLike};
|
use yazi_shared::{data::Data, url::UrlLike};
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
@ -54,7 +54,7 @@ impl Actor for SearchDo {
|
||||||
|
|
||||||
let hidden = tab.pref.show_hidden;
|
let hidden = tab.pref.show_hidden;
|
||||||
let Ok(cwd) = tab.cwd().to_search(&opt.subject) else {
|
let Ok(cwd) = tab.cwd().to_search(&opt.subject) else {
|
||||||
succ!(AppProxy::notify_warn("Search", "Only local filesystem searches are supported"));
|
succ!(NotifyProxy::push_warn("Search", "Only local filesystem searches are supported"));
|
||||||
};
|
};
|
||||||
|
|
||||||
tab.search = Some(tokio::spawn(async move {
|
tab.search = Some(tokio::spawn(async move {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||||
use yazi_core::tab::Tab;
|
use yazi_core::tab::Tab;
|
||||||
use yazi_macro::{act, render, succ};
|
use yazi_macro::{act, render, succ};
|
||||||
use yazi_parser::mgr::{CdSource, TabCreateOpt};
|
use yazi_parser::mgr::{CdSource, TabCreateOpt};
|
||||||
use yazi_proxy::AppProxy;
|
use yazi_proxy::NotifyProxy;
|
||||||
use yazi_shared::{data::Data, url::UrlLike};
|
use yazi_shared::{data::Data, url::UrlLike};
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
@ -18,7 +18,7 @@ impl Actor for TabCreate {
|
||||||
|
|
||||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
if cx.tabs().len() >= MAX_TABS {
|
if cx.tabs().len() >= MAX_TABS {
|
||||||
succ!(AppProxy::notify_warn(
|
succ!(NotifyProxy::push_warn(
|
||||||
"Too many tabs",
|
"Too many tabs",
|
||||||
"You can only open up to 9 tabs at the same time."
|
"You can only open up to 9 tabs at the same time."
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use yazi_macro::{render_and, succ};
|
use yazi_macro::{render_and, succ};
|
||||||
use yazi_parser::mgr::ToggleOpt;
|
use yazi_parser::mgr::ToggleOpt;
|
||||||
use yazi_proxy::AppProxy;
|
use yazi_proxy::NotifyProxy;
|
||||||
use yazi_shared::{data::Data, url::UrlCow};
|
use yazi_shared::{data::Data, url::UrlCow};
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
@ -26,7 +26,7 @@ impl Actor for Toggle {
|
||||||
};
|
};
|
||||||
|
|
||||||
if !b {
|
if !b {
|
||||||
AppProxy::notify_warn(
|
NotifyProxy::push_warn(
|
||||||
"Toggle",
|
"Toggle",
|
||||||
"This file cannot be selected, due to path nesting conflict.",
|
"This file cannot be selected, due to path nesting conflict.",
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use yazi_macro::{render, succ};
|
use yazi_macro::{render, succ};
|
||||||
use yazi_parser::mgr::ToggleAllOpt;
|
use yazi_parser::mgr::ToggleAllOpt;
|
||||||
use yazi_proxy::AppProxy;
|
use yazi_proxy::NotifyProxy;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
@ -39,7 +39,7 @@ impl Actor for ToggleAll {
|
||||||
};
|
};
|
||||||
|
|
||||||
if warn {
|
if warn {
|
||||||
AppProxy::notify_warn(
|
NotifyProxy::push_warn(
|
||||||
"Toggle all",
|
"Toggle all",
|
||||||
"Some files cannot be selected, due to path nesting conflict.",
|
"Some files cannot be selected, due to path nesting conflict.",
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use yazi_macro::{act, render, succ};
|
||||||
use yazi_parser::VoidOpt;
|
use yazi_parser::VoidOpt;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
use crate::Actor;
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
pub struct Unyank;
|
pub struct Unyank;
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@ impl Actor for Unyank {
|
||||||
|
|
||||||
const NAME: &str = "unyank";
|
const NAME: &str = "unyank";
|
||||||
|
|
||||||
fn act(cx: &mut crate::Ctx, _: Self::Options) -> Result<Data> {
|
fn act(cx: &mut Ctx, _: Self::Options) -> Result<Data> {
|
||||||
let repeek = cx.hovered().is_some_and(|f| f.is_dir() && cx.mgr.yanked.contains_in(&f.url));
|
let repeek = cx.hovered().is_some_and(|f| f.is_dir() && cx.mgr.yanked.contains_in(&f.url));
|
||||||
cx.mgr.yanked.clear();
|
cx.mgr.yanked.clear();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use yazi_macro::{render, succ};
|
||||||
use yazi_parser::mgr::UpdatePeekedOpt;
|
use yazi_parser::mgr::UpdatePeekedOpt;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
use crate::Actor;
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
pub struct UpdatePeeked;
|
pub struct UpdatePeeked;
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@ impl Actor for UpdatePeeked {
|
||||||
|
|
||||||
const NAME: &str = "update_peeked";
|
const NAME: &str = "update_peeked";
|
||||||
|
|
||||||
fn act(cx: &mut crate::Ctx, opt: Self::Options) -> Result<Data> {
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
let Some(hovered) = cx.hovered().map(|h| &h.url) else {
|
let Some(hovered) = cx.hovered().map(|h| &h.url) else {
|
||||||
succ!(cx.tab_mut().preview.reset());
|
succ!(cx.tab_mut().preview.reset());
|
||||||
};
|
};
|
||||||
|
|
|
||||||
31
yazi-actor/src/notify/push.rs
Normal file
31
yazi-actor/src/notify/push.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use yazi_core::notify::Message;
|
||||||
|
use yazi_macro::succ;
|
||||||
|
use yazi_parser::notify::PushOpt;
|
||||||
|
use yazi_proxy::NotifyProxy;
|
||||||
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
pub struct Push;
|
||||||
|
|
||||||
|
impl Actor for Push {
|
||||||
|
type Options = PushOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "push";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
|
let instant = Instant::now();
|
||||||
|
|
||||||
|
let mut msg = Message::from(opt);
|
||||||
|
msg.timeout += instant - cx.notify.messages.first().map_or(instant, |m| m.instant);
|
||||||
|
|
||||||
|
if cx.notify.messages.iter().all(|m| m != &msg) {
|
||||||
|
cx.notify.messages.push(msg);
|
||||||
|
NotifyProxy::tick(Duration::ZERO);
|
||||||
|
}
|
||||||
|
succ!();
|
||||||
|
}
|
||||||
|
}
|
||||||
64
yazi-actor/src/notify/tick.rs
Normal file
64
yazi-actor/src/notify/tick.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use ratatui::layout::Rect;
|
||||||
|
use yazi_core::notify::Notify;
|
||||||
|
use yazi_emulator::Dimension;
|
||||||
|
use yazi_macro::succ;
|
||||||
|
use yazi_parser::notify::TickOpt;
|
||||||
|
use yazi_proxy::NotifyProxy;
|
||||||
|
use yazi_shared::data::Data;
|
||||||
|
|
||||||
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
pub struct Tick;
|
||||||
|
|
||||||
|
impl Actor for Tick {
|
||||||
|
type Options = TickOpt;
|
||||||
|
|
||||||
|
const NAME: &str = "tick";
|
||||||
|
|
||||||
|
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||||
|
cx.notify.tick_handle.take().map(|h| h.abort());
|
||||||
|
|
||||||
|
let Dimension { rows, columns, .. } = Dimension::available();
|
||||||
|
let area = Notify::available(Rect { x: 0, y: 0, width: columns, height: rows });
|
||||||
|
|
||||||
|
let limit = cx.notify.limit(area);
|
||||||
|
if limit == 0 {
|
||||||
|
succ!();
|
||||||
|
}
|
||||||
|
|
||||||
|
for m in &mut cx.notify.messages[..limit] {
|
||||||
|
if m.timeout.is_zero() {
|
||||||
|
m.percent = m.percent.saturating_sub(20);
|
||||||
|
} else if m.percent < 100 {
|
||||||
|
m.percent += 20;
|
||||||
|
} else {
|
||||||
|
m.timeout = m.timeout.saturating_sub(opt.interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.notify.messages.retain(|m| m.percent > 0 || !m.timeout.is_zero());
|
||||||
|
let limit = cx.notify.limit(area);
|
||||||
|
let timeouts: Vec<_> = cx.notify.messages[..limit]
|
||||||
|
.iter()
|
||||||
|
.filter(|&m| m.percent == 100 && !m.timeout.is_zero())
|
||||||
|
.map(|m| m.timeout)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let interval = if timeouts.len() != limit {
|
||||||
|
Duration::from_millis(50)
|
||||||
|
} else if let Some(min) = timeouts.iter().min() {
|
||||||
|
*min
|
||||||
|
} else {
|
||||||
|
succ!();
|
||||||
|
};
|
||||||
|
|
||||||
|
cx.notify.tick_handle = Some(tokio::spawn(async move {
|
||||||
|
tokio::time::sleep(interval).await;
|
||||||
|
NotifyProxy::tick(interval);
|
||||||
|
}));
|
||||||
|
succ!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,7 @@ impl Keymap {
|
||||||
Layer::Help => &self.help,
|
Layer::Help => &self.help,
|
||||||
Layer::Cmp => &self.cmp,
|
Layer::Cmp => &self.cmp,
|
||||||
Layer::Which => &[],
|
Layer::Which => &[],
|
||||||
|
Layer::Notify => &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ hashbrown = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
parking_lot = { workspace = true }
|
parking_lot = { workspace = true }
|
||||||
ratatui = { workspace = true }
|
ratatui = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
tokio-stream = { workspace = true }
|
tokio-stream = { workspace = true }
|
||||||
tokio-util = { workspace = true }
|
tokio-util = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
use yazi_parser::app::NotifyOpt;
|
|
||||||
use yazi_proxy::AppProxy;
|
|
||||||
|
|
||||||
use crate::notify::{Message, Notify};
|
|
||||||
|
|
||||||
impl Notify {
|
|
||||||
pub fn push(&mut self, opt: NotifyOpt) {
|
|
||||||
let instant = Instant::now();
|
|
||||||
|
|
||||||
let mut msg = Message::from(opt);
|
|
||||||
msg.timeout += instant - self.messages.first().map_or(instant, |m| m.instant);
|
|
||||||
|
|
||||||
if self.messages.iter().all(|m| m != &msg) {
|
|
||||||
self.messages.push(msg);
|
|
||||||
AppProxy::update_notify(Duration::ZERO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use ratatui::layout::Rect;
|
|
||||||
use yazi_parser::notify::TickOpt;
|
|
||||||
use yazi_proxy::AppProxy;
|
|
||||||
|
|
||||||
use crate::notify::Notify;
|
|
||||||
|
|
||||||
impl Notify {
|
|
||||||
pub fn tick(&mut self, opt: TickOpt, area: Rect) {
|
|
||||||
self.tick_handle.take().map(|h| h.abort());
|
|
||||||
|
|
||||||
let limit = self.limit(area);
|
|
||||||
if limit == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for m in &mut self.messages[..limit] {
|
|
||||||
if m.timeout.is_zero() {
|
|
||||||
m.percent = m.percent.saturating_sub(20);
|
|
||||||
} else if m.percent < 100 {
|
|
||||||
m.percent += 20;
|
|
||||||
} else {
|
|
||||||
m.timeout = m.timeout.saturating_sub(opt.interval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.messages.retain(|m| m.percent > 0 || !m.timeout.is_zero());
|
|
||||||
let limit = self.limit(area);
|
|
||||||
let timeouts: Vec<_> = self.messages[..limit]
|
|
||||||
.iter()
|
|
||||||
.filter(|&m| m.percent == 100 && !m.timeout.is_zero())
|
|
||||||
.map(|m| m.timeout)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let interval = if timeouts.len() != limit {
|
|
||||||
Duration::from_millis(50)
|
|
||||||
} else if let Some(min) = timeouts.iter().min() {
|
|
||||||
*min
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.tick_handle = Some(tokio::spawn(async move {
|
|
||||||
tokio::time::sleep(interval).await;
|
|
||||||
AppProxy::update_notify(interval);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
use yazi_parser::app::{NotifyLevel, NotifyOpt};
|
use yazi_parser::notify::{PushLevel, PushOpt};
|
||||||
|
|
||||||
use super::NOTIFY_BORDER;
|
use super::NOTIFY_BORDER;
|
||||||
|
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub level: NotifyLevel,
|
pub level: PushLevel,
|
||||||
pub timeout: Duration,
|
pub timeout: Duration,
|
||||||
|
|
||||||
pub instant: Instant,
|
pub instant: Instant,
|
||||||
|
|
@ -16,8 +16,8 @@ pub struct Message {
|
||||||
pub max_width: usize,
|
pub max_width: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<NotifyOpt> for Message {
|
impl From<PushOpt> for Message {
|
||||||
fn from(opt: NotifyOpt) -> Self {
|
fn from(opt: PushOpt) -> Self {
|
||||||
let title = opt.title.lines().next().unwrap_or_default();
|
let title = opt.title.lines().next().unwrap_or_default();
|
||||||
let title_width = title.width() + (opt.level.icon().width() + /* Space */ 1);
|
let title_width = title.width() + (opt.level.icon().width() + /* Space */ 1);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
yazi_macro::mod_pub!(commands);
|
|
||||||
|
|
||||||
yazi_macro::mod_flat!(message notify);
|
yazi_macro::mod_flat!(message notify);
|
||||||
|
|
||||||
pub const NOTIFY_BORDER: u16 = 2;
|
pub const NOTIFY_BORDER: u16 = 2;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,23 @@
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::{Constraint, Layout, Rect};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
use super::{Message, NOTIFY_SPACING};
|
use super::{Message, NOTIFY_SPACING};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Notify {
|
pub struct Notify {
|
||||||
pub(super) tick_handle: Option<JoinHandle<()>>,
|
pub tick_handle: Option<JoinHandle<()>>,
|
||||||
pub messages: Vec<Message>,
|
pub messages: Vec<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Notify {
|
impl Notify {
|
||||||
|
pub fn available(area: Rect) -> Rect {
|
||||||
|
let chunks = Layout::horizontal([Constraint::Fill(1), Constraint::Min(80)]).split(area);
|
||||||
|
let chunks = Layout::vertical([Constraint::Fill(1), Constraint::Max(1)]).split(chunks[1]);
|
||||||
|
chunks[0]
|
||||||
|
}
|
||||||
|
|
||||||
pub fn limit(&self, area: Rect) -> usize {
|
pub fn limit(&self, area: Rect) -> usize {
|
||||||
if self.messages.is_empty() {
|
if self.messages.is_empty() {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
yazi_macro::mod_flat!(file plugin prework process tasks);
|
yazi_macro::mod_flat!(file prework process tasks);
|
||||||
|
|
||||||
pub const TASKS_BORDER: u16 = 2;
|
pub const TASKS_BORDER: u16 = 2;
|
||||||
pub const TASKS_PADDING: u16 = 2;
|
pub const TASKS_PADDING: u16 = 2;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
use yazi_parser::app::PluginOpt;
|
|
||||||
|
|
||||||
use super::Tasks;
|
|
||||||
|
|
||||||
impl Tasks {
|
|
||||||
pub fn plugin_entry(&self, opt: PluginOpt) { self.scheduler.plugin_entry(opt); }
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
use std::{fmt::Display, io::Write, str::FromStr};
|
use std::{fmt::Display, io::Write, str::FromStr};
|
||||||
|
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
|
use mlua::{IntoLua, Lua, Value};
|
||||||
use yazi_boot::BOOT;
|
use yazi_boot::BOOT;
|
||||||
use yazi_macro::{emit, relay};
|
use yazi_macro::{emit, relay};
|
||||||
use yazi_shared::Id;
|
use yazi_shared::{Id, event::CmdCow};
|
||||||
|
|
||||||
use crate::{ID, ember::Ember};
|
use crate::{ID, ember::Ember, spark::Spark};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Payload<'a> {
|
pub struct Payload<'a> {
|
||||||
|
|
@ -76,6 +77,25 @@ impl<'a> From<Ember<'a>> for Payload<'a> {
|
||||||
fn from(value: Ember<'a>) -> Self { Self::new(value) }
|
fn from(value: Ember<'a>) -> Self { Self::new(value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<Spark<'a>> for Payload<'a> {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: Spark<'a>) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
Spark::AppAcceptPayload(payload) => Ok(payload),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<CmdCow> for Payload<'_> {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
|
||||||
|
c.take_any2("payload").ok_or_else(|| anyhow!("Missing 'payload' in Payload"))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Payload<'_> {
|
impl Display for Payload<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let result = match &self.body {
|
let result = match &self.body {
|
||||||
|
|
@ -104,3 +124,15 @@ impl Display for Payload<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoLua for Payload<'a> {
|
||||||
|
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||||
|
lua
|
||||||
|
.create_table_from([
|
||||||
|
("receiver", yazi_binding::Id(self.receiver).into_lua(lua)?),
|
||||||
|
("sender", yazi_binding::Id(self.sender).into_lua(lua)?),
|
||||||
|
("body", self.body.into_lua(lua)?),
|
||||||
|
])?
|
||||||
|
.into_lua(lua)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,21 @@ pub enum Spark<'a> {
|
||||||
// Void
|
// Void
|
||||||
Void(yazi_parser::VoidOpt),
|
Void(yazi_parser::VoidOpt),
|
||||||
|
|
||||||
|
// App
|
||||||
|
AppAcceptPayload(crate::Payload<'a>),
|
||||||
|
AppBootstrap(yazi_parser::VoidOpt),
|
||||||
|
AppDeprecate(yazi_parser::app::DeprecateOpt),
|
||||||
|
AppFocus(yazi_parser::VoidOpt),
|
||||||
|
AppMouse(yazi_parser::app::MouseOpt),
|
||||||
|
AppPlugin(yazi_parser::app::PluginOpt),
|
||||||
|
AppPluginDo(yazi_parser::app::PluginOpt),
|
||||||
|
AppQuit(yazi_parser::app::QuitOpt),
|
||||||
|
AppReflow(yazi_parser::app::ReflowOpt),
|
||||||
|
AppResize(yazi_parser::app::ReflowOpt),
|
||||||
|
AppResume(yazi_parser::app::ResumeOpt),
|
||||||
|
AppStop(yazi_parser::app::StopOpt),
|
||||||
|
AppUpdateProgress(yazi_parser::app::UpdateProgressOpt),
|
||||||
|
|
||||||
// Mgr
|
// Mgr
|
||||||
Arrow(yazi_parser::ArrowOpt),
|
Arrow(yazi_parser::ArrowOpt),
|
||||||
Back(yazi_parser::VoidOpt),
|
Back(yazi_parser::VoidOpt),
|
||||||
|
|
@ -42,7 +57,7 @@ pub enum Spark<'a> {
|
||||||
OpenDo(yazi_parser::mgr::OpenDoOpt),
|
OpenDo(yazi_parser::mgr::OpenDoOpt),
|
||||||
Paste(yazi_parser::mgr::PasteOpt),
|
Paste(yazi_parser::mgr::PasteOpt),
|
||||||
Peek(yazi_parser::mgr::PeekOpt),
|
Peek(yazi_parser::mgr::PeekOpt),
|
||||||
Quit(yazi_parser::mgr::QuitOpt),
|
Quit(yazi_parser::app::QuitOpt),
|
||||||
Refresh(yazi_parser::VoidOpt),
|
Refresh(yazi_parser::VoidOpt),
|
||||||
Remove(yazi_parser::mgr::RemoveOpt),
|
Remove(yazi_parser::mgr::RemoveOpt),
|
||||||
RemoveDo(yazi_parser::mgr::RemoveOpt),
|
RemoveDo(yazi_parser::mgr::RemoveOpt),
|
||||||
|
|
@ -107,6 +122,7 @@ pub enum Spark<'a> {
|
||||||
InputShow(yazi_parser::input::ShowOpt),
|
InputShow(yazi_parser::input::ShowOpt),
|
||||||
|
|
||||||
// Notify
|
// Notify
|
||||||
|
NotifyPush(yazi_parser::notify::PushOpt),
|
||||||
NotifyTick(yazi_parser::notify::TickOpt),
|
NotifyTick(yazi_parser::notify::TickOpt),
|
||||||
|
|
||||||
// Pick
|
// Pick
|
||||||
|
|
@ -160,6 +176,21 @@ impl<'a> IntoLua for Spark<'a> {
|
||||||
// Void
|
// Void
|
||||||
Self::Void(b) => b.into_lua(lua),
|
Self::Void(b) => b.into_lua(lua),
|
||||||
|
|
||||||
|
// App
|
||||||
|
Self::AppAcceptPayload(b) => b.into_lua(lua),
|
||||||
|
Self::AppBootstrap(b) => b.into_lua(lua),
|
||||||
|
Self::AppDeprecate(b) => b.into_lua(lua),
|
||||||
|
Self::AppFocus(b) => b.into_lua(lua),
|
||||||
|
Self::AppMouse(b) => b.into_lua(lua),
|
||||||
|
Self::AppPlugin(b) => b.into_lua(lua),
|
||||||
|
Self::AppPluginDo(b) => b.into_lua(lua),
|
||||||
|
Self::AppQuit(b) => b.into_lua(lua),
|
||||||
|
Self::AppReflow(b) => b.into_lua(lua),
|
||||||
|
Self::AppResize(b) => b.into_lua(lua),
|
||||||
|
Self::AppResume(b) => b.into_lua(lua),
|
||||||
|
Self::AppStop(b) => b.into_lua(lua),
|
||||||
|
Self::AppUpdateProgress(b) => b.into_lua(lua),
|
||||||
|
|
||||||
// Mgr
|
// Mgr
|
||||||
Self::Arrow(b) => b.into_lua(lua),
|
Self::Arrow(b) => b.into_lua(lua),
|
||||||
Self::Back(b) => b.into_lua(lua),
|
Self::Back(b) => b.into_lua(lua),
|
||||||
|
|
@ -260,6 +291,7 @@ impl<'a> IntoLua for Spark<'a> {
|
||||||
Self::InputShow(b) => b.into_lua(lua),
|
Self::InputShow(b) => b.into_lua(lua),
|
||||||
|
|
||||||
// Notify
|
// Notify
|
||||||
|
Self::NotifyPush(b) => b.into_lua(lua),
|
||||||
Self::NotifyTick(b) => b.into_lua(lua),
|
Self::NotifyTick(b) => b.into_lua(lua),
|
||||||
|
|
||||||
// Pick
|
// Pick
|
||||||
|
|
@ -292,6 +324,8 @@ impl<'a> IntoLua for Spark<'a> {
|
||||||
|
|
||||||
try_from_spark!(
|
try_from_spark!(
|
||||||
VoidOpt,
|
VoidOpt,
|
||||||
|
app:bootstrap,
|
||||||
|
app:focus,
|
||||||
mgr:back,
|
mgr:back,
|
||||||
mgr:bulk_rename,
|
mgr:bulk_rename,
|
||||||
mgr:enter,
|
mgr:enter,
|
||||||
|
|
@ -309,7 +343,17 @@ try_from_spark!(
|
||||||
mgr:unyank,
|
mgr:unyank,
|
||||||
mgr:watch
|
mgr:watch
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// App
|
||||||
try_from_spark!(ArrowOpt, mgr:arrow, mgr:tab_swap);
|
try_from_spark!(ArrowOpt, mgr:arrow, mgr:tab_swap);
|
||||||
|
try_from_spark!(app::DeprecateOpt, app:deprecate);
|
||||||
|
try_from_spark!(app::MouseOpt, app:mouse);
|
||||||
|
try_from_spark!(app::PluginOpt, app:plugin, app:plugin_do);
|
||||||
|
try_from_spark!(app::QuitOpt, app:quit, mgr:quit);
|
||||||
|
try_from_spark!(app::ReflowOpt, app:reflow, app:resize);
|
||||||
|
try_from_spark!(app::ResumeOpt, app:resume);
|
||||||
|
try_from_spark!(app::StopOpt, app:stop);
|
||||||
|
try_from_spark!(app::UpdateProgressOpt, app:update_progress);
|
||||||
try_from_spark!(cmp::CloseOpt, cmp:close);
|
try_from_spark!(cmp::CloseOpt, cmp:close);
|
||||||
try_from_spark!(cmp::ShowOpt, cmp:show);
|
try_from_spark!(cmp::ShowOpt, cmp:show);
|
||||||
try_from_spark!(cmp::TriggerOpt, cmp:trigger);
|
try_from_spark!(cmp::TriggerOpt, cmp:trigger);
|
||||||
|
|
@ -347,7 +391,6 @@ try_from_spark!(mgr::OpenDoOpt, mgr:open_do);
|
||||||
try_from_spark!(mgr::OpenOpt, mgr:open);
|
try_from_spark!(mgr::OpenOpt, mgr:open);
|
||||||
try_from_spark!(mgr::PasteOpt, mgr:paste);
|
try_from_spark!(mgr::PasteOpt, mgr:paste);
|
||||||
try_from_spark!(mgr::PeekOpt, mgr:peek);
|
try_from_spark!(mgr::PeekOpt, mgr:peek);
|
||||||
try_from_spark!(mgr::QuitOpt, mgr:quit);
|
|
||||||
try_from_spark!(mgr::RemoveOpt, mgr:remove, mgr:remove_do);
|
try_from_spark!(mgr::RemoveOpt, mgr:remove, mgr:remove_do);
|
||||||
try_from_spark!(mgr::RenameOpt, mgr:rename);
|
try_from_spark!(mgr::RenameOpt, mgr:rename);
|
||||||
try_from_spark!(mgr::RevealOpt, mgr:reveal);
|
try_from_spark!(mgr::RevealOpt, mgr:reveal);
|
||||||
|
|
@ -371,11 +414,12 @@ try_from_spark!(mgr::UpdateYankedOpt<'a>, mgr:update_yanked);
|
||||||
try_from_spark!(mgr::UploadOpt, mgr:upload);
|
try_from_spark!(mgr::UploadOpt, mgr:upload);
|
||||||
try_from_spark!(mgr::VisualModeOpt, mgr:visual_mode);
|
try_from_spark!(mgr::VisualModeOpt, mgr:visual_mode);
|
||||||
try_from_spark!(mgr::YankOpt, mgr:yank);
|
try_from_spark!(mgr::YankOpt, mgr:yank);
|
||||||
|
try_from_spark!(notify::PushOpt, notify:push);
|
||||||
try_from_spark!(notify::TickOpt, notify:tick);
|
try_from_spark!(notify::TickOpt, notify:tick);
|
||||||
try_from_spark!(pick::CloseOpt, pick:close);
|
try_from_spark!(pick::CloseOpt, pick:close);
|
||||||
try_from_spark!(pick::ShowOpt, pick:show);
|
try_from_spark!(pick::ShowOpt, pick:show);
|
||||||
try_from_spark!(spot::CopyOpt, spot:copy);
|
try_from_spark!(spot::CopyOpt, spot:copy);
|
||||||
try_from_spark!(tasks::ProcessOpenOpt, tasks:process_open);
|
try_from_spark!(tasks::ProcessOpenOpt, tasks:process_open);
|
||||||
try_from_spark!(tasks::UpdateSucceedOpt, tasks:update_succeed);
|
try_from_spark!(tasks::UpdateSucceedOpt, tasks:update_succeed);
|
||||||
try_from_spark!(which::CallbackOpt, which:callback);
|
|
||||||
try_from_spark!(which::ActivateOpt, which:activate);
|
try_from_spark!(which::ActivateOpt, which:activate);
|
||||||
|
try_from_spark!(which::CallbackOpt, which:callback);
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@ use std::{sync::atomic::Ordering, time::{Duration, Instant}};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tokio::{select, time::sleep};
|
use tokio::{select, time::sleep};
|
||||||
|
use yazi_actor::Ctx;
|
||||||
use yazi_core::Core;
|
use yazi_core::Core;
|
||||||
use yazi_macro::act;
|
use yazi_macro::act;
|
||||||
use yazi_shared::event::{Event, NEED_RENDER};
|
use yazi_shared::{data::Data, event::{Event, NEED_RENDER}};
|
||||||
use yazi_term::Term;
|
use yazi_term::Term;
|
||||||
|
|
||||||
use crate::{Dispatcher, Signals};
|
use crate::{Dispatcher, Signals};
|
||||||
|
|
@ -21,21 +22,23 @@ impl App {
|
||||||
let (mut rx, signals) = (Event::take(), Signals::start()?);
|
let (mut rx, signals) = (Event::take(), Signals::start()?);
|
||||||
|
|
||||||
let mut app = Self { core: Core::make(), term: Some(term), signals };
|
let mut app = Self { core: Core::make(), term: Some(term), signals };
|
||||||
act!(bootstrap, app)?;
|
app.bootstrap()?;
|
||||||
|
|
||||||
let mut events = Vec::with_capacity(50);
|
let mut events = Vec::with_capacity(50);
|
||||||
let (mut timeout, mut last_render) = (None, Instant::now());
|
let (mut timeout, mut need_render, mut last_render) = (None, 0, Instant::now());
|
||||||
macro_rules! drain_events {
|
macro_rules! drain_events {
|
||||||
() => {
|
() => {
|
||||||
for event in events.drain(..) {
|
for event in events.drain(..) {
|
||||||
Dispatcher::new(&mut app).dispatch(event)?;
|
Dispatcher::new(&mut app).dispatch(event)?;
|
||||||
if !NEED_RENDER.load(Ordering::Relaxed) {
|
|
||||||
|
need_render = NEED_RENDER.load(Ordering::Relaxed);
|
||||||
|
if need_render == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout = Duration::from_millis(10).checked_sub(last_render.elapsed());
|
timeout = Duration::from_millis(10).checked_sub(last_render.elapsed());
|
||||||
if timeout.is_none() {
|
if timeout.is_none() {
|
||||||
act!(render, app)?;
|
app.render(need_render == 2)?;
|
||||||
last_render = Instant::now();
|
last_render = Instant::now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +49,7 @@ impl App {
|
||||||
if let Some(t) = timeout.take() {
|
if let Some(t) = timeout.take() {
|
||||||
select! {
|
select! {
|
||||||
_ = sleep(t) => {
|
_ = sleep(t) => {
|
||||||
act!(render, app)?;
|
app.render(need_render == 2)?;
|
||||||
last_render = Instant::now();
|
last_render = Instant::now();
|
||||||
}
|
}
|
||||||
n = rx.recv_many(&mut events, 50) => {
|
n = rx.recv_many(&mut events, 50) => {
|
||||||
|
|
@ -62,4 +65,11 @@ impl App {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bootstrap(&mut self) -> anyhow::Result<Data> {
|
||||||
|
let cx = &mut Ctx::active(&mut self.core, &mut self.term);
|
||||||
|
act!(app:bootstrap, cx)?;
|
||||||
|
|
||||||
|
self.render(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
use anyhow::Result;
|
|
||||||
use yazi_macro::succ;
|
|
||||||
use yazi_parser::app::{DeprecateOpt, NotifyLevel, NotifyOpt};
|
|
||||||
use yazi_shared::data::Data;
|
|
||||||
|
|
||||||
use crate::app::App;
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub(crate) fn deprecate(&mut self, opt: DeprecateOpt) -> Result<Data> {
|
|
||||||
succ!(self.core.notify.push(NotifyOpt {
|
|
||||||
title: "Deprecated API".to_owned(),
|
|
||||||
content: opt.content.into_owned(),
|
|
||||||
level: NotifyLevel::Warn,
|
|
||||||
timeout: std::time::Duration::from_secs(20),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
use anyhow::Result;
|
|
||||||
use yazi_actor::Ctx;
|
|
||||||
use yazi_macro::act;
|
|
||||||
use yazi_parser::VoidOpt;
|
|
||||||
use yazi_shared::data::Data;
|
|
||||||
|
|
||||||
use crate::app::App;
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub fn focus(&mut self, _: VoidOpt) -> Result<Data> {
|
|
||||||
let cx = &mut Ctx::active(&mut self.core);
|
|
||||||
|
|
||||||
act!(mgr:refresh, cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
use anyhow::Result;
|
|
||||||
use yazi_macro::succ;
|
|
||||||
use yazi_parser::app::NotifyOpt;
|
|
||||||
use yazi_shared::data::Data;
|
|
||||||
|
|
||||||
use crate::app::App;
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub(crate) fn notify(&mut self, opt: NotifyOpt) -> Result<Data> {
|
|
||||||
succ!(self.core.notify.push(opt));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
use yazi_boot::ARGS;
|
|
||||||
use yazi_fs::provider::{Provider, local::Local};
|
|
||||||
use yazi_shared::{event::EventQuit, strand::{StrandBuf, StrandLike, ToStrand}};
|
|
||||||
use yazi_term::Term;
|
|
||||||
|
|
||||||
use crate::app::App;
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub(crate) fn quit(&mut self, opt: EventQuit) -> ! {
|
|
||||||
self.core.tasks.shutdown();
|
|
||||||
self.core.mgr.shutdown();
|
|
||||||
|
|
||||||
futures::executor::block_on(async {
|
|
||||||
_ = futures::join!(
|
|
||||||
yazi_dds::shutdown(),
|
|
||||||
yazi_dds::STATE.drain(),
|
|
||||||
self.cwd_to_file(opt.no_cwd_file),
|
|
||||||
self.selected_to_file(opt.selected)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
Term::goodbye(|| opt.code);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn cwd_to_file(&self, no: bool) {
|
|
||||||
if let Some(p) = ARGS.cwd_file.as_ref().filter(|_| !no) {
|
|
||||||
let cwd = self.core.mgr.cwd().to_strand();
|
|
||||||
Local::regular(p).write(cwd.encoded_bytes()).await.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn selected_to_file(&self, selected: Option<StrandBuf>) {
|
|
||||||
if let (Some(s), Some(p)) = (selected, &ARGS.chooser_file) {
|
|
||||||
Local::regular(p).write(s.encoded_bytes()).await.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
use anyhow::Result;
|
|
||||||
use yazi_actor::Ctx;
|
|
||||||
use yazi_macro::act;
|
|
||||||
use yazi_parser::VoidOpt;
|
|
||||||
use yazi_shared::data::Data;
|
|
||||||
|
|
||||||
use crate::app::App;
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub fn resize(&mut self, _: VoidOpt) -> Result<Data> {
|
|
||||||
act!(reflow, self)?;
|
|
||||||
|
|
||||||
self.core.current_mut().arrow(0);
|
|
||||||
self.core.parent_mut().map(|f| f.arrow(0));
|
|
||||||
self.core.current_mut().sync_page(true);
|
|
||||||
|
|
||||||
let cx = &mut Ctx::active(&mut self.core);
|
|
||||||
act!(mgr:peek, cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
use anyhow::Result;
|
|
||||||
use yazi_macro::{act, render, succ};
|
|
||||||
use yazi_parser::app::ResumeOpt;
|
|
||||||
use yazi_shared::data::Data;
|
|
||||||
use yazi_term::Term;
|
|
||||||
|
|
||||||
use crate::app::App;
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub(crate) fn resume(&mut self, opt: ResumeOpt) -> Result<Data> {
|
|
||||||
self.core.active_mut().preview.reset();
|
|
||||||
self.term = Some(Term::start().unwrap());
|
|
||||||
|
|
||||||
// While the app resumes, it's possible that the terminal size has changed.
|
|
||||||
// We need to trigger a resize, and render the UI based on the resized area.
|
|
||||||
act!(resize, self)?;
|
|
||||||
|
|
||||||
self.signals.resume(opt.token);
|
|
||||||
|
|
||||||
succ!(render!());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
use anyhow::Result;
|
|
||||||
use ratatui::layout::Rect;
|
|
||||||
use yazi_emulator::Dimension;
|
|
||||||
use yazi_macro::act;
|
|
||||||
use yazi_parser::notify::TickOpt;
|
|
||||||
use yazi_shared::data::Data;
|
|
||||||
|
|
||||||
use crate::{app::App, notify};
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub(crate) fn update_notify(&mut self, opt: TickOpt) -> Result<Data> {
|
|
||||||
let Dimension { rows, columns, .. } = Dimension::available();
|
|
||||||
let area =
|
|
||||||
notify::Notify::available(Rect { x: 0, y: 0, width: columns, height: rows });
|
|
||||||
|
|
||||||
self.core.notify.tick(opt, area);
|
|
||||||
|
|
||||||
if self.core.notify.messages.is_empty() {
|
|
||||||
act!(render, self)
|
|
||||||
} else {
|
|
||||||
act!(render_partially, self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1 @@
|
||||||
yazi_macro::mod_pub!(commands);
|
yazi_macro::mod_flat!(app render);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(app);
|
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,20 @@ use ratatui::{CompletedFrame, backend::{Backend, CrosstermBackend}, buffer::Buff
|
||||||
use yazi_actor::{Ctx, lives::Lives};
|
use yazi_actor::{Ctx, lives::Lives};
|
||||||
use yazi_binding::elements::COLLISION;
|
use yazi_binding::elements::COLLISION;
|
||||||
use yazi_macro::{act, succ};
|
use yazi_macro::{act, succ};
|
||||||
use yazi_parser::VoidOpt;
|
|
||||||
use yazi_shared::{data::Data, event::NEED_RENDER};
|
use yazi_shared::{data::Data, event::NEED_RENDER};
|
||||||
use yazi_tty::TTY;
|
use yazi_tty::TTY;
|
||||||
|
|
||||||
use crate::{app::App, root::Root};
|
use crate::{app::App, root::Root};
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub(crate) fn render(&mut self, _: VoidOpt) -> Result<Data> {
|
pub(crate) fn render(&mut self, partial: bool) -> Result<Data> {
|
||||||
NEED_RENDER.store(false, Ordering::Relaxed);
|
NEED_RENDER.store(0, Ordering::Relaxed);
|
||||||
let Some(term) = &mut self.term else { succ!() };
|
let Some(term) = &mut self.term else { succ!() };
|
||||||
|
|
||||||
|
if partial {
|
||||||
|
return self.render_partially();
|
||||||
|
}
|
||||||
|
|
||||||
Self::routine(true, None);
|
Self::routine(true, None);
|
||||||
let _guard = scopeguard::guard(self.core.cursor(), |c| Self::routine(false, c));
|
let _guard = scopeguard::guard(self.core.cursor(), |c| Self::routine(false, c));
|
||||||
|
|
||||||
|
|
@ -31,21 +34,21 @@ impl App {
|
||||||
Self::patch(frame);
|
Self::patch(frame);
|
||||||
}
|
}
|
||||||
if !self.core.notify.messages.is_empty() {
|
if !self.core.notify.messages.is_empty() {
|
||||||
act!(render_partially, self)?;
|
self.render_partially()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload preview if collision is resolved
|
// Reload preview if collision is resolved
|
||||||
if collision && !COLLISION.load(Ordering::Relaxed) {
|
if collision && !COLLISION.load(Ordering::Relaxed) {
|
||||||
let cx = &mut Ctx::active(&mut self.core);
|
let cx = &mut Ctx::active(&mut self.core, &mut self.term);
|
||||||
act!(mgr:peek, cx, true)?;
|
act!(mgr:peek, cx, true)?;
|
||||||
}
|
}
|
||||||
succ!();
|
succ!();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn render_partially(&mut self, _: VoidOpt) -> Result<Data> {
|
pub(crate) fn render_partially(&mut self) -> Result<Data> {
|
||||||
let Some(term) = &mut self.term else { succ!() };
|
let Some(term) = &mut self.term else { succ!() };
|
||||||
if !term.can_partial() {
|
if !term.can_partial() {
|
||||||
return act!(render, self);
|
return self.render(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::routine(true, None);
|
Self::routine(true, None);
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crossterm::event::KeyEvent;
|
use crossterm::event::{KeyEvent, MouseEvent};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
use yazi_actor::Ctx;
|
||||||
use yazi_config::keymap::Key;
|
use yazi_config::keymap::Key;
|
||||||
use yazi_macro::{act, emit, succ};
|
use yazi_macro::{act, emit, succ};
|
||||||
use yazi_shared::{data::Data, event::{CmdCow, Event, NEED_RENDER}};
|
use yazi_shared::{data::Data, event::{CmdCow, Event, NEED_RENDER}};
|
||||||
|
|
@ -20,17 +21,15 @@ impl<'a> Dispatcher<'a> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn dispatch(&mut self, event: Event) -> Result<()> {
|
pub(super) fn dispatch(&mut self, event: Event) -> Result<()> {
|
||||||
// FIXME: handle errors
|
|
||||||
let result = match event {
|
let result = match event {
|
||||||
Event::Call(cmd) => self.dispatch_call(cmd),
|
Event::Call(cmd) => self.dispatch_call(cmd),
|
||||||
Event::Seq(cmds) => self.dispatch_seq(cmds),
|
Event::Seq(cmds) => self.dispatch_seq(cmds),
|
||||||
Event::Render => self.dispatch_render(),
|
Event::Render(partial) => self.dispatch_render(partial),
|
||||||
Event::Key(key) => self.dispatch_key(key),
|
Event::Key(key) => self.dispatch_key(key),
|
||||||
Event::Mouse(mouse) => act!(mouse, self.app, mouse),
|
Event::Mouse(mouse) => self.dispatch_mouse(mouse),
|
||||||
Event::Resize => act!(resize, self.app),
|
Event::Resize => self.dispatch_resize(),
|
||||||
Event::Focus => act!(focus, self.app),
|
Event::Focus => self.dispatch_focus(),
|
||||||
Event::Paste(str) => self.dispatch_paste(str),
|
Event::Paste(str) => self.dispatch_paste(str),
|
||||||
Event::Quit(opt) => act!(quit, self.app, opt),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
|
|
@ -54,8 +53,13 @@ impl<'a> Dispatcher<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn dispatch_render(&mut self) -> Result<Data> {
|
fn dispatch_render(&mut self, partial: bool) -> Result<Data> {
|
||||||
succ!(NEED_RENDER.store(true, Ordering::Relaxed))
|
if partial {
|
||||||
|
_ = NEED_RENDER.compare_exchange(0, 2, Ordering::Relaxed, Ordering::Relaxed);
|
||||||
|
} else {
|
||||||
|
NEED_RENDER.store(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
succ!()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -64,6 +68,24 @@ impl<'a> Dispatcher<'a> {
|
||||||
succ!();
|
succ!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn dispatch_mouse(&mut self, mouse: MouseEvent) -> Result<Data> {
|
||||||
|
let cx = &mut Ctx::active(&mut self.app.core, &mut self.app.term);
|
||||||
|
act!(app:mouse, cx, mouse)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn dispatch_resize(&mut self) -> Result<Data> {
|
||||||
|
let cx = &mut Ctx::active(&mut self.app.core, &mut self.app.term);
|
||||||
|
act!(app:resize, cx, crate::Root::reflow as fn(_) -> _)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn dispatch_focus(&mut self) -> Result<Data> {
|
||||||
|
let cx = &mut Ctx::active(&mut self.app.core, &mut self.app.term);
|
||||||
|
act!(app:focus, cx)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn dispatch_paste(&mut self, str: String) -> Result<Data> {
|
fn dispatch_paste(&mut self, str: String) -> Result<Data> {
|
||||||
if self.app.core.input.visible {
|
if self.app.core.input.visible {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use anyhow::Result;
|
use anyhow::{Context, Result};
|
||||||
use yazi_actor::Ctx;
|
use yazi_actor::Ctx;
|
||||||
use yazi_macro::{act, succ};
|
use yazi_macro::{act, succ};
|
||||||
use yazi_shared::{Layer, data::Data, event::CmdCow};
|
use yazi_shared::{Layer, data::Data, event::CmdCow};
|
||||||
|
|
@ -27,34 +27,45 @@ impl<'a> Executor<'a> {
|
||||||
Layer::Help => self.help(cmd),
|
Layer::Help => self.help(cmd),
|
||||||
Layer::Cmp => self.cmp(cmd),
|
Layer::Cmp => self.cmp(cmd),
|
||||||
Layer::Which => self.which(cmd),
|
Layer::Which => self.which(cmd),
|
||||||
|
Layer::Notify => self.notify(cmd),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app(&mut self, cmd: CmdCow) -> Result<Data> {
|
fn app(&mut self, mut cmd: CmdCow) -> Result<Data> {
|
||||||
|
let cx = &mut Ctx::new(&cmd, &mut self.app.core, &mut self.app.term)?;
|
||||||
|
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if cmd.name == stringify!($name) {
|
if cmd.name == stringify!($name) {
|
||||||
return act!($name, self.app, cmd);
|
return act!(app:$name, cx, cmd);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
on!(accept_payload);
|
on!(accept_payload);
|
||||||
on!(notify);
|
|
||||||
on!(plugin);
|
on!(plugin);
|
||||||
on!(plugin_do);
|
on!(plugin_do);
|
||||||
on!(update_notify);
|
|
||||||
on!(update_progress);
|
on!(update_progress);
|
||||||
on!(resize);
|
|
||||||
on!(stop);
|
|
||||||
on!(resume);
|
|
||||||
on!(deprecate);
|
on!(deprecate);
|
||||||
|
on!(quit);
|
||||||
|
|
||||||
succ!();
|
match &*cmd.name {
|
||||||
|
"resize" => act!(app:resize, cx, crate::Root::reflow as fn(_) -> _),
|
||||||
|
"resume" => act!(app:resume, cx, yazi_parser::app::ResumeOpt {
|
||||||
|
tx: self.app.signals.tx.clone(),
|
||||||
|
token: cmd.take_any("token").context("Invalid 'token' in ResumeOpt")?,
|
||||||
|
reflow: crate::Root::reflow,
|
||||||
|
}),
|
||||||
|
"stop" => act!(app:stop, cx, yazi_parser::app::StopOpt {
|
||||||
|
tx: self.app.signals.tx.clone(),
|
||||||
|
token: cmd.take_any("token").context("Invalid 'token' in StopOpt")?,
|
||||||
|
}),
|
||||||
|
_ => succ!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mgr(&mut self, cmd: CmdCow) -> Result<Data> {
|
fn mgr(&mut self, cmd: CmdCow) -> Result<Data> {
|
||||||
let cx = &mut Ctx::new(&mut self.app.core, &cmd)?;
|
let cx = &mut Ctx::new(&cmd, &mut self.app.core, &mut self.app.term)?;
|
||||||
|
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
|
|
@ -143,13 +154,13 @@ impl<'a> Executor<'a> {
|
||||||
// Help
|
// Help
|
||||||
"help" => act!(help:toggle, cx, Layer::Mgr),
|
"help" => act!(help:toggle, cx, Layer::Mgr),
|
||||||
// Plugin
|
// Plugin
|
||||||
"plugin" => act!(plugin, self.app, cmd),
|
"plugin" => act!(app:plugin, cx, cmd),
|
||||||
_ => succ!(),
|
_ => succ!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tasks(&mut self, cmd: CmdCow) -> Result<Data> {
|
fn tasks(&mut self, cmd: CmdCow) -> Result<Data> {
|
||||||
let cx = &mut Ctx::new(&mut self.app.core, &cmd)?;
|
let cx = &mut Ctx::new(&cmd, &mut self.app.core, &mut self.app.term)?;
|
||||||
|
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
|
|
@ -173,13 +184,13 @@ impl<'a> Executor<'a> {
|
||||||
// Help
|
// Help
|
||||||
"help" => act!(help:toggle, cx, Layer::Tasks),
|
"help" => act!(help:toggle, cx, Layer::Tasks),
|
||||||
// Plugin
|
// Plugin
|
||||||
"plugin" => act!(plugin, self.app, cmd),
|
"plugin" => act!(app:plugin, cx, cmd),
|
||||||
_ => succ!(),
|
_ => succ!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spot(&mut self, cmd: CmdCow) -> Result<Data> {
|
fn spot(&mut self, cmd: CmdCow) -> Result<Data> {
|
||||||
let cx = &mut Ctx::new(&mut self.app.core, &cmd)?;
|
let cx = &mut Ctx::new(&cmd, &mut self.app.core, &mut self.app.term)?;
|
||||||
|
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
|
|
@ -198,13 +209,13 @@ impl<'a> Executor<'a> {
|
||||||
// Help
|
// Help
|
||||||
"help" => act!(help:toggle, cx, Layer::Spot),
|
"help" => act!(help:toggle, cx, Layer::Spot),
|
||||||
// Plugin
|
// Plugin
|
||||||
"plugin" => act!(plugin, self.app, cmd),
|
"plugin" => act!(app:plugin, cx, cmd),
|
||||||
_ => succ!(),
|
_ => succ!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pick(&mut self, cmd: CmdCow) -> Result<Data> {
|
fn pick(&mut self, cmd: CmdCow) -> Result<Data> {
|
||||||
let cx = &mut Ctx::new(&mut self.app.core, &cmd)?;
|
let cx = &mut Ctx::new(&cmd, &mut self.app.core, &mut self.app.term)?;
|
||||||
|
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
|
|
@ -222,14 +233,14 @@ impl<'a> Executor<'a> {
|
||||||
// Help
|
// Help
|
||||||
"help" => act!(help:toggle, cx, Layer::Pick),
|
"help" => act!(help:toggle, cx, Layer::Pick),
|
||||||
// Plugin
|
// Plugin
|
||||||
"plugin" => act!(plugin, self.app, cmd),
|
"plugin" => act!(app:plugin, cx, cmd),
|
||||||
_ => succ!(),
|
_ => succ!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input(&mut self, cmd: CmdCow) -> Result<Data> {
|
fn input(&mut self, cmd: CmdCow) -> Result<Data> {
|
||||||
let mode = self.app.core.input.mode();
|
let mode = self.app.core.input.mode();
|
||||||
let cx = &mut Ctx::new(&mut self.app.core, &cmd)?;
|
let cx = &mut Ctx::new(&cmd, &mut self.app.core, &mut self.app.term)?;
|
||||||
|
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
|
|
@ -249,7 +260,7 @@ impl<'a> Executor<'a> {
|
||||||
// Help
|
// Help
|
||||||
"help" => return act!(help:toggle, cx, Layer::Input),
|
"help" => return act!(help:toggle, cx, Layer::Input),
|
||||||
// Plugin
|
// Plugin
|
||||||
"plugin" => return act!(plugin, self.app, cmd),
|
"plugin" => return act!(app:plugin, cx, cmd),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -263,7 +274,7 @@ impl<'a> Executor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm(&mut self, cmd: CmdCow) -> Result<Data> {
|
fn confirm(&mut self, cmd: CmdCow) -> Result<Data> {
|
||||||
let cx = &mut Ctx::new(&mut self.app.core, &cmd)?;
|
let cx = &mut Ctx::new(&cmd, &mut self.app.core, &mut self.app.term)?;
|
||||||
|
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
|
|
@ -281,7 +292,7 @@ impl<'a> Executor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn help(&mut self, cmd: CmdCow) -> Result<Data> {
|
fn help(&mut self, cmd: CmdCow) -> Result<Data> {
|
||||||
let cx = &mut Ctx::new(&mut self.app.core, &cmd)?;
|
let cx = &mut Ctx::new(&cmd, &mut self.app.core, &mut self.app.term)?;
|
||||||
|
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
|
|
@ -299,13 +310,13 @@ impl<'a> Executor<'a> {
|
||||||
// Help
|
// Help
|
||||||
"close" => act!(help:toggle, cx, Layer::Help),
|
"close" => act!(help:toggle, cx, Layer::Help),
|
||||||
// Plugin
|
// Plugin
|
||||||
"plugin" => act!(plugin, self.app, cmd),
|
"plugin" => act!(app:plugin, cx, cmd),
|
||||||
_ => succ!(),
|
_ => succ!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cmp(&mut self, cmd: CmdCow) -> Result<Data> {
|
fn cmp(&mut self, cmd: CmdCow) -> Result<Data> {
|
||||||
let cx = &mut Ctx::new(&mut self.app.core, &cmd)?;
|
let cx = &mut Ctx::new(&cmd, &mut self.app.core, &mut self.app.term)?;
|
||||||
|
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
|
|
@ -324,13 +335,13 @@ impl<'a> Executor<'a> {
|
||||||
// Help
|
// Help
|
||||||
"help" => act!(help:toggle, cx, Layer::Cmp),
|
"help" => act!(help:toggle, cx, Layer::Cmp),
|
||||||
// Plugin
|
// Plugin
|
||||||
"plugin" => act!(plugin, self.app, cmd),
|
"plugin" => act!(app:plugin, cx, cmd),
|
||||||
_ => succ!(),
|
_ => succ!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn which(&mut self, cmd: CmdCow) -> Result<Data> {
|
fn which(&mut self, cmd: CmdCow) -> Result<Data> {
|
||||||
let cx = &mut Ctx::new(&mut self.app.core, &cmd)?;
|
let cx = &mut Ctx::new(&cmd, &mut self.app.core, &mut self.app.term)?;
|
||||||
|
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
|
|
@ -345,4 +356,21 @@ impl<'a> Executor<'a> {
|
||||||
|
|
||||||
succ!();
|
succ!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, cmd: CmdCow) -> Result<Data> {
|
||||||
|
let cx = &mut Ctx::new(&cmd, &mut self.app.core, &mut self.app.term)?;
|
||||||
|
|
||||||
|
macro_rules! on {
|
||||||
|
($name:ident) => {
|
||||||
|
if cmd.name == stringify!($name) {
|
||||||
|
return act!(notify:$name, cx, cmd);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
on!(push);
|
||||||
|
on!(tick);
|
||||||
|
|
||||||
|
succ!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,6 @@ pub(crate) struct Notify<'a> {
|
||||||
impl<'a> Notify<'a> {
|
impl<'a> Notify<'a> {
|
||||||
pub(crate) fn new(core: &'a Core) -> Self { Self { core } }
|
pub(crate) fn new(core: &'a Core) -> Self { Self { core } }
|
||||||
|
|
||||||
pub(crate) fn available(area: Rect) -> Rect {
|
|
||||||
let chunks = layout::Layout::horizontal([Constraint::Fill(1), Constraint::Min(80)]).split(area);
|
|
||||||
|
|
||||||
let chunks =
|
|
||||||
layout::Layout::vertical([Constraint::Fill(1), Constraint::Max(1)]).split(chunks[1]);
|
|
||||||
|
|
||||||
chunks[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tiles<'m>(area: Rect, messages: impl Iterator<Item = &'m Message> + Clone) -> Vec<Rect> {
|
fn tiles<'m>(area: Rect, messages: impl Iterator<Item = &'m Message> + Clone) -> Vec<Rect> {
|
||||||
layout::Layout::vertical(
|
layout::Layout::vertical(
|
||||||
[Constraint::Fill(1)]
|
[Constraint::Fill(1)]
|
||||||
|
|
@ -42,7 +33,7 @@ impl<'a> Notify<'a> {
|
||||||
impl Widget for Notify<'_> {
|
impl Widget for Notify<'_> {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let notify = &self.core.notify;
|
let notify = &self.core.notify;
|
||||||
let available = Self::available(area);
|
let available = yazi_core::notify::Notify::available(area);
|
||||||
|
|
||||||
let messages = notify.messages.iter().take(notify.limit(available)).rev();
|
let messages = notify.messages.iter().take(notify.limit(available)).rev();
|
||||||
let tiles = Self::tiles(available, messages.clone());
|
let tiles = Self::tiles(available, messages.clone());
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ impl<'a> Router<'a> {
|
||||||
|
|
||||||
use Layer as L;
|
use Layer as L;
|
||||||
Ok(match layer {
|
Ok(match layer {
|
||||||
L::App => unreachable!(),
|
L::App | L::Notify => unreachable!(),
|
||||||
L::Mgr | L::Tasks | L::Spot | L::Pick | L::Input | L::Confirm | L::Help => {
|
L::Mgr | L::Tasks | L::Spot | L::Pick | L::Input | L::Confirm | L::Help => {
|
||||||
self.matches(layer, key)
|
self.matches(layer, key)
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ impl<'a> Router<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if on.len() > 1 {
|
if on.len() > 1 {
|
||||||
let cx = &mut Ctx::active(&mut self.app.core);
|
let cx = &mut Ctx::active(&mut self.app.core, &mut self.app.term);
|
||||||
act!(which:activate, cx, (layer, key)).ok();
|
act!(which:activate, cx, (layer, key)).ok();
|
||||||
} else {
|
} else {
|
||||||
emit!(Seq(ChordCow::from(chord).into_seq()));
|
emit!(Seq(ChordCow::from(chord).into_seq()));
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use yazi_config::YAZI;
|
||||||
use yazi_shared::{CompletionToken, event::Event};
|
use yazi_shared::{CompletionToken, event::Event};
|
||||||
|
|
||||||
pub(super) struct Signals {
|
pub(super) struct Signals {
|
||||||
tx: mpsc::UnboundedSender<(bool, CompletionToken)>,
|
pub(super) tx: mpsc::UnboundedSender<(bool, CompletionToken)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Signals {
|
impl Signals {
|
||||||
|
|
@ -17,10 +17,6 @@ impl Signals {
|
||||||
Ok(Self { tx })
|
Ok(Self { tx })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn stop(&mut self, token: CompletionToken) { self.tx.send((false, token)).ok(); }
|
|
||||||
|
|
||||||
pub(super) fn resume(&mut self, token: CompletionToken) { self.tx.send((true, token)).ok(); }
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn handle_sys(n: libc::c_int) -> bool {
|
fn handle_sys(n: libc::c_int) -> bool {
|
||||||
use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGSTOP, SIGTERM, SIGTSTP};
|
use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGSTOP, SIGTERM, SIGTSTP};
|
||||||
|
|
@ -30,7 +26,7 @@ impl Signals {
|
||||||
match n {
|
match n {
|
||||||
SIGINT => { /* ignored */ }
|
SIGINT => { /* ignored */ }
|
||||||
SIGQUIT | SIGHUP | SIGTERM => {
|
SIGQUIT | SIGHUP | SIGTERM => {
|
||||||
Event::Quit(Default::default()).emit();
|
AppProxy::quit(Default::default());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SIGTSTP => {
|
SIGTSTP => {
|
||||||
|
|
@ -38,7 +34,7 @@ impl Signals {
|
||||||
AppProxy::stop().await;
|
AppProxy::stop().await;
|
||||||
if unsafe { libc::kill(0, SIGSTOP) } != 0 {
|
if unsafe { libc::kill(0, SIGSTOP) } != 0 {
|
||||||
error!("Failed to stop the process:\n{}", std::io::Error::last_os_error());
|
error!("Failed to stop the process:\n{}", std::io::Error::last_os_error());
|
||||||
Event::Quit(Default::default()).emit();
|
AppProxy::quit(Default::default());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! emit {
|
macro_rules! emit {
|
||||||
(Quit($opt:expr)) => {
|
|
||||||
yazi_shared::event::Event::Quit($opt).emit();
|
|
||||||
};
|
|
||||||
(Call($cmd:expr)) => {
|
(Call($cmd:expr)) => {
|
||||||
yazi_shared::event::Event::Call(yazi_shared::event::CmdCow::from($cmd)).emit();
|
yazi_shared::event::Event::Call(yazi_shared::event::CmdCow::from($cmd)).emit();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! render {
|
macro_rules! render {
|
||||||
() => {
|
() => {
|
||||||
yazi_shared::event::NEED_RENDER.store(true, std::sync::atomic::Ordering::Relaxed);
|
yazi_shared::event::NEED_RENDER.store(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
};
|
};
|
||||||
($cond:expr) => {
|
($cond:expr) => {
|
||||||
if $cond {
|
if $cond {
|
||||||
|
|
@ -28,3 +28,15 @@ macro_rules! render_and {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! render_partial {
|
||||||
|
() => {{
|
||||||
|
_ = yazi_shared::event::NEED_RENDER.compare_exchange(
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
std::sync::atomic::Ordering::Relaxed,
|
||||||
|
std::sync::atomic::Ordering::Relaxed,
|
||||||
|
);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ dyn-clone = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
mlua = { workspace = true }
|
mlua = { workspace = true }
|
||||||
ordered-float = { workspace = true }
|
ordered-float = { workspace = true }
|
||||||
|
ratatui = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_with = { workspace = true }
|
serde_with = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||||
use yazi_shared::{SStr, event::CmdCow};
|
use yazi_shared::{SStr, event::CmdCow};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct DeprecateOpt {
|
pub struct DeprecateOpt {
|
||||||
pub content: SStr,
|
pub content: SStr,
|
||||||
}
|
}
|
||||||
|
|
@ -16,3 +18,11 @@ impl TryFrom<CmdCow> for DeprecateOpt {
|
||||||
Ok(Self { content })
|
Ok(Self { content })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromLua for DeprecateOpt {
|
||||||
|
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoLua for DeprecateOpt {
|
||||||
|
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
yazi_macro::mod_flat!(deprecate mouse notify plugin resume stop update_progress);
|
yazi_macro::mod_flat!(deprecate mouse plugin quit reflow resume stop update_progress);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use crossterm::event::MouseEvent;
|
use crossterm::event::MouseEvent;
|
||||||
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct MouseOpt {
|
pub struct MouseOpt {
|
||||||
pub event: MouseEvent,
|
pub event: MouseEvent,
|
||||||
}
|
}
|
||||||
|
|
@ -7,3 +9,11 @@ pub struct MouseOpt {
|
||||||
impl From<MouseEvent> for MouseOpt {
|
impl From<MouseEvent> for MouseOpt {
|
||||||
fn from(event: MouseEvent) -> Self { Self { event } }
|
fn from(event: MouseEvent) -> Self { Self { event } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromLua for MouseOpt {
|
||||||
|
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoLua for MouseOpt {
|
||||||
|
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::{borrow::Cow, fmt::{self, Debug}, str::FromStr};
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use dyn_clone::DynClone;
|
use dyn_clone::DynClone;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use mlua::{Lua, Table};
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use yazi_shared::{SStr, data::{Data, DataKey}, event::{Cmd, CmdCow}};
|
use yazi_shared::{SStr, data::{Data, DataKey}, event::{Cmd, CmdCow}};
|
||||||
|
|
||||||
|
|
@ -39,6 +39,14 @@ impl TryFrom<CmdCow> for PluginOpt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromLua for PluginOpt {
|
||||||
|
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoLua for PluginOpt {
|
||||||
|
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
||||||
|
}
|
||||||
|
|
||||||
impl PluginOpt {
|
impl PluginOpt {
|
||||||
pub fn new_callback(id: impl Into<SStr>, f: impl PluginCallback) -> Self {
|
pub fn new_callback(id: impl Into<SStr>, f: impl PluginCallback) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
35
yazi-parser/src/app/quit.rs
Normal file
35
yazi-parser/src/app/quit.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use yazi_shared::{event::CmdCow, strand::StrandBuf};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
|
pub struct QuitOpt {
|
||||||
|
pub code: i32,
|
||||||
|
#[serde(skip)] // FIXME
|
||||||
|
pub selected: Option<StrandBuf>,
|
||||||
|
pub no_cwd_file: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<CmdCow> for QuitOpt {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
|
||||||
|
if let Some(opt) = c.take_any2("opt") {
|
||||||
|
return opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
code: c.get("code").unwrap_or_default(),
|
||||||
|
selected: None, // FIXME
|
||||||
|
no_cwd_file: c.bool("no-cwd-file"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromLua for QuitOpt {
|
||||||
|
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> { lua.from_value(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoLua for QuitOpt {
|
||||||
|
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { lua.to_value(&self) }
|
||||||
|
}
|
||||||
19
yazi-parser/src/app/reflow.rs
Normal file
19
yazi-parser/src/app/reflow.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value};
|
||||||
|
use ratatui::layout::Rect;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ReflowOpt {
|
||||||
|
pub reflow: fn(Rect) -> mlua::Result<Table>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<fn(Rect) -> mlua::Result<Table>> for ReflowOpt {
|
||||||
|
fn from(f: fn(Rect) -> mlua::Result<Table>) -> Self { Self { reflow: f } }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromLua for ReflowOpt {
|
||||||
|
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoLua for ReflowOpt {
|
||||||
|
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
||||||
|
}
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
use anyhow::bail;
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value};
|
||||||
use yazi_shared::{CompletionToken, event::CmdCow};
|
use ratatui::layout::Rect;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use yazi_shared::CompletionToken;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ResumeOpt {
|
pub struct ResumeOpt {
|
||||||
|
pub tx: mpsc::UnboundedSender<(bool, CompletionToken)>,
|
||||||
pub token: CompletionToken,
|
pub token: CompletionToken,
|
||||||
|
pub reflow: fn(Rect) -> mlua::Result<Table>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<CmdCow> for ResumeOpt {
|
impl FromLua for ResumeOpt {
|
||||||
type Error = anyhow::Error;
|
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||||
|
|
||||||
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
|
|
||||||
let Some(token) = c.take_any("token") else {
|
|
||||||
bail!("Invalid 'token' in ResumeOpt");
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self { token })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoLua for ResumeOpt {
|
||||||
|
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
use anyhow::bail;
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||||
use yazi_shared::{CompletionToken, event::CmdCow};
|
use tokio::sync::mpsc;
|
||||||
|
use yazi_shared::CompletionToken;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct StopOpt {
|
pub struct StopOpt {
|
||||||
|
pub tx: mpsc::UnboundedSender<(bool, CompletionToken)>,
|
||||||
pub token: CompletionToken,
|
pub token: CompletionToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<CmdCow> for StopOpt {
|
impl FromLua for StopOpt {
|
||||||
type Error = anyhow::Error;
|
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||||
|
|
||||||
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
|
|
||||||
let Some(token) = c.take_any("token") else {
|
|
||||||
bail!("Invalid 'token' in StopOpt");
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self { token })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoLua for StopOpt {
|
||||||
|
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use yazi_shared::event::CmdCow;
|
use yazi_shared::event::CmdCow;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct UpdateProgressOpt {
|
pub struct UpdateProgressOpt {
|
||||||
pub summary: TaskSummary,
|
pub summary: TaskSummary,
|
||||||
}
|
}
|
||||||
|
|
@ -19,6 +21,14 @@ impl TryFrom<CmdCow> for UpdateProgressOpt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromLua for UpdateProgressOpt {
|
||||||
|
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoLua for UpdateProgressOpt {
|
||||||
|
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
||||||
|
}
|
||||||
|
|
||||||
// --- Progress
|
// --- Progress
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
|
||||||
pub struct TaskSummary {
|
pub struct TaskSummary {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||||
use yazi_shared::event::CmdCow;
|
use yazi_shared::event::CmdCow;
|
||||||
|
|
||||||
use crate::mgr::QuitOpt;
|
use crate::app::QuitOpt;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct CloseOpt(pub QuitOpt);
|
pub struct CloseOpt(pub QuitOpt);
|
||||||
|
|
||||||
impl From<CmdCow> for CloseOpt {
|
impl TryFrom<CmdCow> for CloseOpt {
|
||||||
fn from(c: CmdCow) -> Self { Self(c.into()) }
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(c: CmdCow) -> Result<Self, Self::Error> { c.try_into().map(Self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromLua for CloseOpt {
|
impl FromLua for CloseOpt {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ yazi_macro::mod_flat!(
|
||||||
open_do
|
open_do
|
||||||
paste
|
paste
|
||||||
peek
|
peek
|
||||||
quit
|
|
||||||
remove
|
remove
|
||||||
rename
|
rename
|
||||||
reveal
|
reveal
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use yazi_shared::event::{CmdCow, EventQuit};
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
|
||||||
pub struct QuitOpt {
|
|
||||||
pub code: i32,
|
|
||||||
pub no_cwd_file: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CmdCow> for QuitOpt {
|
|
||||||
fn from(c: CmdCow) -> Self {
|
|
||||||
Self { code: c.get("code").unwrap_or_default(), no_cwd_file: c.bool("no-cwd-file") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<QuitOpt> for EventQuit {
|
|
||||||
fn from(value: QuitOpt) -> Self {
|
|
||||||
Self { code: value.code, no_cwd_file: value.no_cwd_file, ..Default::default() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromLua for QuitOpt {
|
|
||||||
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> { lua.from_value(value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoLua for QuitOpt {
|
|
||||||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { lua.to_value(&self) }
|
|
||||||
}
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
yazi_macro::mod_flat!(tick);
|
yazi_macro::mod_flat!(push tick);
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,16 @@ use yazi_config::{Style, THEME};
|
||||||
use yazi_shared::event::CmdCow;
|
use yazi_shared::event::CmdCow;
|
||||||
|
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct NotifyOpt {
|
pub struct PushOpt {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub level: NotifyLevel,
|
pub level: PushLevel,
|
||||||
#[serde_as(as = "DurationSeconds<f64>")] // FIXME
|
#[serde_as(as = "DurationSeconds<f64>")] // FIXME
|
||||||
pub timeout: Duration,
|
pub timeout: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<CmdCow> for NotifyOpt {
|
impl TryFrom<CmdCow> for PushOpt {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
|
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
|
||||||
|
|
@ -25,25 +25,25 @@ impl TryFrom<CmdCow> for NotifyOpt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromLua for NotifyOpt {
|
impl FromLua for PushOpt {
|
||||||
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> { lua.from_value(value) }
|
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> { lua.from_value(value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoLua for NotifyOpt {
|
impl IntoLua for PushOpt {
|
||||||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { lua.to_value(&self) }
|
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { lua.to_value(&self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Level
|
// --- Level
|
||||||
#[derive(Clone, Copy, Default, Deserialize, Eq, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum NotifyLevel {
|
pub enum PushLevel {
|
||||||
#[default]
|
#[default]
|
||||||
Info,
|
Info,
|
||||||
Warn,
|
Warn,
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NotifyLevel {
|
impl PushLevel {
|
||||||
pub fn icon(self) -> &'static str {
|
pub fn icon(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Info => &THEME.notify.icon_info,
|
Self::Info => &THEME.notify.icon_info,
|
||||||
|
|
@ -61,7 +61,7 @@ impl NotifyLevel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for NotifyLevel {
|
impl FromStr for PushLevel {
|
||||||
type Err = serde::de::value::Error;
|
type Err = serde::de::value::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
|
@ -6,8 +6,8 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||||
use yazi_binding::{elements::{Line, Pos, Text}, runtime};
|
use yazi_binding::{elements::{Line, Pos, Text}, runtime};
|
||||||
use yazi_config::{keymap::{Chord, ChordCow, Key}, popup::{ConfirmCfg, InputCfg}};
|
use yazi_config::{keymap::{Chord, ChordCow, Key}, popup::{ConfirmCfg, InputCfg}};
|
||||||
use yazi_macro::relay;
|
use yazi_macro::relay;
|
||||||
use yazi_parser::{app::NotifyOpt, which::ActivateOpt};
|
use yazi_parser::{notify::PushOpt, which::ActivateOpt};
|
||||||
use yazi_proxy::{AppProxy, ConfirmProxy, InputProxy, WhichProxy};
|
use yazi_proxy::{ConfirmProxy, InputProxy, NotifyProxy, WhichProxy};
|
||||||
use yazi_shared::Debounce;
|
use yazi_shared::Debounce;
|
||||||
|
|
||||||
use super::Utils;
|
use super::Utils;
|
||||||
|
|
@ -100,7 +100,7 @@ impl Utils {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn notify(lua: &Lua) -> mlua::Result<Function> {
|
pub(super) fn notify(lua: &Lua) -> mlua::Result<Function> {
|
||||||
lua.create_function(|_, opt: NotifyOpt| Ok(AppProxy::notify(opt)))
|
lua.create_function(|_, opt: PushOpt| Ok(NotifyProxy::push(opt)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_keys(value: Value) -> mlua::Result<Vec<Key>> {
|
fn parse_keys(value: Value) -> mlua::Result<Vec<Key>> {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use yazi_macro::{emit, relay};
|
use yazi_macro::{emit, relay};
|
||||||
use yazi_parser::app::{NotifyLevel, NotifyOpt, PluginOpt, TaskSummary};
|
use yazi_parser::app::{PluginOpt, QuitOpt, TaskSummary};
|
||||||
use yazi_shared::CompletionToken;
|
use yazi_shared::CompletionToken;
|
||||||
|
|
||||||
pub struct AppProxy;
|
pub struct AppProxy;
|
||||||
|
|
||||||
impl AppProxy {
|
impl AppProxy {
|
||||||
|
pub fn quit(opt: QuitOpt) {
|
||||||
|
emit!(Call(relay!(app:quit).with_any("opt", opt)));
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn stop() {
|
pub async fn stop() {
|
||||||
let token = CompletionToken::default();
|
let token = CompletionToken::default();
|
||||||
emit!(Call(relay!(app:stop).with_any("token", token.clone())));
|
emit!(Call(relay!(app:stop).with_any("token", token.clone())));
|
||||||
|
|
@ -19,32 +21,6 @@ impl AppProxy {
|
||||||
token.future().await;
|
token.future().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notify(opt: NotifyOpt) {
|
|
||||||
emit!(Call(relay!(app:notify).with_any("opt", opt)));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_notify(dur: Duration) {
|
|
||||||
emit!(Call(relay!(app:update_notify, [dur.as_secs_f64()])));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn notify_warn(title: impl Into<String>, content: impl Into<String>) {
|
|
||||||
Self::notify(NotifyOpt {
|
|
||||||
title: title.into(),
|
|
||||||
content: content.into(),
|
|
||||||
level: NotifyLevel::Warn,
|
|
||||||
timeout: Duration::from_secs(5),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn notify_error(title: &str, content: impl ToString) {
|
|
||||||
Self::notify(NotifyOpt {
|
|
||||||
title: title.to_owned(),
|
|
||||||
content: content.to_string(),
|
|
||||||
level: NotifyLevel::Error,
|
|
||||||
timeout: Duration::from_secs(10),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn plugin(opt: PluginOpt) {
|
pub fn plugin(opt: PluginOpt) {
|
||||||
emit!(Call(relay!(app:plugin).with_any("opt", opt)));
|
emit!(Call(relay!(app:plugin).with_any("opt", opt)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
yazi_macro::mod_flat!(app cmp confirm input mgr pick semaphore tasks which);
|
yazi_macro::mod_flat!(app cmp confirm input mgr notify pick semaphore tasks which);
|
||||||
|
|
||||||
pub fn init() { crate::init_semaphore(); }
|
pub fn init() { crate::init_semaphore(); }
|
||||||
|
|
|
||||||
34
yazi-proxy/src/notify.rs
Normal file
34
yazi-proxy/src/notify.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use yazi_macro::{emit, relay};
|
||||||
|
use yazi_parser::notify::{PushLevel, PushOpt};
|
||||||
|
|
||||||
|
pub struct NotifyProxy;
|
||||||
|
|
||||||
|
impl NotifyProxy {
|
||||||
|
pub fn push(opt: PushOpt) {
|
||||||
|
emit!(Call(relay!(notify:push).with_any("opt", opt)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_warn(title: impl Into<String>, content: impl Into<String>) {
|
||||||
|
Self::push(PushOpt {
|
||||||
|
title: title.into(),
|
||||||
|
content: content.into(),
|
||||||
|
level: PushLevel::Warn,
|
||||||
|
timeout: Duration::from_secs(5),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_error(title: &str, content: impl ToString) {
|
||||||
|
Self::push(PushOpt {
|
||||||
|
title: title.to_owned(),
|
||||||
|
content: content.to_string(),
|
||||||
|
level: PushLevel::Error,
|
||||||
|
timeout: Duration::from_secs(10),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(dur: Duration) {
|
||||||
|
emit!(Call(relay!(notify:tick, [dur.as_secs_f64()])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use tokio::{io::{AsyncBufReadExt, BufReader}, select, sync::mpsc};
|
use tokio::{io::{AsyncBufReadExt, BufReader}, select, sync::mpsc};
|
||||||
use yazi_binding::Permit;
|
use yazi_binding::Permit;
|
||||||
use yazi_proxy::{AppProxy, HIDER};
|
use yazi_proxy::{AppProxy, HIDER, NotifyProxy};
|
||||||
|
|
||||||
use super::{ProcessInBg, ProcessInBlock, ProcessInOrphan, ShellOpt};
|
use super::{ProcessInBg, ProcessInBlock, ProcessInOrphan, ShellOpt};
|
||||||
use crate::{TaskOp, TaskOps, process::{ProcessOutBg, ProcessOutBlock, ProcessOutOrphan}};
|
use crate::{TaskOp, TaskOps, process::{ProcessOutBg, ProcessOutBlock, ProcessOutOrphan}};
|
||||||
|
|
@ -20,7 +20,7 @@ impl Process {
|
||||||
let (id, cmd) = (task.id, task.cmd.clone());
|
let (id, cmd) = (task.id, task.cmd.clone());
|
||||||
let result = super::shell(task.into()).await;
|
let result = super::shell(task.into()).await;
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
AppProxy::notify_warn(cmd.to_string_lossy(), format!("Failed to start process: {e}"));
|
NotifyProxy::push_warn(cmd.to_string_lossy(), format!("Failed to start process: {e}"));
|
||||||
return Ok(self.ops.out(id, ProcessOutBlock::Succ));
|
return Ok(self.ops.out(id, ProcessOutBlock::Succ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@ impl Process {
|
||||||
Some(code) => format!("Process exited with status code: {code}"),
|
Some(code) => format!("Process exited with status code: {code}"),
|
||||||
None => "Process terminated by signal".to_string(),
|
None => "Process terminated by signal".to_string(),
|
||||||
};
|
};
|
||||||
AppProxy::notify_warn(cmd.to_string_lossy(), content);
|
NotifyProxy::push_warn(cmd.to_string_lossy(), content);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self.ops.out(id, ProcessOutBlock::Succ))
|
Ok(self.ops.out(id, ProcessOutBlock::Succ))
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crossterm::event::{KeyEvent, MouseEvent};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use super::CmdCow;
|
use super::CmdCow;
|
||||||
use crate::{RoCell, strand::StrandBuf};
|
use crate::RoCell;
|
||||||
|
|
||||||
static TX: RoCell<mpsc::UnboundedSender<Event>> = RoCell::new();
|
static TX: RoCell<mpsc::UnboundedSender<Event>> = RoCell::new();
|
||||||
static RX: RoCell<mpsc::UnboundedReceiver<Event>> = RoCell::new();
|
static RX: RoCell<mpsc::UnboundedReceiver<Event>> = RoCell::new();
|
||||||
|
|
@ -11,20 +11,12 @@ static RX: RoCell<mpsc::UnboundedReceiver<Event>> = RoCell::new();
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Call(CmdCow),
|
Call(CmdCow),
|
||||||
Seq(Vec<CmdCow>),
|
Seq(Vec<CmdCow>),
|
||||||
Render,
|
Render(bool),
|
||||||
Key(KeyEvent),
|
Key(KeyEvent),
|
||||||
Mouse(MouseEvent),
|
Mouse(MouseEvent),
|
||||||
Resize,
|
Resize,
|
||||||
Focus,
|
Focus,
|
||||||
Paste(String),
|
Paste(String),
|
||||||
Quit(EventQuit),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct EventQuit {
|
|
||||||
pub code: i32,
|
|
||||||
pub no_cwd_file: bool,
|
|
||||||
pub selected: Option<StrandBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Event {
|
impl Event {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
yazi_macro::mod_flat!(cmd cow event);
|
yazi_macro::mod_flat!(cmd cow event);
|
||||||
|
|
||||||
pub static NEED_RENDER: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
|
pub static NEED_RENDER: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(0);
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ pub enum Layer {
|
||||||
Help,
|
Help,
|
||||||
Cmp,
|
Cmp,
|
||||||
Which,
|
Which,
|
||||||
|
Notify,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Layer {
|
impl Display for Layer {
|
||||||
|
|
@ -31,6 +32,7 @@ impl Display for Layer {
|
||||||
Self::Help => "help",
|
Self::Help => "help",
|
||||||
Self::Cmp => "cmp",
|
Self::Cmp => "cmp",
|
||||||
Self::Which => "which",
|
Self::Which => "which",
|
||||||
|
Self::Notify => "notify",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue