mirror of
https://github.com/Astatin3/unshell-nodes-rs.git
synced 2026-06-08 16:18:08 -06:00
Move main project to new repository
This commit is contained in:
@@ -1,9 +0,0 @@
|
|||||||
# un-shell
|
|
||||||
### What is this?
|
|
||||||
- Currently, nothing. The idea behind this, is to unify many diffrent speratic cybersecurity tools and provide a simplistic way to access them.
|
|
||||||
|
|
||||||
### What should this do?
|
|
||||||
- For example, if you needed to pivot between computers, no need to mess with `iptables` or tor. You should simply just route traffic on the other end of a network.
|
|
||||||
- If you need to run NMAP on a remote server, you should be able to stream the binary over, while it is executing to limit AVs from detecting the binary on the disk.
|
|
||||||
- If you need to run a series of commands at a moment's notice, you should be able to do so in a flexible way.
|
|
||||||
- If you want to be pinged on your phone, the moment a server goes offline
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "sysintercept"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bincode = "2.0.1"
|
|
||||||
ctor = "0.5.0"
|
|
||||||
lazy_static = "1.5.0"
|
|
||||||
libc = "0.2.175"
|
|
||||||
serde_json = "1.0.142"
|
|
||||||
syscall-intercept = { path = "syscall-intercept-rs" }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "intercept"
|
|
||||||
crate-type = ["rlib", "cdylib"]
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
mod syscalls;
|
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use libc::exit;
|
|
||||||
use std::{
|
|
||||||
cell::{Cell, RefCell},
|
|
||||||
io::{BufWriter, Write},
|
|
||||||
net::TcpStream,
|
|
||||||
sync::Mutex,
|
|
||||||
};
|
|
||||||
use syscall_intercept::*;
|
|
||||||
|
|
||||||
static mut CLIENT: Option<Mutex<BufWriter<TcpStream>>> = None;
|
|
||||||
|
|
||||||
#[ctor::ctor]
|
|
||||||
fn start() {
|
|
||||||
unsafe {
|
|
||||||
CLIENT = Some({
|
|
||||||
match TcpStream::connect("127.0.0.1:1234") {
|
|
||||||
Ok(stream) => {
|
|
||||||
let writer = BufWriter::new(stream);
|
|
||||||
Mutex::new(writer)
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Failed to connect to server: {}", e);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe { set_hook_fn(hook) };
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn print_direct(str: &str)
|
|
||||||
|
|
||||||
thread_local! {
|
|
||||||
/// A flag indicating whether the current thread is in an intercept context.
|
|
||||||
static INTERCEPTED: Cell<bool> = Cell::new(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// lazy_static! {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
struct InterceptGuard;
|
|
||||||
|
|
||||||
impl InterceptGuard {
|
|
||||||
fn try_lock() -> Option<Self> {
|
|
||||||
INTERCEPTED.with(|x| {
|
|
||||||
if x.get() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
x.set(true);
|
|
||||||
Some(InterceptGuard)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for InterceptGuard {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
INTERCEPTED.with(|x| x.set(false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn hook(
|
|
||||||
syscall_num: isize,
|
|
||||||
arg0: isize,
|
|
||||||
arg1: isize,
|
|
||||||
arg2: isize,
|
|
||||||
arg3: isize,
|
|
||||||
arg4: isize,
|
|
||||||
arg5: isize,
|
|
||||||
result: &mut isize,
|
|
||||||
) -> InterceptResult {
|
|
||||||
// detect and avoid recursive interception
|
|
||||||
let _guard = match InterceptGuard::try_lock() {
|
|
||||||
Some(g) => g,
|
|
||||||
None => return InterceptResult::Forward,
|
|
||||||
};
|
|
||||||
|
|
||||||
// unsafe {
|
|
||||||
// unset_hook_fn();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Return if is print
|
|
||||||
// if syscall_num == libc::SYS_write as _ && arg0 == 1 {
|
|
||||||
// return InterceptResult::Forward;
|
|
||||||
// }
|
|
||||||
|
|
||||||
let args = [arg0, arg1, arg2, arg3, arg4, arg5];
|
|
||||||
let desc = syscalls::get_syscall_desc(syscall_num, args);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
#[allow(static_mut_refs)]
|
|
||||||
if let Some(client) = CLIENT.as_ref() {
|
|
||||||
client
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.write_all(&format!("Data: {:?}\n", desc).as_bytes())
|
|
||||||
.unwrap();
|
|
||||||
client.lock().unwrap().flush().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsafe {
|
|
||||||
// set_hook_fn(hook);
|
|
||||||
// }
|
|
||||||
|
|
||||||
InterceptResult::Forward
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "tui-tool"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
color-eyre = "0.6.5"
|
|
||||||
crossterm = "0.29.0"
|
|
||||||
ratatui = "0.29.0"
|
|
||||||
strum = { version = "0.27.2", features = ["derive"] }
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
use color_eyre::Result;
|
|
||||||
use ratatui::{
|
|
||||||
DefaultTerminal, Frame,
|
|
||||||
buffer::Buffer,
|
|
||||||
crossterm::event::{self, Event, KeyCode, KeyEventKind},
|
|
||||||
layout::{Constraint, Layout, Rect},
|
|
||||||
style::{Color, Stylize, palette::tailwind},
|
|
||||||
symbols,
|
|
||||||
text::Line,
|
|
||||||
widgets::{Block, Padding, Paragraph, Tabs, Widget},
|
|
||||||
};
|
|
||||||
use strum::IntoEnumIterator;
|
|
||||||
|
|
||||||
use crate::tabs::{Tab, TabController};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct App {
|
|
||||||
state: AppState,
|
|
||||||
tab_controller: TabController,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy, PartialEq, Eq)]
|
|
||||||
enum AppState {
|
|
||||||
#[default]
|
|
||||||
Running,
|
|
||||||
Quitting,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
|
|
||||||
while self.state == AppState::Running {
|
|
||||||
terminal.draw(|frame| self.render(frame))?;
|
|
||||||
self.handle_events()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_events(&mut self) -> std::io::Result<()> {
|
|
||||||
if let Event::Key(key) = event::read()? {
|
|
||||||
if key.kind == KeyEventKind::Press {
|
|
||||||
if !self.tab_controller.key(key.code) {
|
|
||||||
match key.code {
|
|
||||||
KeyCode::Char('l') | KeyCode::Right => self.tab_controller.next(),
|
|
||||||
KeyCode::Char('h') | KeyCode::Left => self.tab_controller.previous(),
|
|
||||||
KeyCode::Char('q') | KeyCode::Esc => self.quit(),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn quit(&mut self) {
|
|
||||||
self.state = AppState::Quitting;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
fn render(&mut self, frame: &mut Frame) {
|
|
||||||
let area = frame.area();
|
|
||||||
|
|
||||||
use Constraint::{Length, Min};
|
|
||||||
let vertical = Layout::vertical([Length(1), Min(0), Length(1)]);
|
|
||||||
let [header_area, inner_area, footer_area] = vertical.areas(area);
|
|
||||||
|
|
||||||
let horizontal = Layout::horizontal([Min(0), Length(20)]);
|
|
||||||
let [tabs_area, title_area] = horizontal.areas(header_area);
|
|
||||||
|
|
||||||
render_title(title_area, frame);
|
|
||||||
self.tab_controller.render_tab_titles(tabs_area, frame);
|
|
||||||
self.tab_controller.render(inner_area, frame);
|
|
||||||
render_footer(footer_area, frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_title(area: Rect, frame: &mut Frame) {
|
|
||||||
frame.render_widget("Ratatui Tabs Example".bold(), area);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_footer(area: Rect, frame: &mut Frame) {
|
|
||||||
frame.render_widget(
|
|
||||||
Line::raw("◄ ► to change tab | Press q to quit").centered(),
|
|
||||||
area,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
mod app;
|
|
||||||
mod tabs;
|
|
||||||
|
|
||||||
use color_eyre::Result;
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
color_eyre::install()?;
|
|
||||||
let terminal = ratatui::init();
|
|
||||||
let app_result = app::App::default().run(terminal);
|
|
||||||
ratatui::restore();
|
|
||||||
app_result
|
|
||||||
}
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
use ratatui::{
|
|
||||||
Frame,
|
|
||||||
crossterm::event::KeyCode,
|
|
||||||
layout::{Constraint, Layout, Margin, Rect},
|
|
||||||
style::{Modifier, Style, palette::tailwind},
|
|
||||||
text::Text,
|
|
||||||
widgets::{
|
|
||||||
Block, BorderType, Cell, HighlightSpacing, Paragraph, Row, Scrollbar, ScrollbarOrientation,
|
|
||||||
ScrollbarState, Table, TableState,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
pub const fn color() -> tailwind::Palette {
|
|
||||||
tailwind::BLUE
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct EnvVar {
|
|
||||||
name: String,
|
|
||||||
data: String,
|
|
||||||
// email: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EnvVar {
|
|
||||||
const fn ref_array(&self) -> [&String; 2] {
|
|
||||||
[&self.name, &self.data]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct State {
|
|
||||||
state: TableState,
|
|
||||||
items: Vec<EnvVar>,
|
|
||||||
longest_item_name: u16,
|
|
||||||
scroll_state: ScrollbarState,
|
|
||||||
color_index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
// const ITEM_HEIGHT: usize = 2;
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
pub fn render(&mut self, area: Rect, frame: &mut Frame) {
|
|
||||||
let vertical = &Layout::vertical([Constraint::Min(5), Constraint::Length(4)]);
|
|
||||||
let rects = vertical.split(area);
|
|
||||||
|
|
||||||
// self.set_colors();
|
|
||||||
|
|
||||||
self.render_table(frame, rects[0]);
|
|
||||||
self.render_scrollbar(frame, rects[0]);
|
|
||||||
// self.render_footer(frame, rects[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_table(&mut self, frame: &mut Frame, area: Rect) {
|
|
||||||
// let header_style = Style::default()
|
|
||||||
// .fg(tailwind::SLATE.c200)
|
|
||||||
// .bg(tailwind::SLATE.c900);
|
|
||||||
// let selected_row_style = Style::default()
|
|
||||||
// .add_modifier(Modifier::REVERSED)
|
|
||||||
// .fg(tailwind::SLATE.c400);
|
|
||||||
// let selected_col_style = Style::default().fg(tailwind::SLATE.c400);
|
|
||||||
// let selected_cell_style = Style::default()
|
|
||||||
// .add_modifier(Modifier::REVERSED)
|
|
||||||
// .fg(tailwind::SLATE.c600);
|
|
||||||
|
|
||||||
let header = ["Name", "Address", "Email"]
|
|
||||||
.into_iter()
|
|
||||||
.map(Cell::from)
|
|
||||||
.collect::<Row>()
|
|
||||||
// .style(header_style)
|
|
||||||
.height(1);
|
|
||||||
|
|
||||||
let rows = self.items.iter().enumerate().map(|(i, data)| {
|
|
||||||
let color = match i % 2 {
|
|
||||||
0 => tailwind::SLATE.c950,
|
|
||||||
_ => tailwind::SLATE.c900,
|
|
||||||
};
|
|
||||||
let item = data.ref_array();
|
|
||||||
item.into_iter()
|
|
||||||
.map(|content| Cell::from(Text::from(format!("{content}"))))
|
|
||||||
.collect::<Row>()
|
|
||||||
.style(Style::new().fg(tailwind::SLATE.c200).bg(color))
|
|
||||||
// .height(ITEM_HEIGHT as u16)
|
|
||||||
});
|
|
||||||
let t = Table::new(
|
|
||||||
rows,
|
|
||||||
[
|
|
||||||
Constraint::Length(self.longest_item_name),
|
|
||||||
Constraint::Min(10),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.header(header)
|
|
||||||
.highlight_symbol(Text::from(" █ "))
|
|
||||||
.highlight_spacing(HighlightSpacing::Always);
|
|
||||||
// .set_w
|
|
||||||
frame.render_stateful_widget(t, area, &mut self.state);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_scrollbar(&mut self, frame: &mut Frame, area: Rect) {
|
|
||||||
frame.render_stateful_widget(
|
|
||||||
Scrollbar::default()
|
|
||||||
.orientation(ScrollbarOrientation::VerticalRight)
|
|
||||||
.begin_symbol(None)
|
|
||||||
.end_symbol(None),
|
|
||||||
area,
|
|
||||||
&mut self.scroll_state,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn render_footer(&self, frame: &mut Frame, area: Rect) {
|
|
||||||
// let info_footer = Paragraph::new(Text::from_iter(INFO_TEXT))
|
|
||||||
// // .style(
|
|
||||||
// // Style::new()
|
|
||||||
// // .fg(self.colors.row_fg)
|
|
||||||
// // .bg(self.colors.buffer_bg),
|
|
||||||
// // )
|
|
||||||
// .centered()
|
|
||||||
// .block(
|
|
||||||
// Block::bordered()
|
|
||||||
// .border_type(BorderType::Double)
|
|
||||||
// .border_style(Style::new().fg(self.colors.footer_border_color)),
|
|
||||||
// );
|
|
||||||
// frame.render_widget(info_footer, area);
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn key(&mut self, keycode: KeyCode) -> bool {
|
|
||||||
match keycode {
|
|
||||||
KeyCode::Char('j') | KeyCode::Down => {
|
|
||||||
self.next_row();
|
|
||||||
false
|
|
||||||
}
|
|
||||||
KeyCode::Char('k') | KeyCode::Up => {
|
|
||||||
self.previous_row();
|
|
||||||
false
|
|
||||||
}
|
|
||||||
// KeyCode::Char('l') | KeyCode::Right if shift_pressed => self.next_color(),
|
|
||||||
// KeyCode::Char('h') | KeyCode::Left if shift_pressed => {
|
|
||||||
// self.previous_color();
|
|
||||||
// }
|
|
||||||
// KeyCode::Char('l') | KeyCode::Right => {
|
|
||||||
// self.next_column();
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
// KeyCode::Char('h') | KeyCode::Left => {
|
|
||||||
// self.previous_column();
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
pub fn next_row(&mut self) {
|
|
||||||
let i = match self.state.selected() {
|
|
||||||
Some(i) => {
|
|
||||||
if i >= self.items.len() - 1 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
i + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => 0,
|
|
||||||
};
|
|
||||||
self.state.select(Some(i));
|
|
||||||
self.scroll_state = self.scroll_state.position(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn previous_row(&mut self) {
|
|
||||||
let i = match self.state.selected() {
|
|
||||||
Some(i) => {
|
|
||||||
if i == 0 {
|
|
||||||
self.items.len() - 1
|
|
||||||
} else {
|
|
||||||
i - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => 0,
|
|
||||||
};
|
|
||||||
self.state.select(Some(i));
|
|
||||||
self.scroll_state = self.scroll_state.position(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_column(&mut self) {
|
|
||||||
self.state.select_next_column();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn previous_column(&mut self) {
|
|
||||||
self.state.select_previous_column();
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn next_color(&mut self) {
|
|
||||||
// self.color_index = (self.color_index + 1) % PALETTES.len();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn previous_color(&mut self) {
|
|
||||||
// let count = PALETTES.len();
|
|
||||||
// self.color_index = (self.color_index + count - 1) % count;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn set_colors(&mut self) {
|
|
||||||
// self.colors = TableColors::new(&PALETTES[self.color_index]);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for State {
|
|
||||||
fn default() -> Self {
|
|
||||||
let data_vec: Vec<EnvVar> = std::env::vars()
|
|
||||||
.map(|(k, v)| EnvVar { name: k, data: v })
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
state: TableState::default().with_selected(0),
|
|
||||||
longest_item_name: data_vec.iter().map(|var| var.name.len()).max().unwrap_or(0) as u16,
|
|
||||||
scroll_state: ScrollbarState::new((data_vec.len() - 1)),
|
|
||||||
// colors: TableColors::new(&PALETTES[0]),
|
|
||||||
color_index: 0,
|
|
||||||
items: data_vec,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn constraint_len_calculator(items: &[Data]) -> (u16, u16, u16) {
|
|
||||||
// let name_len = items
|
|
||||||
// .iter()
|
|
||||||
// .map(Data::name)
|
|
||||||
// .map(UnicodeWidthStr::width)
|
|
||||||
// .max()
|
|
||||||
// .unwrap_or(0);
|
|
||||||
// let address_len = items
|
|
||||||
// .iter()
|
|
||||||
// .map(Data::address)
|
|
||||||
// .flat_map(str::lines)
|
|
||||||
// .map(UnicodeWidthStr::width)
|
|
||||||
// .max()
|
|
||||||
// .unwrap_or(0);
|
|
||||||
// let email_len = items
|
|
||||||
// .iter()
|
|
||||||
// .map(Data::email)
|
|
||||||
// .map(UnicodeWidthStr::width)
|
|
||||||
// .max()
|
|
||||||
// .unwrap_or(0);
|
|
||||||
|
|
||||||
// #[allow(clippy::cast_possible_truncation)]
|
|
||||||
// (name_len as u16, address_len as u16, email_len as u16)
|
|
||||||
// }
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
mod env_vars;
|
|
||||||
mod tab1;
|
|
||||||
mod tab2;
|
|
||||||
mod tab3;
|
|
||||||
|
|
||||||
use ratatui::{
|
|
||||||
Frame,
|
|
||||||
crossterm::event::KeyCode,
|
|
||||||
layout::Rect,
|
|
||||||
style::{Color, Stylize, palette::tailwind},
|
|
||||||
symbols,
|
|
||||||
text::Line,
|
|
||||||
widgets::{Block, Borders, Padding, Tabs},
|
|
||||||
};
|
|
||||||
use strum::{Display, EnumIter, FromRepr, IntoEnumIterator};
|
|
||||||
|
|
||||||
pub struct TabController {
|
|
||||||
current_index: usize,
|
|
||||||
tabs: Vec<Tab>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TabController {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
current_index: 0,
|
|
||||||
tabs: vec![Tab::Env(env_vars::State::default()), Tab::Tab2, Tab::Tab3],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TabController {
|
|
||||||
pub fn previous(&mut self) {
|
|
||||||
if self.current_index > 0 {
|
|
||||||
self.current_index -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next(&mut self) {
|
|
||||||
if self.current_index < self.tabs.len() - 1 {
|
|
||||||
self.current_index += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tab(&mut self) -> &mut Tab {
|
|
||||||
&mut self.tabs[self.current_index]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_tab_titles(&mut self, area: Rect, frame: &mut Frame) {
|
|
||||||
let titles = Tab::iter().map(Tab::title);
|
|
||||||
let highlight_style = (Color::default(), self.tab().color().c700);
|
|
||||||
|
|
||||||
frame.render_widget(
|
|
||||||
Tabs::new(titles)
|
|
||||||
.highlight_style(highlight_style)
|
|
||||||
.select(self.current_index)
|
|
||||||
.padding("", "")
|
|
||||||
.divider(" "),
|
|
||||||
area,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(&mut self, area: Rect, frame: &mut Frame) {
|
|
||||||
self.tab().render(area, frame)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn key(&mut self, keycode: KeyCode) -> bool {
|
|
||||||
self.tab().key(keycode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Display, FromRepr, EnumIter)]
|
|
||||||
pub enum Tab {
|
|
||||||
#[strum(to_string = "Env")]
|
|
||||||
Env(env_vars::State),
|
|
||||||
#[strum(to_string = "Tab 2")]
|
|
||||||
Tab2,
|
|
||||||
#[strum(to_string = "Tab 3")]
|
|
||||||
Tab3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Tab {
|
|
||||||
fn default() -> Self {
|
|
||||||
return Self::Env(env_vars::State::default());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tab {
|
|
||||||
/// Return tab's name as a styled `Line`
|
|
||||||
pub fn title(self) -> Line<'static> {
|
|
||||||
format!(" {self} ")
|
|
||||||
.fg(tailwind::SLATE.c200)
|
|
||||||
.bg(self.color().c900)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A block surrounding the tab's content
|
|
||||||
fn block(&self) -> Block<'static> {
|
|
||||||
Block::bordered()
|
|
||||||
.border_set(symbols::border::PROPORTIONAL_TALL)
|
|
||||||
.padding(Padding::horizontal(1))
|
|
||||||
.border_style(self.color().c700)
|
|
||||||
.borders(Borders::TOP)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn color(&self) -> tailwind::Palette {
|
|
||||||
match self {
|
|
||||||
Self::Env(_) => env_vars::color(),
|
|
||||||
Self::Tab2 => tab2::color(),
|
|
||||||
Self::Tab3 => tab3::color(),
|
|
||||||
// _ => tab3::color(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(&mut self, area: Rect, frame: &mut Frame) {
|
|
||||||
let block = self.block();
|
|
||||||
let inner_area = block.inner(area);
|
|
||||||
|
|
||||||
frame.render_widget(block, area);
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::Env(state) => state.render(inner_area, frame),
|
|
||||||
Self::Tab2 => tab2::render(inner_area, frame),
|
|
||||||
Self::Tab3 => tab3::render(inner_area, frame),
|
|
||||||
// _ => tab3::render(inner_area, frame),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn key(&mut self, keycode: KeyCode) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Env(state) => state.key(keycode),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
use ratatui::{Frame, layout::Rect, style::palette::tailwind, widgets::Paragraph};
|
|
||||||
|
|
||||||
pub const fn color() -> tailwind::Palette {
|
|
||||||
tailwind::BLUE
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(area: Rect, frame: &mut Frame) {
|
|
||||||
frame.render_widget(Paragraph::new("Hello, World!"), area);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
use ratatui::{Frame, layout::Rect, style::palette::tailwind, widgets::Paragraph};
|
|
||||||
|
|
||||||
pub const fn color() -> tailwind::Palette {
|
|
||||||
tailwind::EMERALD
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(area: Rect, frame: &mut Frame) {
|
|
||||||
frame.render_widget(Paragraph::new("Hello, World!"), area);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
use ratatui::{Frame, layout::Rect, style::palette::tailwind, widgets::Paragraph};
|
|
||||||
|
|
||||||
pub const fn color() -> tailwind::Palette {
|
|
||||||
tailwind::INDIGO
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(area: Rect, frame: &mut Frame) {
|
|
||||||
frame.render_widget(Paragraph::new("Hello, World!"), area);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user