mirror of
https://github.com/alexpasmantier/television.git
synced 2025-07-29 06:11:37 +00:00
fix: minor changes
This commit is contained in:
parent
4986dd00d3
commit
15cd53b76f
@ -17,3 +17,4 @@ ctrl-f12 = "actions:edit"
|
||||
[actions.edit]
|
||||
description = "Opens the selected entries with Neovim"
|
||||
command = "nvim {}"
|
||||
mode = "execute"
|
||||
|
@ -1,9 +1,8 @@
|
||||
use crate::{event::Key, screen::constants::ACTION_PREFIX};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::{OneOrMany, serde_as};
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::event::Key;
|
||||
|
||||
/// The different actions that can be performed by the application.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Hash, PartialOrd, Ord)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@ -423,8 +422,8 @@ impl<'de> serde::Deserialize<'de> for Action {
|
||||
"watch_timer" => Action::WatchTimer,
|
||||
"select_prev_history" => Action::SelectPrevHistory,
|
||||
"select_next_history" => Action::SelectNextHistory,
|
||||
s if s.starts_with("actions:") => {
|
||||
let action_name = &s[8..]; // Remove "actions:" prefix
|
||||
s if s.starts_with(ACTION_PREFIX) => {
|
||||
let action_name = s.trim_start_matches(ACTION_PREFIX);
|
||||
Action::ExternalAction(action_name.to_string())
|
||||
}
|
||||
_ => {
|
||||
|
@ -686,77 +686,47 @@ impl App {
|
||||
Action::ExternalAction(ref action_name) => {
|
||||
debug!("External action triggered: {}", action_name);
|
||||
|
||||
// Handle external action execution for selected entries
|
||||
let selected_entries = if !self
|
||||
.television
|
||||
.channel
|
||||
.selected_entries()
|
||||
.is_empty()
|
||||
if let Some(selected_entries) =
|
||||
self.television.get_selected_entries()
|
||||
{
|
||||
// Use multi-selected entries
|
||||
self.television.channel.selected_entries().clone()
|
||||
} else if let Some(current_entry) =
|
||||
self.television.get_selected_entry()
|
||||
{
|
||||
// Use single entry under cursor
|
||||
std::iter::once(current_entry).collect()
|
||||
} else {
|
||||
debug!("No entries available for external action");
|
||||
self.action_tx.send(Action::Error(
|
||||
"No entry available for external action"
|
||||
.to_string(),
|
||||
))?;
|
||||
return Ok(ActionOutcome::None);
|
||||
};
|
||||
|
||||
debug!(
|
||||
"Selected {} entries for external action",
|
||||
selected_entries.len()
|
||||
);
|
||||
|
||||
if let Some(action_spec) = self
|
||||
.television
|
||||
.channel_prototype
|
||||
.actions
|
||||
.get(action_name)
|
||||
{
|
||||
debug!("Found action spec for: {}", action_name);
|
||||
// Store the external action info and exit - the command will be executed after terminal cleanup
|
||||
self.should_quit = true;
|
||||
self.render_tx.send(RenderingTask::Quit)?;
|
||||
// Concatenate entry values with space separator, quoting items with whitespace
|
||||
let concatenated_entries: String =
|
||||
selected_entries
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
let raw = entry.raw.clone();
|
||||
if raw.chars().any(char::is_whitespace)
|
||||
{
|
||||
format!("'{}'", raw)
|
||||
} else {
|
||||
raw
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ");
|
||||
return Ok(ActionOutcome::ExternalAction(
|
||||
action_spec.clone(),
|
||||
concatenated_entries,
|
||||
));
|
||||
if let Some(action_spec) = self
|
||||
.television
|
||||
.channel_prototype
|
||||
.actions
|
||||
.get(action_name)
|
||||
{
|
||||
// Store the external action info and exit - the command will be executed after terminal cleanup
|
||||
self.should_quit = true;
|
||||
self.render_tx.send(RenderingTask::Quit)?;
|
||||
// Concatenate entry values with space separator, quoting items with whitespace
|
||||
let concatenated_entries: String =
|
||||
selected_entries
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
let raw = entry.raw.clone();
|
||||
if raw
|
||||
.chars()
|
||||
.any(char::is_whitespace)
|
||||
{
|
||||
format!("'{}'", raw)
|
||||
} else {
|
||||
raw
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ");
|
||||
return Ok(ActionOutcome::ExternalAction(
|
||||
action_spec.clone(),
|
||||
concatenated_entries,
|
||||
));
|
||||
}
|
||||
}
|
||||
error!("Unknown action: {}", action_name);
|
||||
// List available actions for debugging
|
||||
let available_actions: Vec<&String> = self
|
||||
.television
|
||||
.channel_prototype
|
||||
.actions
|
||||
.keys()
|
||||
.collect();
|
||||
debug!("Available actions: {:?}", available_actions);
|
||||
self.action_tx.send(Action::Error(format!(
|
||||
"Unknown action: {}",
|
||||
action_name
|
||||
)))?;
|
||||
debug!("No entries available for external action");
|
||||
self.action_tx.send(Action::Error(
|
||||
"No entry available for external action"
|
||||
.to_string(),
|
||||
))?;
|
||||
return Ok(ActionOutcome::None);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -165,11 +165,11 @@ impl CommandSpec {
|
||||
)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ExecutionMode {
|
||||
/// Fork the command as a child process (current behavior, tv stays open)
|
||||
/// Fork the command as a child process (tv stays open)
|
||||
#[default]
|
||||
Fork,
|
||||
/// Replace the current process with the command (tv exits, command takes over)
|
||||
Become,
|
||||
Execute,
|
||||
}
|
||||
|
||||
/// Output handling mode for external actions
|
||||
@ -178,12 +178,10 @@ pub enum ExecutionMode {
|
||||
)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum OutputMode {
|
||||
/// Inherit stdin/stdout/stderr (current behavior)
|
||||
#[default]
|
||||
Inherit,
|
||||
/// Capture output for processing
|
||||
Capture,
|
||||
/// Discard output silently
|
||||
#[default]
|
||||
Discard,
|
||||
}
|
||||
|
||||
@ -193,12 +191,13 @@ pub struct ActionSpec {
|
||||
pub description: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub command: CommandSpec,
|
||||
/// How to execute the command (fork vs become)
|
||||
/// How to execute the command
|
||||
#[serde(default)]
|
||||
pub r#become: bool,
|
||||
pub mode: ExecutionMode,
|
||||
/// How to handle command output
|
||||
#[serde(default)]
|
||||
pub output_mode: OutputMode,
|
||||
// TODO: add `requirements` (see `prototypes::BinaryRequirement`)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub const POINTER_SYMBOL: &str = "> ";
|
||||
pub const SELECTED_SYMBOL: &str = "● ";
|
||||
pub const DESELECTED_SYMBOL: &str = " ";
|
||||
|
||||
pub const LOGO_WIDTH: u16 = 24;
|
||||
pub const ACTION_PREFIX: &str = "actions:";
|
||||
|
@ -1,11 +1,12 @@
|
||||
use crate::{
|
||||
channels::prototypes::{ActionSpec, ExecutionMode, OutputMode},
|
||||
utils::shell::Shell,
|
||||
};
|
||||
use std::{collections::HashMap, process::Command};
|
||||
|
||||
#[cfg(not(unix))]
|
||||
use tracing::warn;
|
||||
|
||||
use super::shell::Shell;
|
||||
use crate::channels::prototypes::{ActionSpec, OutputMode};
|
||||
|
||||
pub fn shell_command<S>(
|
||||
command: &str,
|
||||
interactive: bool,
|
||||
@ -52,36 +53,33 @@ pub fn execute_action(
|
||||
&action_spec.command.env,
|
||||
);
|
||||
|
||||
// Configure stdio based on output mode (future extension point)
|
||||
match action_spec.output_mode {
|
||||
OutputMode::Inherit => {
|
||||
cmd.stdin(std::process::Stdio::inherit())
|
||||
.stdout(std::process::Stdio::inherit())
|
||||
.stderr(std::process::Stdio::inherit());
|
||||
// Execute based on execution mode
|
||||
match action_spec.mode {
|
||||
ExecutionMode::Execute => {
|
||||
// For Execute mode, let the new process inherit file descriptors naturally
|
||||
// TODO: use execve to replace current process
|
||||
let mut child = cmd.spawn()?;
|
||||
child.wait()
|
||||
}
|
||||
OutputMode::Capture => {
|
||||
// Future: capture output for processing
|
||||
cmd.stdin(std::process::Stdio::inherit())
|
||||
.stdout(std::process::Stdio::inherit())
|
||||
.stderr(std::process::Stdio::inherit());
|
||||
}
|
||||
OutputMode::Discard => {
|
||||
// Future: discard output silently
|
||||
cmd.stdin(std::process::Stdio::inherit())
|
||||
.stdout(std::process::Stdio::inherit())
|
||||
.stderr(std::process::Stdio::inherit());
|
||||
}
|
||||
}
|
||||
ExecutionMode::Fork => {
|
||||
// For Fork mode, configure stdio based on output mode
|
||||
match action_spec.output_mode {
|
||||
OutputMode::Capture => {
|
||||
// TODO: For now, inherit stdio (future: capture output for processing)
|
||||
cmd.stdin(std::process::Stdio::inherit())
|
||||
.stdout(std::process::Stdio::inherit())
|
||||
.stderr(std::process::Stdio::inherit());
|
||||
}
|
||||
OutputMode::Discard => {
|
||||
// Discard output silently
|
||||
cmd.stdin(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null());
|
||||
}
|
||||
}
|
||||
|
||||
// Execute based on become flag (future extension point)
|
||||
if action_spec.r#become {
|
||||
// Future: use execve to replace current process
|
||||
// For now, use normal execution
|
||||
let mut child = cmd.spawn()?;
|
||||
child.wait()
|
||||
} else {
|
||||
// Normal fork execution (current behavior)
|
||||
let mut child = cmd.spawn()?;
|
||||
child.wait()
|
||||
let mut child = cmd.spawn()?;
|
||||
child.wait()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user