Added license and credits
This commit is contained in:
+20
@@ -35,6 +35,10 @@ pub enum InputMode {
|
||||
FobMetaRegion,
|
||||
/// Fob export metadata: editing notes field
|
||||
FobMetaNotes,
|
||||
/// License overlay (centered box)
|
||||
License,
|
||||
/// Credits overlay (centered box)
|
||||
Credits,
|
||||
}
|
||||
|
||||
/// Export format being used
|
||||
@@ -165,6 +169,9 @@ pub enum RadioEvent {
|
||||
StateChanged(RadioState),
|
||||
}
|
||||
|
||||
/// License text (embedded at compile time)
|
||||
pub const LICENSE_TEXT: &str = include_str!("../LICENSE");
|
||||
|
||||
/// Main application state
|
||||
pub struct App {
|
||||
/// Current input mode
|
||||
@@ -196,6 +203,10 @@ pub struct App {
|
||||
/// Currently selected signal menu item index
|
||||
pub signal_menu_index: usize,
|
||||
|
||||
// -- License/Credits overlay --
|
||||
/// Scroll offset for license/credits overlay (lines)
|
||||
pub overlay_scroll: usize,
|
||||
|
||||
// -- Settings menu state --
|
||||
/// Currently selected settings field
|
||||
pub settings_field_index: usize,
|
||||
@@ -312,6 +323,7 @@ impl App {
|
||||
last_error: None,
|
||||
status_message: None,
|
||||
signal_menu_index: 0,
|
||||
overlay_scroll: 0,
|
||||
settings_field_index: 0,
|
||||
settings_value_index: 0,
|
||||
next_capture_id,
|
||||
@@ -441,6 +453,14 @@ impl App {
|
||||
"lock" => self.transmit_command(parts.get(1), ButtonCommand::Lock)?,
|
||||
"trunk" => self.transmit_command(parts.get(1), ButtonCommand::Trunk)?,
|
||||
"panic" => self.transmit_command(parts.get(1), ButtonCommand::Panic)?,
|
||||
"license" | "licence" => {
|
||||
self.input_mode = InputMode::License;
|
||||
self.overlay_scroll = 0;
|
||||
}
|
||||
"credits" => {
|
||||
self.input_mode = InputMode::Credits;
|
||||
self.overlay_scroll = 0;
|
||||
}
|
||||
"delete" => {
|
||||
if parts.len() < 2 {
|
||||
self.last_error = Some("Usage: :delete <ID> or :delete all".to_string());
|
||||
|
||||
+16
-1
@@ -161,7 +161,9 @@ fn run_app<B: ratatui::backend::Backend>(terminal: &mut Terminal<B>, app: &mut A
|
||||
let command = app.command_input.clone();
|
||||
app.execute_command(&command)?;
|
||||
app.command_input.clear();
|
||||
app.input_mode = InputMode::Normal;
|
||||
if app.input_mode == InputMode::Command {
|
||||
app.input_mode = InputMode::Normal;
|
||||
}
|
||||
}
|
||||
KeyCode::Char(c) => {
|
||||
app.command_input.push(c);
|
||||
@@ -392,6 +394,19 @@ fn run_app<B: ratatui::backend::Backend>(terminal: &mut Terminal<B>, app: &mut A
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
||||
InputMode::License | InputMode::Credits => match key.code {
|
||||
KeyCode::Esc | KeyCode::Enter => {
|
||||
app.input_mode = InputMode::Normal;
|
||||
}
|
||||
KeyCode::Up | KeyCode::Char('k') => {
|
||||
app.overlay_scroll = app.overlay_scroll.saturating_sub(1);
|
||||
}
|
||||
KeyCode::Down | KeyCode::Char('j') => {
|
||||
app.overlay_scroll = app.overlay_scroll.saturating_add(1);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,16 @@ pub fn render_command_line(frame: &mut Frame, area: Rect, app: &App) {
|
||||
"EXPORT",
|
||||
Style::default().fg(Color::Green),
|
||||
),
|
||||
InputMode::License => (
|
||||
String::new(),
|
||||
"LICENSE",
|
||||
Style::default().fg(Color::Cyan),
|
||||
),
|
||||
InputMode::Credits => (
|
||||
String::new(),
|
||||
"CREDITS",
|
||||
Style::default().fg(Color::Cyan),
|
||||
),
|
||||
};
|
||||
|
||||
let input_line = Line::from(vec![
|
||||
|
||||
+16
-1
@@ -8,13 +8,14 @@ use ratatui::{
|
||||
Frame,
|
||||
};
|
||||
|
||||
use crate::app::{App, InputMode, RadioState};
|
||||
use crate::app::{App, InputMode, RadioState, LICENSE_TEXT};
|
||||
|
||||
use super::captures_list::render_captures_list;
|
||||
use super::command::render_command_line;
|
||||
use super::settings_menu::{render_settings_dropdown, render_settings_tabs};
|
||||
use super::signal_menu::render_signal_menu;
|
||||
use super::status_bar::render_status_bar;
|
||||
use super::text_overlay::render_text_overlay;
|
||||
|
||||
use crate::app::InputMode as IM;
|
||||
|
||||
@@ -91,6 +92,19 @@ pub fn draw_ui(frame: &mut Frame, app: &App) {
|
||||
) {
|
||||
render_export_form(frame, app);
|
||||
}
|
||||
|
||||
if app.input_mode == InputMode::License {
|
||||
render_text_overlay(frame, app, "License", LICENSE_TEXT, Alignment::Left);
|
||||
}
|
||||
if app.input_mode == InputMode::Credits {
|
||||
render_text_overlay(
|
||||
frame,
|
||||
app,
|
||||
"Credits",
|
||||
super::text_overlay::CREDITS_TEXT,
|
||||
Alignment::Center,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Render the header with title and radio status
|
||||
@@ -159,6 +173,7 @@ fn render_help_bar(frame: &mut Frame, area: Rect, app: &App) {
|
||||
| InputMode::FobMetaModel
|
||||
| InputMode::FobMetaRegion => "Enter: Next Field | Esc: Cancel Export",
|
||||
InputMode::FobMetaNotes => "Enter: Save & Export | Esc: Cancel Export",
|
||||
InputMode::License | InputMode::Credits => "Esc/Enter: Close | Up/Down: Scroll",
|
||||
};
|
||||
|
||||
let help = Paragraph::new(Line::from(Span::styled(
|
||||
|
||||
@@ -4,6 +4,7 @@ mod captures_list;
|
||||
mod command;
|
||||
mod layout;
|
||||
pub mod signal_menu;
|
||||
mod text_overlay;
|
||||
pub mod settings_menu;
|
||||
mod status_bar;
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
//! Centered text overlay for License and Credits.
|
||||
|
||||
use ratatui::{
|
||||
layout::{Alignment, Rect},
|
||||
style::{Color, Style},
|
||||
text::Line,
|
||||
widgets::{Block, Borders, Clear, Paragraph, Wrap},
|
||||
Frame,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
|
||||
/// Credits text for the :credits command
|
||||
pub const CREDITS_TEXT: &str = r#"=============== CREDITS ==============
|
||||
|
||||
Keyfob Analysis tool is developed by Kara Zajac (.leviathan) and would not be possible without ProtoPirate. I am truely standing on the shoulders of giants when I developed this application.
|
||||
|
||||
ProtoPirate Development Team
|
||||
----
|
||||
RocketGod
|
||||
MMX
|
||||
Leeroy
|
||||
gullradriel
|
||||
Skorp - Thanks, I sneaked a lot from Weather App!
|
||||
Vadim's Radio Driver
|
||||
|
||||
Protocol Magic
|
||||
----
|
||||
L0rdDiakon
|
||||
YougZ
|
||||
RocketGod
|
||||
MMX
|
||||
DoobTheGoober
|
||||
Skorp
|
||||
Slackware
|
||||
Trikk
|
||||
Wootini
|
||||
Li0ard
|
||||
Leeroy
|
||||
|
||||
Reverse Engineering Support
|
||||
----
|
||||
DoobTheGoober
|
||||
MMX
|
||||
NeedNotApply
|
||||
RocketGod
|
||||
Slackware
|
||||
Trikk
|
||||
Li0ard"#;
|
||||
|
||||
/// Render a centered overlay with a title and scrollable text (e.g. License or Credits).
|
||||
pub fn render_text_overlay(
|
||||
frame: &mut Frame,
|
||||
app: &App,
|
||||
title: &str,
|
||||
text: &str,
|
||||
alignment: Alignment,
|
||||
) {
|
||||
let area = frame.area();
|
||||
|
||||
// Use most of the terminal: 80% width, 85% height, centered
|
||||
let width = (area.width as f32 * 0.80) as u16;
|
||||
let height = (area.height as f32 * 0.85) as u16;
|
||||
let x = area.width.saturating_sub(width) / 2;
|
||||
let y = area.height.saturating_sub(height) / 2;
|
||||
let overlay_area = Rect::new(x, y, width.min(area.width), height.min(area.height));
|
||||
|
||||
frame.render_widget(Clear, overlay_area);
|
||||
|
||||
let total_lines = text.lines().count();
|
||||
let inner_height = overlay_area.height.saturating_sub(2) as usize; // minus border
|
||||
let scroll_max = total_lines.saturating_sub(inner_height).saturating_sub(1);
|
||||
let scroll = app.overlay_scroll.min(scroll_max);
|
||||
|
||||
let paragraph = Paragraph::new(
|
||||
text.lines()
|
||||
.map(Line::from)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title(format!(" {} ", title))
|
||||
.border_style(Style::default().fg(Color::Cyan)),
|
||||
)
|
||||
.wrap(Wrap { trim: true })
|
||||
.scroll((scroll as u16, 0))
|
||||
.alignment(alignment);
|
||||
|
||||
frame.render_widget(paragraph, overlay_area);
|
||||
}
|
||||
Reference in New Issue
Block a user