use std::io::Write;
const DOT_MAP: [[u8; 4]; 2] = [
[0x01, 0x02, 0x04, 0x40], [0x08, 0x10, 0x20, 0x80], ];
const KEYHOLE_COLS: usize = 20;
const KEYHOLE_GRID: &[&str] = &[
" ######## ", " ############ ", " ############## ", " ################ ", " ################ ", " #### #### #### ", " #### #### ", " #### #### ", " #### #### #### ", " ################ ", " ################ ", " ############## ", " #### #### ", " #### #### ", " ######## ", " ######## ", " ###### ", " ###### ", " ###### ", " ###### ", " ###### ", " ###### ", " ######## ", " ######## ", ];
const BRAND_LINE: &str = "K E Y H O G";
const RULE_LINE: &str = "───────────";
const GRADIENT: &[(u8, u8, u8)] = &[
(180, 83, 9), (245, 158, 11), (251, 191, 36), (253, 224, 71), (254, 240, 138), ];
fn lerp_color(a: (u8, u8, u8), b: (u8, u8, u8), t: f32) -> (u8, u8, u8) {
let r = a.0 as f32 + (b.0 as f32 - a.0 as f32) * t;
let g = a.1 as f32 + (b.1 as f32 - a.1 as f32) * t;
let bv = a.2 as f32 + (b.2 as f32 - a.2 as f32) * t;
(r as u8, g as u8, bv as u8)
}
fn sample_gradient(t: f32) -> (u8, u8, u8) {
let t = t.clamp(0.0, 1.0);
let segments = GRADIENT.len() - 1;
let scaled = t * segments as f32;
let idx = (scaled as usize).min(segments - 1);
let local_t = scaled - idx as f32;
lerp_color(GRADIENT[idx], GRADIENT[idx + 1], local_t)
}
fn pack_braille() -> Vec<Vec<(char, f32)>> {
let grid: Vec<Vec<bool>> = KEYHOLE_GRID
.iter()
.map(|row| row.chars().map(|c| c == '#').collect())
.collect();
let cell_rows = grid.len().div_ceil(4); let cell_cols = KEYHOLE_COLS / 2;
let mut result = Vec::with_capacity(cell_rows);
for cy in 0..cell_rows {
let mut row = Vec::with_capacity(cell_cols);
for cx in 0..cell_cols {
let mut bits: u8 = 0;
for (dx, col) in DOT_MAP.iter().enumerate() {
for (dy, &dot) in col.iter().enumerate() {
let gy = cy * 4 + dy;
let gx = cx * 2 + dx;
if gy < grid.len() && gx < grid[gy].len() && grid[gy][gx] {
bits |= dot;
}
}
}
let ch = char::from_u32(0x2800 + u32::from(bits)).unwrap_or('⠀');
let t = cx as f32 / cell_cols.max(1) as f32;
row.push((ch, t));
}
result.push(row);
}
result
}
fn supports_true_color() -> bool {
if let Ok(ct) = std::env::var("COLORTERM") {
return ct == "truecolor" || ct == "24bit";
}
if let Ok(term) = std::env::var("TERM") {
return term.contains("256color") || term.contains("24bit");
}
false
}
pub fn print_banner<W: Write>(
w: &mut W,
color: bool,
animate: bool,
detector_count: usize,
) -> std::io::Result<()> {
let true_color = color && supports_true_color();
let braille_rows = pack_braille();
writeln!(w)?;
for (row_idx, row) in braille_rows.iter().enumerate() {
write!(w, " ")?;
for &(ch, t) in row {
if color && ch != '\u{2800}' {
let vert_t = row_idx as f32 / braille_rows.len().max(1) as f32;
let blended_t = t * 0.6 + vert_t * 0.4;
let (r, g, b) = sample_gradient(blended_t);
if true_color {
write!(w, "\x1b[38;2;{r};{g};{b}m{ch}\x1b[0m")?;
} else {
let idx = 208 + ((blended_t * 15.0) as u8).min(15);
write!(w, "\x1b[38;5;{idx}m{ch}\x1b[0m")?;
}
} else {
write!(w, "{ch}")?;
}
}
writeln!(w)?;
if animate {
w.flush()?;
let delay_us = 30_000 + (row_idx as u64 * 5_000);
std::thread::sleep(std::time::Duration::from_micros(delay_us.min(80_000)));
}
}
writeln!(w)?;
if color {
let brand_chars: Vec<char> = BRAND_LINE.chars().collect();
let width = brand_chars.len().max(1);
write!(w, " ")?;
for (i, ch) in brand_chars.iter().enumerate() {
if *ch != ' ' {
let t = i as f32 / width as f32;
let (r, g, b) = sample_gradient(t);
if true_color {
write!(w, "\x1b[38;2;{r};{g};{b}m{ch}\x1b[0m")?;
} else {
let idx = 208 + ((t * 15.0) as u8).min(15);
write!(w, "\x1b[38;5;{idx}m{ch}\x1b[0m")?;
}
} else {
write!(w, "{ch}")?;
}
}
writeln!(w)?;
writeln!(w, " \x1b[90m{RULE_LINE}\x1b[0m")?;
} else {
writeln!(w, " {BRAND_LINE}")?;
writeln!(w, " {RULE_LINE}")?;
}
let version = env!("CARGO_PKG_VERSION");
if color {
writeln!(
w,
" \x1b[90mv{version} · secret scanner · {detector_count} detectors\x1b[0m"
)?;
writeln!(w, " \x1b[90mby santh\x1b[0m")?;
} else {
writeln!(
w,
" v{version} · secret scanner · {detector_count} detectors"
)?;
writeln!(w, " by santh")?;
}
writeln!(w)?;
Ok(())
}