Added license and credits

This commit is contained in:
leviathan
2026-02-13 22:12:30 -05:00
parent 655f019469
commit 2ff9c29406
6 changed files with 154 additions and 2 deletions
+20
View File
@@ -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());
+15
View File
@@ -161,8 +161,10 @@ 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();
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);
}
_ => {}
},
}
}
}
+10
View File
@@ -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
View File
@@ -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(
+1
View File
@@ -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;
+91
View File
@@ -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);
}