use crate::drives::{human_size, DriveInfo, DriveScanner, LsblkScanner};
use crate::ops::{
build_command, progress_label, start_operation, FileSystem, OperationEvent, OperationKind,
OperationRequest,
};
use eframe::egui::{
self, Align2, Color32, FontId, Pos2, Rect, RichText, Rounding, Sense, Shape, Stroke, Vec2,
};
use std::collections::HashSet;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::mpsc::Receiver;
use std::time::{Duration, Instant};
pub struct MotteApp {
scanner: LsblkScanner,
drives: Vec<DriveInfo>,
selected_drive: Option<String>,
iso_path: String,
filesystem: FileSystem,
mode: OperationKind,
label: String,
target_acknowledged: bool,
final_confirm_open: bool,
logs: Vec<String>,
scan_error: Option<String>,
last_scan: Instant,
first_scan: bool,
operation_rx: Option<Receiver<OperationEvent>>,
operation_running: bool,
operation_status: String,
progress: Option<(u64, u64)>,
image_browser: ImageBrowserState,
logo_texture: Option<egui::TextureHandle>,
}
#[derive(Debug, Clone)]
struct ImageBrowserState {
open: bool,
current_dir: PathBuf,
entries: Vec<BrowserEntry>,
error: Option<String>,
}
impl ImageBrowserState {
fn new() -> Self {
Self {
open: false,
current_dir: initial_browser_directory(""),
entries: Vec::new(),
error: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct BrowserEntry {
name: String,
path: PathBuf,
kind: BrowserEntryKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum BrowserEntryKind {
Directory,
Image,
Other,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum BrowserAction {
None,
Parent,
Refresh,
OpenDirectory(PathBuf),
SelectImage(PathBuf),
Close,
}
impl MotteApp {
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
cc.egui_ctx.set_visuals(knott_visuals());
let logo_texture = load_logo_texture(&cc.egui_ctx);
let mut app = Self {
scanner: LsblkScanner,
drives: Vec::new(),
selected_drive: None,
iso_path: String::new(),
filesystem: FileSystem::Fat32,
mode: OperationKind::WipeAndFlash,
label: "MOTTE_USB".to_owned(),
target_acknowledged: false,
final_confirm_open: false,
logs: Vec::new(),
scan_error: None,
last_scan: Instant::now() - Duration::from_secs(10),
first_scan: true,
operation_rx: None,
operation_running: false,
operation_status: "READY".to_owned(),
progress: None,
image_browser: ImageBrowserState::new(),
logo_texture,
};
app.refresh_drives();
app
}
fn refresh_drives(&mut self) {
let previous_paths = self
.drives
.iter()
.map(|drive| drive.path.clone())
.collect::<HashSet<_>>();
match self.scanner.scan() {
Ok(drives) => {
self.scan_error = None;
if !self.first_scan {
for drive in &drives {
if !previous_paths.contains(&drive.path) {
self.push_log(format!(
"DETECTED {} // {} // {}",
drive.display_name(),
drive.path,
drive.human_size()
));
}
}
}
if let Some(selected) = &self.selected_drive {
if !drives.iter().any(|drive| &drive.path == selected) {
self.push_log(format!("TARGET REMOVED // {selected}"));
self.selected_drive = None;
self.target_acknowledged = false;
self.final_confirm_open = false;
}
}
self.drives = drives;
self.first_scan = false;
}
Err(error) => {
self.scan_error = Some(error.to_string());
}
}
self.last_scan = Instant::now();
}
fn drain_operation_events(&mut self) {
let mut finished = false;
if let Some(rx) = self.operation_rx.take() {
while let Ok(event) = rx.try_recv() {
match event {
OperationEvent::Started(command) => {
self.operation_status = "AUTHORIZATION".to_owned();
self.push_log(format!("START // {command}"));
}
OperationEvent::Log(line) => {
if line.contains("checking formatter tools") || line.contains("installing")
{
self.operation_status = "PREPARING TOOLS".to_owned();
} else if line.contains("releasing mounted") || line.contains("unmounting")
{
self.operation_status = "RELEASING MOUNTS".to_owned();
} else if line.contains("writing") {
self.operation_status = "WRITING IMAGE".to_owned();
} else if line.contains("refreshing target partition table") {
self.operation_status = "REFRESHING TARGET".to_owned();
} else if line.contains("target flush complete") {
if let Some((_, total)) = self.progress {
self.progress = Some((total, total));
}
self.operation_status = "FLUSH COMPLETE".to_owned();
} else if line.contains("creating partition table") {
self.operation_status = "PARTITIONING".to_owned();
} else if line.contains("waiting for the new partition") {
self.operation_status = "WAITING FOR PARTITION".to_owned();
} else if line.contains("formatting") {
self.operation_status = "FORMATTING".to_owned();
} else if line.contains("flushing") {
self.operation_status = "FLUSHING".to_owned();
}
self.push_log(line.to_ascii_uppercase());
}
OperationEvent::Progress {
copied_bytes,
total_bytes,
} => {
self.progress = Some((copied_bytes, total_bytes));
self.operation_status = progress_label(copied_bytes, total_bytes);
}
OperationEvent::Finished(result) => {
finished = true;
self.operation_running = false;
match result {
Ok(()) => {
self.progress = self.progress.map(|(_, total)| (total, total));
self.operation_status = "COMPLETE".to_owned();
self.push_log("OPERATION COMPLETE // MEDIA READY".to_owned());
}
Err(error) => {
self.operation_status = "FAILED".to_owned();
self.push_log(format!("OPERATION FAILED // {error}"));
}
}
}
}
}
if !finished {
self.operation_rx = Some(rx);
}
}
}
fn start_selected_operation(&mut self) {
if !self.can_start() {
self.final_confirm_open = false;
self.push_log("PRE-FLIGHT CHECK INCOMPLETE // OPERATION BLOCKED".to_owned());
return;
}
let Some(drive_path) = self.selected_drive.clone() else {
self.push_log("SELECT A REMOVABLE TARGET FIRST".to_owned());
return;
};
let request = OperationRequest {
kind: self.mode,
drive_path,
iso_path: self
.mode
.needs_iso()
.then(|| PathBuf::from(self.iso_path.trim())),
filesystem: self.filesystem,
label: self.label.clone(),
};
let expected_image_bytes = request.expected_image_bytes();
match build_command(&request) {
Ok(command) => {
self.logs.clear();
self.progress = None;
self.final_confirm_open = false;
self.operation_running = true;
self.operation_status = "STARTING".to_owned();
self.operation_rx = Some(start_operation(command, expected_image_bytes));
}
Err(error) => self.push_log(format!("CANNOT START // {error}")),
}
}
fn can_start(&self) -> bool {
self.selected_drive.is_some()
&& !self.operation_running
&& self.target_acknowledged
&& (!self.mode.needs_iso() || image_file_ready(&self.iso_path))
}
fn selected_drive(&self) -> Option<&DriveInfo> {
let selected = self.selected_drive.as_ref()?;
self.drives.iter().find(|drive| &drive.path == selected)
}
fn push_log(&mut self, line: String) {
self.logs.push(line);
if self.logs.len() > 250 {
let overflow = self.logs.len() - 250;
self.logs.drain(0..overflow);
}
}
fn open_image_browser(&mut self) {
self.image_browser.open = true;
self.image_browser.current_dir = initial_browser_directory(&self.iso_path);
self.refresh_browser_entries();
}
fn refresh_browser_entries(&mut self) {
match scan_browser_directory(&self.image_browser.current_dir) {
Ok(entries) => {
self.image_browser.entries = entries;
self.image_browser.error = None;
}
Err(error) => {
self.image_browser.entries.clear();
self.image_browser.error = Some(error.to_string());
}
}
}
fn set_browser_dir(&mut self, path: PathBuf) {
self.image_browser.current_dir = path;
self.refresh_browser_entries();
}
fn set_image_path(&mut self, path: PathBuf) {
self.iso_path = path.display().to_string();
let detail = fs::metadata(&path)
.ok()
.map(|metadata| format!(" // {}", human_size(metadata.len())))
.unwrap_or_default();
self.push_log(format!("IMAGE SELECTED // {}{}", self.iso_path, detail));
}
fn handle_dropped_files(&mut self, ctx: &egui::Context) {
let dropped_files = ctx.input(|input| input.raw.dropped_files.clone());
for file in dropped_files {
if let Some(path) = file.path {
if supported_image_extension(&path) {
self.set_image_path(path);
break;
}
}
}
}
fn handle_browser_action(&mut self, action: BrowserAction) {
match action {
BrowserAction::None => {}
BrowserAction::Parent => {
if let Some(parent) = self.image_browser.current_dir.parent() {
self.set_browser_dir(parent.to_path_buf());
}
}
BrowserAction::Refresh => self.refresh_browser_entries(),
BrowserAction::OpenDirectory(path) => self.set_browser_dir(path),
BrowserAction::SelectImage(path) => {
self.set_image_path(path);
self.image_browser.open = false;
}
BrowserAction::Close => self.image_browser.open = false,
}
}
}
impl eframe::App for MotteApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
self.handle_dropped_files(ctx);
self.drain_operation_events();
if self.last_scan.elapsed() >= Duration::from_secs(2) && !self.operation_running {
self.refresh_drives();
}
let phase = ctx.input(|input| input.time) as f32;
ctx.request_repaint_after(Duration::from_millis(if self.operation_running {
16
} else {
33
}));
egui::CentralPanel::default()
.frame(egui::Frame::none().fill(kd_black()))
.show(ctx, |ui| {
let viewport_width = ui.available_width();
let viewport_height = ui.available_height();
let compact = viewport_width < 1040.0 || viewport_height < 760.0;
paint_background(ui, phase);
egui::ScrollArea::vertical()
.auto_shrink([false, false])
.show(ui, |ui| {
ui.set_min_width(viewport_width);
system_status_bar(
ui,
self.operation_running,
&self.operation_status,
phase,
);
ui.add_space(if compact { 10.0 } else { 18.0 });
self.header(ui, phase, compact);
ui.add_space(if compact { 10.0 } else { 16.0 });
if viewport_width < 980.0 {
self.drive_panel(ui, phase, compact);
ui.add_space(14.0);
self.action_panel(ui, phase, compact);
} else {
ui.columns(2, |columns| {
columns[0].vertical(|ui| self.drive_panel(ui, phase, compact));
columns[1].vertical(|ui| self.action_panel(ui, phase, compact));
});
}
});
});
self.image_browser_window(ctx, phase);
self.final_confirmation_window(ctx, phase);
}
}
impl MotteApp {
fn header(&mut self, ui: &mut egui::Ui, phase: f32, compact: bool) {
let header_height = if compact { 132.0 } else { 178.0 };
let (rect, _) = ui.allocate_exact_size(
Vec2::new(ui.available_width(), header_height),
Sense::hover(),
);
let painter = ui.painter_at(rect);
painter.rect_filled(rect, Rounding::same(2.0), Color32::from_rgb(3, 3, 3));
painter.rect_stroke(rect, Rounding::ZERO, Stroke::new(1.0, kd_border()));
draw_corner_brackets(&painter, rect, 34.0, Color32::from_gray(122));
draw_header_sweep(&painter, rect, phase);
if rect.width() > 760.0 {
let mark_offset = if compact { -92.0 } else { -116.0 };
let mark_center = rect.right_center() + egui::vec2(mark_offset, 0.0);
if let Some(texture) = &self.logo_texture {
let logo_size = texture.size_vec2();
let max_size =
egui::vec2(if compact { 132.0 } else { 168.0 }, rect.height() - 26.0);
let scale = (max_size.x / logo_size.x).min(max_size.y / logo_size.y);
let size = logo_size * scale;
let logo_rect = Rect::from_center_size(mark_center, size);
painter.image(
texture.id(),
logo_rect,
Rect::from_min_max(Pos2::ZERO, Pos2::new(1.0, 1.0)),
Color32::WHITE,
);
} else {
draw_trefoil_mark(
&painter,
mark_center,
if compact { 25.0 } else { 32.0 },
phase,
);
draw_crosshair(
&painter,
mark_center,
if compact { 58.0 } else { 78.0 },
Color32::from_rgba_premultiplied(255, 255, 255, 48),
);
painter.text(
mark_center + egui::vec2(0.0, if compact { 48.0 } else { 62.0 }),
Align2::CENTER_CENTER,
"KNOTT DYNAMICS",
FontId::monospace(if compact { 9.0 } else { 11.0 }),
Color32::from_gray(176),
);
}
}
painter.text(
rect.left_top() + egui::vec2(28.0, 22.0),
Align2::LEFT_TOP,
"MOTTE",
FontId::proportional(if compact { 44.0 } else { 60.0 }),
Color32::from_rgb(246, 246, 246),
);
painter.text(
rect.left_top() + egui::vec2(31.0, if compact { 72.0 } else { 88.0 }),
Align2::LEFT_TOP,
"STRENGTH IN STRUCTURE // RUST MEDIA SYSTEM",
FontId::monospace(if compact { 11.0 } else { 14.0 }),
Color32::from_gray(172),
);
painter.text(
rect.left_top() + egui::vec2(31.0, if compact { 96.0 } else { 116.0 }),
Align2::LEFT_TOP,
"ARCH ISO FLASHER / USB FORMATTER / REMOVABLE TARGET CONTROL",
FontId::monospace(11.0),
Color32::from_gray(96),
);
if !compact {
painter.text(
rect.left_top() + egui::vec2(31.0, 140.0),
Align2::LEFT_TOP,
"FLASH PATH: IMAGE -> REMOVABLE BUS -> SYNC // FORMAT PATH: WIPE -> PARTITION -> FS",
FontId::monospace(9.0),
Color32::from_gray(58),
);
painter.text(
rect.left_bottom() + egui::vec2(30.0, -12.0),
Align2::LEFT_BOTTOM,
"REV 0.1 // MEDIA ACTUATION STACK // VERIFY DEVICE PATH BEFORE WRITE",
FontId::monospace(10.0),
Color32::from_gray(70),
);
}
let status = if self.operation_running {
self.operation_status.as_str()
} else if self.drives.is_empty() {
"AWAITING TARGET"
} else {
"ONLINE"
};
if rect.width() > 760.0 {
draw_status_pill(
&painter,
rect.right_top() + egui::vec2(-250.0, 24.0),
status,
);
}
}
fn drive_panel(&mut self, ui: &mut egui::Ui, phase: f32, compact: bool) {
let response = kd_panel(ui, |ui| {
panel_heading(ui, "TARGET BUS", "REMOVABLE DRIVES");
ui.add_space(10.0);
if let Some(error) = &self.scan_error {
warning_text(ui, &format!("DRIVE SCAN FAILED // {error}"));
ui.add_space(8.0);
}
if self.drives.is_empty() {
empty_state(
ui,
"NO USB TARGET",
"Plug in removable media. Motte scans the device bus automatically.",
);
} else {
let mut clicked_drive = None;
egui::ScrollArea::vertical()
.max_height(if compact { 220.0 } else { 330.0 })
.auto_shrink([false, false])
.show(ui, |ui| {
for drive in &self.drives {
let selected = self.selected_drive.as_deref() == Some(&drive.path);
let response = drive_card(ui, drive, selected, phase);
if response.clicked() && !self.operation_running && !drive.read_only {
clicked_drive = Some(drive.path.clone());
}
ui.add_space(10.0);
}
});
if let Some(path) = clicked_drive {
if self.selected_drive.as_deref() != Some(path.as_str()) {
self.target_acknowledged = false;
self.final_confirm_open = false;
}
self.selected_drive = Some(path);
}
}
ui.add_space(12.0);
selected_drive_summary(ui, self.selected_drive());
ui.add_space(10.0);
if ui
.add_enabled(!self.operation_running, kd_button("RESCAN BUS", false))
.clicked()
{
self.refresh_drives();
}
});
draw_outer_glow(ui.painter(), response.rect, 2, 20.0, 10);
draw_corner_brackets(ui.painter(), response.rect, 22.0, Color32::from_gray(88));
}
fn action_panel(&mut self, ui: &mut egui::Ui, phase: f32, compact: bool) {
let response = kd_panel(ui, |ui| {
panel_heading(ui, "MEDIA PROTOCOL", "CREATE BOOT MEDIA");
ui.add_space(10.0);
for mode in OperationKind::ALL {
let selected = self.mode == mode;
let response = mode_chip(ui, mode.title(), mode.description(), selected, phase);
if response.clicked() && !self.operation_running {
self.mode = mode;
}
ui.add_space(8.0);
}
ui.add_space(10.0);
if self.mode.needs_iso() {
self.image_picker(ui, phase);
} else {
format_controls(
ui,
&mut self.filesystem,
&mut self.label,
self.operation_running,
);
}
ui.add_space(14.0);
let selected_target = self.selected_drive.clone();
target_acknowledgement_box(
ui,
selected_target.as_deref(),
&mut self.target_acknowledged,
self.operation_running,
);
ui.add_space(10.0);
readiness_panel(
ui,
self.selected_drive.is_some(),
self.mode,
&self.iso_path,
self.target_acknowledged,
);
ui.add_space(12.0);
if let Some((copied, total)) = self.progress {
animated_progress(ui, copied, total, phase);
ui.add_space(8.0);
} else if self.operation_running {
indeterminate_progress(ui, phase);
ui.add_space(8.0);
}
let can_start = self.can_start();
let button_text = if self.operation_running {
"RUNNING"
} else {
"REVIEW + SUBMIT"
};
if ui
.add_enabled(can_start, kd_button(button_text, true))
.clicked()
{
self.final_confirm_open = true;
}
if !can_start && !self.operation_running {
ui.add_space(8.0);
ui.label(
RichText::new(
"SELECT TARGET, SELECT IMAGE OR FORMAT SETTINGS, THEN CHECK THE TARGET ACKNOWLEDGEMENT.",
)
.size(11.0)
.monospace()
.color(Color32::from_gray(112)),
);
}
ui.add_space(14.0);
log_panel(ui, &self.logs, compact);
});
draw_outer_glow(ui.painter(), response.rect, 2, 20.0, 10);
draw_corner_brackets(ui.painter(), response.rect, 22.0, Color32::from_gray(88));
}
fn image_picker(&mut self, ui: &mut egui::Ui, phase: f32) {
let mut browse_clicked = false;
let mut clear_clicked = false;
ui.horizontal(|ui| {
field_label(ui, "BOOT IMAGE SOURCE");
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui
.add_enabled(
!self.operation_running,
kd_button("OPEN IMAGE VAULT", false),
)
.clicked()
{
browse_clicked = true;
}
if ui
.add_enabled(
!self.operation_running && !self.iso_path.trim().is_empty(),
kd_button("CLEAR", false),
)
.clicked()
{
clear_clicked = true;
}
});
});
let edit = egui::TextEdit::singleline(&mut self.iso_path)
.hint_text("/home/me/Downloads/archlinux.iso or drag an image here")
.desired_width(f32::INFINITY);
ui.add_enabled(!self.operation_running, edit);
ui.add_space(9.0);
image_preview_card(ui, self.iso_path.trim(), phase);
if browse_clicked {
self.open_image_browser();
}
if clear_clicked {
self.iso_path.clear();
self.push_log("IMAGE CLEARED".to_owned());
}
}
fn image_browser_window(&mut self, ctx: &egui::Context, phase: f32) {
if !self.image_browser.open {
return;
}
let mut open = self.image_browser.open;
let mut action = BrowserAction::None;
egui::Window::new("IMAGE VAULT // LOCAL FILESYSTEM")
.open(&mut open)
.collapsible(false)
.resizable(true)
.default_size(Vec2::new(820.0, 560.0))
.frame(
egui::Frame::none()
.fill(Color32::from_rgba_premultiplied(0, 0, 0, 245))
.stroke(Stroke::new(1.0, Color32::from_gray(120)))
.inner_margin(egui::Margin::same(16.0)),
)
.show(ctx, |ui| {
action = image_browser_contents(ui, &self.image_browser, phase);
});
self.image_browser.open = open;
self.handle_browser_action(action);
}
fn final_confirmation_window(&mut self, ctx: &egui::Context, phase: f32) {
if !self.final_confirm_open {
return;
}
let target = self
.selected_drive
.clone()
.unwrap_or_else(|| "NO TARGET".to_owned());
let target_detail = self
.selected_drive()
.map(|drive| format!("{} // {}", drive.display_name(), drive.human_size()))
.unwrap_or_else(|| "TARGET NO LONGER PRESENT".to_owned());
let media_detail = if self.mode.needs_iso() {
if self.iso_path.trim().is_empty() {
"NO IMAGE SELECTED".to_owned()
} else {
self.iso_path.clone()
}
} else {
format!("{} // LABEL {}", self.filesystem.label(), self.label)
};
let operation = self.mode.title().to_ascii_uppercase();
let mut open = self.final_confirm_open;
let mut submit = false;
let mut cancel = false;
egui::Window::new("FINAL CONFIRMATION // ARE YOU SURE?")
.open(&mut open)
.collapsible(false)
.resizable(false)
.default_size(Vec2::new(620.0, 390.0))
.frame(
egui::Frame::none()
.fill(Color32::from_rgba_premultiplied(0, 0, 0, 248))
.stroke(Stroke::new(1.0, Color32::from_gray(176)))
.inner_margin(egui::Margin::same(18.0)),
)
.show(ctx, |ui| {
let rect = ui.max_rect();
draw_corner_brackets(
ui.painter(),
rect.shrink(2.0),
28.0,
Color32::from_gray(128),
);
panel_heading(ui, "DESTRUCTIVE SUBMIT", "ARE YOU ABSOLUTELY SURE?");
ui.add_space(12.0);
final_warning_card(ui, phase);
ui.add_space(12.0);
tech_note(ui, "OPERATION", &operation);
ui.add_space(8.0);
tech_note(
ui,
"TARGET DEVICE",
&format!("{} // {}", target, target_detail),
);
ui.add_space(8.0);
tech_note(ui, "MEDIA / FORMAT", &media_detail);
ui.add_space(14.0);
ui.label(
RichText::new(
"THIS WILL DESTROY DATA ON THE SELECTED DEVICE. THIS CANNOT BE UNDONE.",
)
.monospace()
.size(11.0)
.strong()
.color(Color32::from_gray(230)),
);
ui.add_space(16.0);
ui.horizontal(|ui| {
if ui.add(kd_button("CANCEL", false)).clicked() {
cancel = true;
}
if ui
.add_enabled(self.can_start(), kd_button("YES, ERASE AND START", true))
.clicked()
{
submit = true;
}
});
});
self.final_confirm_open = open && !cancel && !submit;
if cancel {
self.push_log("FINAL CONFIRMATION CANCELLED".to_owned());
}
if submit {
self.start_selected_operation();
}
}
}
fn load_logo_texture(ctx: &egui::Context) -> Option<egui::TextureHandle> {
let bytes = include_bytes!("../assets/logo.png");
let image = image::load_from_memory(bytes).ok()?.to_rgba8();
let size = [image.width() as usize, image.height() as usize];
let pixels = image.as_flat_samples();
let color_image = egui::ColorImage::from_rgba_unmultiplied(size, pixels.as_slice());
Some(ctx.load_texture("motte-logo", color_image, egui::TextureOptions::LINEAR))
}
fn knott_visuals() -> egui::Visuals {
let mut visuals = egui::Visuals::dark();
visuals.override_text_color = Some(Color32::from_gray(220));
visuals.panel_fill = kd_black();
visuals.window_fill = kd_black();
visuals.extreme_bg_color = kd_black();
visuals.faint_bg_color = Color32::from_rgb(8, 8, 8);
visuals.widgets.noninteractive.bg_fill = Color32::from_rgb(8, 8, 8);
visuals.widgets.inactive.bg_fill = Color32::from_rgb(12, 12, 12);
visuals.widgets.hovered.bg_fill = Color32::from_rgb(24, 24, 24);
visuals.widgets.active.bg_fill = Color32::from_rgb(34, 34, 34);
visuals.widgets.inactive.fg_stroke = Stroke::new(1.0, Color32::from_gray(188));
visuals.widgets.hovered.fg_stroke = Stroke::new(1.0, Color32::WHITE);
visuals.selection.bg_fill = Color32::from_gray(230);
visuals.selection.stroke = Stroke::new(1.0, Color32::WHITE);
visuals
}
fn kd_black() -> Color32 {
Color32::from_rgb(0, 0, 0)
}
fn kd_surface() -> Color32 {
Color32::from_rgb(8, 8, 8)
}
fn kd_panel_fill() -> Color32 {
Color32::from_rgba_premultiplied(6, 6, 6, 232)
}
fn kd_border() -> Color32 {
Color32::from_rgb(32, 32, 32)
}
fn kd_green() -> Color32 {
Color32::from_rgb(0, 255, 136)
}
fn draw_outer_glow(
painter: &egui::Painter,
rect: Rect,
passes: usize,
spread: f32,
base_alpha: u8,
) {
for pass in 0..passes {
let t = pass as f32 / passes.max(1) as f32;
let alpha = ((base_alpha as f32) * (1.0 - t * 0.72)).round() as u8;
painter.rect_stroke(
rect.expand(spread * (t + 0.35)),
Rounding::same(2.0),
Stroke::new(1.0, Color32::from_rgba_premultiplied(255, 255, 255, alpha)),
);
}
}
fn draw_header_sweep(painter: &egui::Painter, rect: Rect, phase: f32) {
let sweep = ((phase * 0.18).fract() * (rect.width() + 260.0)) - 130.0;
let x = rect.left() + sweep;
painter.line_segment(
[
Pos2::new(x, rect.top() + 8.0),
Pos2::new(x + 130.0, rect.bottom() - 8.0),
],
Stroke::new(1.4, Color32::from_rgba_premultiplied(255, 255, 255, 32)),
);
painter.line_segment(
[
Pos2::new(x - 24.0, rect.top() + 8.0),
Pos2::new(x + 106.0, rect.bottom() - 8.0),
],
Stroke::new(0.6, Color32::from_rgba_premultiplied(255, 255, 255, 18)),
);
}
fn paint_background(ui: &mut egui::Ui, phase: f32) {
let rect = ui.max_rect();
let painter = ui.painter().clone();
painter.rect_filled(rect, Rounding::ZERO, kd_black());
let spacing = 52.0;
let mut x = rect.left() - rect.left().rem_euclid(spacing);
while x <= rect.right() {
painter.line_segment(
[Pos2::new(x, rect.top()), Pos2::new(x, rect.bottom())],
Stroke::new(1.0, Color32::from_rgba_premultiplied(255, 255, 255, 8)),
);
x += spacing;
}
let mut y = rect.top() - rect.top().rem_euclid(spacing);
while y <= rect.bottom() {
painter.line_segment(
[Pos2::new(rect.left(), y), Pos2::new(rect.right(), y)],
Stroke::new(1.0, Color32::from_rgba_premultiplied(255, 255, 255, 8)),
);
y += spacing;
}
draw_algorithmic_botany(&painter, rect, phase);
for (index, label) in [
"R:0.117 SIGMA:0.884 EPS:0.031",
"USB BUS // REMOVABLE MEDIA ONLY",
"MOTTE-RS // KD VISUAL PROTOCOL",
]
.iter()
.enumerate()
{
painter.text(
rect.left_top() + egui::vec2(14.0, 88.0 + index as f32 * 170.0),
Align2::LEFT_TOP,
*label,
FontId::monospace(9.0),
Color32::from_gray(42),
);
}
}
fn draw_algorithmic_botany(painter: &egui::Painter, rect: Rect, phase: f32) {
let vines = [
(
rect.left_top() + egui::vec2(70.0, 185.0),
0.58,
rect.height().min(720.0) * 0.18,
0.0,
),
(
rect.left_bottom() + egui::vec2(80.0, -6.0),
-1.18,
rect.height().min(720.0) * 0.18,
0.67,
),
(
rect.left_bottom() + egui::vec2(rect.width() * 0.49, -8.0),
-1.88,
rect.height().min(720.0) * 0.18,
1.34,
),
];
for (index, (base, base_angle, length, offset)) in vines.iter().enumerate() {
let growth = ping_pong(phase * 0.075 + offset);
let angle = base_angle + (phase * 0.08 + index as f32).sin() * 0.025;
draw_branch(
painter,
*base,
angle,
*length,
0,
5,
growth,
Color32::from_rgba_premultiplied(255, 255, 255, 22),
);
}
}
fn ping_pong(value: f32) -> f32 {
let value = value.rem_euclid(2.0);
if value <= 1.0 {
value
} else {
2.0 - value
}
}
fn draw_branch(
painter: &egui::Painter,
start: Pos2,
angle: f32,
length: f32,
depth: usize,
max_depth: usize,
growth: f32,
color: Color32,
) {
if depth > max_depth || length < 7.0 {
return;
}
let stage = growth * (max_depth as f32 + 1.0) - depth as f32;
let visible = stage.clamp(0.0, 1.0);
if visible <= 0.0 {
return;
}
let alpha = color.a().saturating_sub((depth as u8) * 3);
let stroke = Stroke::new(
(0.95 - depth as f32 * 0.09).max(0.3),
Color32::from_rgba_premultiplied(255, 255, 255, alpha),
);
let full_end = start + egui::vec2(angle.cos() * length, angle.sin() * length);
let end = start + (full_end - start) * visible;
painter.line_segment([start, end], stroke);
if visible < 1.0 {
return;
}
let next = length * 0.66;
let spread = 0.42 + depth as f32 * 0.035;
draw_branch(
painter,
end,
angle - spread,
next,
depth + 1,
max_depth,
growth,
color,
);
draw_branch(
painter,
end,
angle + spread * 0.78,
next * 0.9,
depth + 1,
max_depth,
growth,
color,
);
}
fn system_status_bar(ui: &mut egui::Ui, running: bool, status: &str, phase: f32) {
let (rect, _) = ui.allocate_exact_size(Vec2::new(ui.available_width(), 30.0), Sense::hover());
let painter = ui.painter_at(rect);
painter.rect_filled(
rect,
Rounding::ZERO,
Color32::from_rgba_premultiplied(0, 0, 0, 235),
);
painter.line_segment(
[rect.left_bottom(), rect.right_bottom()],
Stroke::new(1.0, kd_border()),
);
let dot = if running {
((phase * 8.0).sin() * 0.5 + 0.5) * 3.0 + 3.0
} else {
4.0
};
painter.circle_filled(rect.left_center() + egui::vec2(16.0, 0.0), dot, kd_green());
painter.text(
rect.left_center() + egui::vec2(30.0, 0.0),
Align2::LEFT_CENTER,
format!("ONLINE // SYS:MOTTE // BUILD:0.1 // {status}"),
FontId::monospace(11.0),
Color32::from_gray(186),
);
if rect.width() > 820.0 {
painter.text(
rect.right_center() + egui::vec2(-12.0, 0.0),
Align2::RIGHT_CENTER,
"KNOTT DYNAMICS AESTHETIC // SILVER-ON-BLACK",
FontId::monospace(10.0),
Color32::from_gray(78),
);
}
}
fn kd_panel<R>(ui: &mut egui::Ui, add_contents: impl FnOnce(&mut egui::Ui) -> R) -> egui::Response {
egui::Frame::none()
.fill(kd_panel_fill())
.rounding(Rounding::same(2.0))
.stroke(Stroke::new(1.0, kd_border()))
.inner_margin(egui::Margin::same(18.0))
.show(ui, add_contents)
.response
}
fn panel_heading(ui: &mut egui::Ui, tag: &str, title: &str) {
ui.horizontal(|ui| {
ui.vertical(|ui| {
ui.label(
RichText::new(tag)
.monospace()
.size(10.0)
.color(Color32::from_gray(112)),
);
ui.label(
RichText::new(title)
.size(24.0)
.strong()
.color(Color32::from_rgb(244, 244, 244)),
);
});
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
let (rect, _) = ui.allocate_exact_size(Vec2::new(74.0, 24.0), Sense::hover());
let painter = ui.painter_at(rect);
painter.rect_stroke(
rect,
Rounding::ZERO,
Stroke::new(1.0, Color32::from_gray(64)),
);
painter.text(
rect.center(),
Align2::CENTER_CENTER,
"MK.IV",
FontId::monospace(10.0),
Color32::from_gray(150),
);
});
});
ui.add_space(4.0);
let (rect, _) = ui.allocate_exact_size(Vec2::new(ui.available_width(), 1.0), Sense::hover());
ui.painter().line_segment(
[rect.left_center(), rect.right_center()],
Stroke::new(1.0, Color32::from_gray(34)),
);
}
fn kd_button(label: &str, primary: bool) -> egui::Button<'_> {
let text = RichText::new(label)
.monospace()
.size(if primary { 16.0 } else { 11.0 })
.strong()
.color(if primary {
Color32::BLACK
} else {
Color32::from_gray(220)
});
egui::Button::new(text)
.fill(if primary {
Color32::from_gray(232)
} else {
kd_surface()
})
.stroke(Stroke::new(
1.0,
if primary {
Color32::WHITE
} else {
Color32::from_gray(74)
},
))
.rounding(Rounding::same(1.0))
.min_size(Vec2::new(
if primary { 0.0 } else { 112.0 },
if primary { 52.0 } else { 32.0 },
))
}
fn draw_corner_brackets(painter: &egui::Painter, rect: Rect, len: f32, color: Color32) {
let stroke = Stroke::new(1.0, color);
let corners = [
(rect.left_top(), egui::vec2(1.0, 0.0), egui::vec2(0.0, 1.0)),
(
rect.right_top(),
egui::vec2(-1.0, 0.0),
egui::vec2(0.0, 1.0),
),
(
rect.left_bottom(),
egui::vec2(1.0, 0.0),
egui::vec2(0.0, -1.0),
),
(
rect.right_bottom(),
egui::vec2(-1.0, 0.0),
egui::vec2(0.0, -1.0),
),
];
for (corner, x_dir, y_dir) in corners {
painter.line_segment([corner, corner + x_dir * len], stroke);
painter.line_segment([corner, corner + y_dir * len], stroke);
}
}
fn draw_crosshair(painter: &egui::Painter, center: Pos2, radius: f32, color: Color32) {
let stroke = Stroke::new(1.0, color);
painter.circle_stroke(center, radius, stroke);
painter.circle_stroke(center, radius * 0.52, stroke);
painter.line_segment(
[
center + egui::vec2(-radius, 0.0),
center + egui::vec2(radius, 0.0),
],
stroke,
);
painter.line_segment(
[
center + egui::vec2(0.0, -radius),
center + egui::vec2(0.0, radius),
],
stroke,
);
}
fn draw_trefoil_mark(painter: &egui::Painter, center: Pos2, scale: f32, phase: f32) {
let mut points = Vec::with_capacity(181);
for step in 0..=180 {
let t = step as f32 / 180.0 * std::f32::consts::TAU + phase * 0.08;
let x = (t.sin() + 2.0 * (2.0 * t).sin()) * scale * 0.42;
let y = (t.cos() - 2.0 * (2.0 * t).cos()) * scale * 0.42;
points.push(center + egui::vec2(x, y));
}
painter.add(Shape::line(
points,
Stroke::new(2.0, Color32::from_gray(214)),
));
painter.text(
center,
Align2::CENTER_CENTER,
"K",
FontId::proportional(24.0),
Color32::WHITE,
);
}
fn draw_status_pill(painter: &egui::Painter, pos: Pos2, label: &str) {
let rect = Rect::from_min_size(pos, Vec2::new(220.0, 34.0));
painter.rect_filled(rect, Rounding::same(1.0), kd_surface());
painter.rect_stroke(
rect,
Rounding::ZERO,
Stroke::new(1.0, Color32::from_gray(82)),
);
painter.circle_filled(rect.left_center() + egui::vec2(16.0, 0.0), 4.0, kd_green());
painter.text(
rect.left_center() + egui::vec2(30.0, 0.0),
Align2::LEFT_CENTER,
label,
FontId::monospace(11.0),
Color32::from_gray(218),
);
}
fn drive_card(ui: &mut egui::Ui, drive: &DriveInfo, selected: bool, phase: f32) -> egui::Response {
let (rect, response) =
ui.allocate_exact_size(Vec2::new(ui.available_width(), 112.0), Sense::click());
let hover = response.hovered();
let pulse = ((phase * 3.0).sin() * 0.5 + 0.5) * 52.0;
let fill = if selected {
Color32::from_rgb(18, 18, 18)
} else if hover {
Color32::from_rgb(14, 14, 14)
} else {
kd_surface()
};
ui.painter().rect_filled(rect, Rounding::same(1.0), fill);
ui.painter().rect_stroke(
rect,
Rounding::ZERO,
Stroke::new(
if selected { 1.5 } else { 1.0 },
if selected {
Color32::from_rgba_premultiplied(255, 255, 255, (128.0 + pulse) as u8)
} else {
Color32::from_gray(44)
},
),
);
if selected {
draw_outer_glow(ui.painter(), rect, 2, 8.0, 26);
}
draw_corner_brackets(ui.painter(), rect.shrink(2.0), 14.0, Color32::from_gray(82));
let icon_rect = Rect::from_min_size(
rect.left_top() + egui::vec2(16.0, 22.0),
Vec2::new(58.0, 58.0),
);
ui.painter().rect_stroke(
icon_rect,
Rounding::ZERO,
Stroke::new(1.0, Color32::from_gray(112)),
);
ui.painter().line_segment(
[icon_rect.left_center(), icon_rect.right_center()],
Stroke::new(1.0, Color32::from_gray(46)),
);
ui.painter().text(
icon_rect.center(),
Align2::CENTER_CENTER,
"USB",
FontId::monospace(13.0),
Color32::from_gray(230),
);
ui.painter().text(
rect.left_top() + egui::vec2(92.0, 18.0),
Align2::LEFT_TOP,
drive.display_name().to_ascii_uppercase(),
FontId::proportional(18.0),
Color32::from_gray(238),
);
ui.painter().text(
rect.left_top() + egui::vec2(92.0, 47.0),
Align2::LEFT_TOP,
format!(
"{} // {} // {}",
drive.path,
drive.human_size(),
drive.safety_badge().to_ascii_uppercase()
),
FontId::monospace(12.0),
Color32::from_gray(150),
);
let mounts = if drive.mountpoints.is_empty() {
"UNMOUNTED".to_owned()
} else {
drive.mountpoints.join(" // ").to_ascii_uppercase()
};
ui.painter().text(
rect.left_top() + egui::vec2(92.0, 74.0),
Align2::LEFT_TOP,
mounts,
FontId::monospace(11.0),
if drive.read_only {
Color32::from_gray(120)
} else {
kd_green()
},
);
draw_drive_selector(ui.painter(), rect, selected, drive.read_only, phase);
response
}
fn draw_drive_selector(
painter: &egui::Painter,
rect: Rect,
selected: bool,
read_only: bool,
phase: f32,
) {
let box_rect = Rect::from_min_size(
rect.right_center() + egui::vec2(-58.0, -15.0),
Vec2::new(30.0, 30.0),
);
let pulse = ((phase * 4.0).sin() * 0.5 + 0.5) * 48.0;
let stroke_color = if selected {
Color32::from_rgba_premultiplied(0, 255, 136, (150.0 + pulse) as u8)
} else if read_only {
Color32::from_gray(58)
} else {
Color32::from_gray(112)
};
painter.rect_stroke(box_rect, Rounding::ZERO, Stroke::new(1.5, stroke_color));
if selected {
painter.line_segment(
[
box_rect.left_center() + egui::vec2(7.0, 1.0),
box_rect.center() + egui::vec2(-2.0, 7.0),
],
Stroke::new(2.0, kd_green()),
);
painter.line_segment(
[
box_rect.center() + egui::vec2(-2.0, 7.0),
box_rect.right_center() + egui::vec2(-6.0, -8.0),
],
Stroke::new(2.0, kd_green()),
);
}
painter.text(
box_rect.center_bottom() + egui::vec2(0.0, 17.0),
Align2::CENTER_CENTER,
if read_only {
"LOCKED"
} else if selected {
"CHECKED"
} else {
"SELECT"
},
FontId::monospace(9.0),
if selected {
kd_green()
} else {
Color32::from_gray(92)
},
);
}
fn selected_drive_summary(ui: &mut egui::Ui, selected: Option<&DriveInfo>) {
match selected {
Some(drive) => tech_note(
ui,
"TARGET LOCK",
&format!(
"{} // {} PARTITION(S) // CHECK TARGET ACKNOWLEDGEMENT BEFORE SUBMIT",
drive.path,
drive.partitions.len()
),
),
None => tech_note(
ui,
"TARGET LOCK",
"NO TARGET SELECTED // INSERT USB MEDIA OR RESCAN BUS",
),
}
}
fn tech_note(ui: &mut egui::Ui, tag: &str, text: &str) {
egui::Frame::none()
.fill(Color32::from_rgb(4, 4, 4))
.stroke(Stroke::new(1.0, Color32::from_gray(38)))
.inner_margin(egui::Margin::same(10.0))
.show(ui, |ui| {
ui.label(
RichText::new(tag)
.monospace()
.size(10.0)
.color(Color32::from_gray(96)),
);
ui.label(
RichText::new(text)
.monospace()
.size(11.0)
.color(Color32::from_gray(178)),
);
});
}
fn mode_chip(
ui: &mut egui::Ui,
title: &str,
description: &str,
selected: bool,
phase: f32,
) -> egui::Response {
let (rect, response) =
ui.allocate_exact_size(Vec2::new(ui.available_width(), 72.0), Sense::click());
let glow = ((phase * 2.7).sin() * 0.5 + 0.5) * 60.0;
ui.painter().rect_filled(
rect,
Rounding::same(1.0),
if selected {
Color32::from_rgb(18, 18, 18)
} else {
kd_surface()
},
);
ui.painter().rect_stroke(
rect,
Rounding::ZERO,
Stroke::new(
if selected { 1.5 } else { 1.0 },
if selected {
Color32::from_rgba_premultiplied(255, 255, 255, (120.0 + glow) as u8)
} else {
Color32::from_gray(46)
},
),
);
if selected {
draw_outer_glow(ui.painter(), rect, 2, 7.0, 22);
}
ui.painter().text(
rect.left_top() + egui::vec2(18.0, 12.0),
Align2::LEFT_TOP,
title.to_ascii_uppercase(),
FontId::monospace(15.0),
Color32::from_gray(236),
);
ui.painter().text(
rect.left_top() + egui::vec2(18.0, 40.0),
Align2::LEFT_TOP,
description.to_ascii_uppercase(),
FontId::monospace(10.0),
Color32::from_gray(132),
);
ui.painter().text(
rect.right_center() + egui::vec2(-18.0, 0.0),
Align2::RIGHT_CENTER,
if selected { "ACTIVE" } else { "STANDBY" },
FontId::monospace(10.0),
if selected {
kd_green()
} else {
Color32::from_gray(82)
},
);
response
}
fn field_label(ui: &mut egui::Ui, label: &str) {
ui.label(
RichText::new(label)
.monospace()
.size(11.0)
.color(Color32::from_gray(166)),
);
}
fn format_controls(
ui: &mut egui::Ui,
filesystem: &mut FileSystem,
label: &mut String,
disabled: bool,
) {
field_label(ui, "FORMAT PARAMETERS");
ui.horizontal(|ui| {
for candidate in FileSystem::ALL {
ui.add_enabled_ui(!disabled, |ui| {
ui.selectable_value(
filesystem,
candidate,
candidate.label().to_ascii_uppercase(),
);
});
}
});
ui.add_space(8.0);
field_label(ui, "VOLUME LABEL");
let edit = egui::TextEdit::singleline(label)
.hint_text("MOTTE_USB")
.desired_width(f32::INFINITY);
ui.add_enabled(!disabled, edit);
}
fn image_preview_card(ui: &mut egui::Ui, image_path: &str, phase: f32) {
let pulse = ((phase * 3.4).sin() * 0.5 + 0.5) * 44.0;
let (title, detail, badge, badge_color, stroke_color) = image_preview_state(image_path, pulse);
let (rect, _) = ui.allocate_exact_size(Vec2::new(ui.available_width(), 104.0), Sense::hover());
ui.painter()
.rect_filled(rect, Rounding::same(1.0), kd_surface());
ui.painter()
.rect_stroke(rect, Rounding::ZERO, Stroke::new(1.0, stroke_color));
if image_file_ready(image_path) {
draw_outer_glow(ui.painter(), rect, 2, 7.0, 20);
}
draw_corner_brackets(ui.painter(), rect.shrink(2.0), 14.0, Color32::from_gray(78));
let icon_rect = Rect::from_min_size(
rect.left_top() + egui::vec2(16.0, 22.0),
Vec2::new(58.0, 58.0),
);
ui.painter().rect_stroke(
icon_rect,
Rounding::ZERO,
Stroke::new(1.0, Color32::from_gray(112)),
);
ui.painter().line_segment(
[
icon_rect.left_top() + egui::vec2(10.0, 14.0),
icon_rect.right_top() + egui::vec2(-10.0, 14.0),
],
Stroke::new(1.0, Color32::from_gray(48)),
);
ui.painter().text(
icon_rect.center(),
Align2::CENTER_CENTER,
"ISO",
FontId::monospace(13.0),
Color32::from_gray(230),
);
ui.painter().text(
rect.left_top() + egui::vec2(92.0, 20.0),
Align2::LEFT_TOP,
title.to_ascii_uppercase(),
FontId::proportional(18.0),
Color32::from_gray(238),
);
ui.painter().text(
rect.left_top() + egui::vec2(92.0, 50.0),
Align2::LEFT_TOP,
detail.to_ascii_uppercase(),
FontId::monospace(11.0),
Color32::from_gray(142),
);
ui.painter().text(
rect.right_top() + egui::vec2(-18.0, 20.0),
Align2::RIGHT_TOP,
badge,
FontId::monospace(10.0),
badge_color,
);
}
fn image_preview_state(
image_path: &str,
pulse: f32,
) -> (String, String, &'static str, Color32, Color32) {
if image_path.is_empty() {
return (
"Choose an Arch ISO".to_owned(),
"Open Image Vault, paste a path, or drag a .iso/.img file here.".to_owned(),
"WAITING",
Color32::from_gray(140),
Color32::from_gray(42),
);
}
let path = Path::new(image_path);
if !supported_image_extension(path) {
return (
file_name_or_path(path),
"Expected a .iso or .img boot image.".to_owned(),
"CHECK EXT",
Color32::from_gray(210),
Color32::from_gray(112),
);
}
match fs::metadata(path) {
Ok(metadata) if metadata.is_file() => {
let name = file_name_or_path(path);
let folder = path
.parent()
.map(|parent| format_path_display(parent))
.unwrap_or_else(|| "CURRENT FOLDER".to_owned());
let arch_hint = name.to_ascii_lowercase().contains("archlinux");
let badge = if arch_hint {
"ARCH READY"
} else {
"IMAGE READY"
};
(
name,
format!("{} // {}", human_size(metadata.len()), folder),
badge,
kd_green(),
Color32::from_rgba_premultiplied(255, 255, 255, (96.0 + pulse) as u8),
)
}
Ok(_) => (
file_name_or_path(path),
"That path exists, but it is not a file.".to_owned(),
"NOT FILE",
Color32::from_gray(210),
Color32::from_gray(112),
),
Err(_) => (
file_name_or_path(path),
"File not found yet. Pick an existing ISO before flashing.".to_owned(),
"MISSING",
Color32::from_gray(185),
Color32::from_gray(82),
),
}
}
fn readiness_panel(
ui: &mut egui::Ui,
target_selected: bool,
mode: OperationKind,
image_path: &str,
acknowledged: bool,
) {
egui::Frame::none()
.fill(Color32::from_rgb(4, 4, 4))
.stroke(Stroke::new(1.0, Color32::from_gray(38)))
.inner_margin(egui::Margin::same(12.0))
.show(ui, |ui| {
ui.label(
RichText::new("PRE-FLIGHT CHECK")
.monospace()
.size(11.0)
.strong()
.color(Color32::from_gray(172)),
);
ui.add_space(5.0);
readiness_row(ui, target_selected, "USB TARGET SELECTED");
if mode.needs_iso() {
readiness_row(ui, image_file_ready(image_path), "BOOT IMAGE SELECTED");
} else {
readiness_row(ui, true, "FORMAT PARAMETERS SET");
}
readiness_row(ui, acknowledged, "TARGET ACKNOWLEDGED");
readiness_row(ui, false, "FINAL CONFIRMATION REQUIRED ON SUBMIT");
});
}
fn readiness_row(ui: &mut egui::Ui, ready: bool, label: &str) {
ui.horizontal(|ui| {
let (rect, _) = ui.allocate_exact_size(Vec2::splat(12.0), Sense::hover());
ui.painter().circle_filled(
rect.center(),
4.5,
if ready {
kd_green()
} else {
Color32::from_gray(68)
},
);
ui.label(RichText::new(label).monospace().size(11.0).color(if ready {
Color32::from_gray(214)
} else {
Color32::from_gray(104)
}));
});
}
fn target_acknowledgement_box(
ui: &mut egui::Ui,
target: Option<&str>,
acknowledged: &mut bool,
disabled: bool,
) {
if target.is_none() {
*acknowledged = false;
}
egui::Frame::none()
.fill(Color32::from_rgb(4, 4, 4))
.stroke(Stroke::new(1.0, Color32::from_gray(48)))
.inner_margin(egui::Margin::same(12.0))
.show(ui, |ui| {
field_label(ui, "TARGET ACKNOWLEDGEMENT");
let message = match target {
Some(target) => format!(
"I understand {} is the selected device and data on it can be destroyed.",
target
),
None => "Select a USB target to enable this check.".to_owned(),
};
ui.add_enabled(
!disabled && target.is_some(),
egui::Checkbox::new(
acknowledged,
RichText::new(message.to_ascii_uppercase())
.monospace()
.size(11.0)
.color(Color32::from_gray(210)),
),
);
});
}
fn final_warning_card(ui: &mut egui::Ui, phase: f32) {
let (rect, _) = ui.allocate_exact_size(Vec2::new(ui.available_width(), 58.0), Sense::hover());
let pulse = ((phase * 7.0).sin() * 0.5 + 0.5) * 70.0;
ui.painter()
.rect_filled(rect, Rounding::same(1.0), Color32::from_rgb(10, 10, 10));
ui.painter().rect_stroke(
rect,
Rounding::ZERO,
Stroke::new(
1.0,
Color32::from_rgba_premultiplied(255, 255, 255, (95.0 + pulse) as u8),
),
);
draw_corner_brackets(
ui.painter(),
rect.shrink(2.0),
13.0,
Color32::from_gray(100),
);
ui.painter().text(
rect.center(),
Align2::CENTER_CENTER,
"LAST GATE BEFORE DESTRUCTIVE WRITE",
FontId::monospace(13.0),
Color32::from_gray(236),
);
}
fn animated_progress(ui: &mut egui::Ui, copied: u64, total: u64, phase: f32) {
let percent = if total == 0 {
0.0
} else {
copied as f32 / total as f32
}
.clamp(0.0, 1.0);
let (rect, _) = ui.allocate_exact_size(Vec2::new(ui.available_width(), 38.0), Sense::hover());
draw_progress_bar(
ui.painter(),
rect,
percent,
phase,
&progress_label(copied, total),
);
}
fn indeterminate_progress(ui: &mut egui::Ui, phase: f32) {
let value = ((phase * 2.4).sin() * 0.5 + 0.5).clamp(0.04, 0.96);
let (rect, _) = ui.allocate_exact_size(Vec2::new(ui.available_width(), 38.0), Sense::hover());
draw_progress_bar(ui.painter(), rect, value, phase, "WORKING // WAIT FOR SYNC");
}
fn draw_progress_bar(painter: &egui::Painter, rect: Rect, value: f32, phase: f32, label: &str) {
painter.rect_filled(rect, Rounding::same(1.0), kd_surface());
painter.rect_stroke(
rect,
Rounding::ZERO,
Stroke::new(1.0, Color32::from_gray(58)),
);
let fill_rect = Rect::from_min_max(
rect.left_top(),
Pos2::new(rect.left() + rect.width() * value, rect.bottom()),
);
painter.rect_filled(fill_rect, Rounding::same(1.0), Color32::from_gray(210));
let shimmer_x = rect.left() + rect.width() * ((phase * 0.75).fract());
painter.line_segment(
[
Pos2::new(shimmer_x, rect.top()),
Pos2::new(shimmer_x + 34.0, rect.bottom()),
],
Stroke::new(2.0, Color32::from_rgba_premultiplied(255, 255, 255, 110)),
);
painter.text(
rect.center(),
Align2::CENTER_CENTER,
label.to_ascii_uppercase(),
FontId::monospace(11.0),
if value > 0.48 {
Color32::BLACK
} else {
Color32::from_gray(210)
},
);
}
fn log_panel(ui: &mut egui::Ui, logs: &[String], compact: bool) {
field_label(ui, "LIVE TELEMETRY");
egui::Frame::none()
.fill(Color32::from_rgb(2, 2, 2))
.stroke(Stroke::new(1.0, Color32::from_gray(34)))
.inner_margin(egui::Margin::same(12.0))
.show(ui, |ui| {
egui::ScrollArea::vertical()
.max_height(if compact { 105.0 } else { 150.0 })
.stick_to_bottom(true)
.show(ui, |ui| {
if logs.is_empty() {
ui.label(
RichText::new("TELEMETRY STREAM IDLE")
.monospace()
.size(11.0)
.color(Color32::from_gray(82)),
);
}
for line in logs {
ui.label(
RichText::new(format!("> {line}"))
.monospace()
.size(11.0)
.color(Color32::from_gray(176)),
);
}
});
});
}
fn warning_text(ui: &mut egui::Ui, text: &str) {
ui.label(
RichText::new(text)
.monospace()
.size(11.0)
.color(Color32::from_gray(192)),
);
}
fn empty_state(ui: &mut egui::Ui, title: &str, description: &str) {
egui::Frame::none()
.fill(kd_surface())
.stroke(Stroke::new(1.0, Color32::from_gray(42)))
.inner_margin(egui::Margin::same(18.0))
.show(ui, |ui| {
ui.vertical_centered(|ui| {
ui.add_space(28.0);
ui.label(
RichText::new(title)
.size(22.0)
.strong()
.color(Color32::from_gray(234)),
);
ui.add_space(8.0);
ui.label(
RichText::new(description)
.monospace()
.size(11.0)
.color(Color32::from_gray(126)),
);
ui.add_space(28.0);
});
});
}
fn image_browser_contents(
ui: &mut egui::Ui,
state: &ImageBrowserState,
phase: f32,
) -> BrowserAction {
let mut action = BrowserAction::None;
panel_heading(ui, "IMAGE VAULT", "SELECT BOOT IMAGE");
ui.add_space(10.0);
let path_text = format_path_display(&state.current_dir);
tech_note(ui, "CURRENT DIRECTORY", &path_text);
ui.add_space(8.0);
ui.horizontal(|ui| {
if ui
.add_enabled(
state.current_dir.parent().is_some(),
kd_button("PARENT", false),
)
.clicked()
{
action = BrowserAction::Parent;
}
if ui.add(kd_button("REFRESH", false)).clicked() {
action = BrowserAction::Refresh;
}
if ui.add(kd_button("CLOSE", false)).clicked() {
action = BrowserAction::Close;
}
});
ui.add_space(10.0);
if let Some(error) = &state.error {
warning_text(ui, &format!("DIRECTORY READ FAILED // {error}"));
return action;
}
egui::Frame::none()
.fill(Color32::from_rgb(2, 2, 2))
.stroke(Stroke::new(1.0, Color32::from_gray(38)))
.inner_margin(egui::Margin::same(10.0))
.show(ui, |ui| {
egui::ScrollArea::vertical()
.max_height(350.0)
.auto_shrink([false, false])
.show(ui, |ui| {
if state.entries.is_empty() {
ui.label(
RichText::new("NO FILES IN DIRECTORY")
.monospace()
.size(11.0)
.color(Color32::from_gray(96)),
);
}
for entry in &state.entries {
let response = browser_entry_row(ui, entry, phase);
if response.clicked() {
match entry.kind {
BrowserEntryKind::Directory => {
action = BrowserAction::OpenDirectory(entry.path.clone());
}
BrowserEntryKind::Image => {
action = BrowserAction::SelectImage(entry.path.clone());
}
BrowserEntryKind::Other => {}
}
}
ui.add_space(6.0);
}
});
});
action
}
fn browser_entry_row(ui: &mut egui::Ui, entry: &BrowserEntry, phase: f32) -> egui::Response {
let clickable = entry.kind != BrowserEntryKind::Other;
let (rect, response) = ui.allocate_exact_size(
Vec2::new(ui.available_width(), 42.0),
if clickable {
Sense::click()
} else {
Sense::hover()
},
);
let fill = if response.hovered() && clickable {
Color32::from_rgb(20, 20, 20)
} else {
kd_surface()
};
ui.painter().rect_filled(rect, Rounding::same(1.0), fill);
ui.painter().rect_stroke(
rect,
Rounding::ZERO,
Stroke::new(1.0, Color32::from_gray(36)),
);
let kind = match entry.kind {
BrowserEntryKind::Directory => "DIR",
BrowserEntryKind::Image => "ISO",
BrowserEntryKind::Other => "---",
};
let color = match entry.kind {
BrowserEntryKind::Directory => Color32::from_gray(215),
BrowserEntryKind::Image => kd_green(),
BrowserEntryKind::Other => Color32::from_gray(74),
};
let pulse = ((phase * 4.0).sin() * 0.5 + 0.5) * 35.0;
ui.painter().text(
rect.left_center() + egui::vec2(12.0, 0.0),
Align2::LEFT_CENTER,
kind,
FontId::monospace(10.0),
if entry.kind == BrowserEntryKind::Image {
Color32::from_rgba_premultiplied(0, 255, 136, (160.0 + pulse) as u8)
} else {
color
},
);
ui.painter().text(
rect.left_center() + egui::vec2(58.0, 0.0),
Align2::LEFT_CENTER,
entry.name.to_ascii_uppercase(),
FontId::monospace(11.0),
color,
);
response
}
fn image_file_ready(image_path: &str) -> bool {
let image_path = image_path.trim();
if image_path.is_empty() {
return false;
}
let path = Path::new(image_path);
supported_image_extension(path)
&& fs::metadata(path)
.map(|metadata| metadata.is_file())
.unwrap_or(false)
}
fn supported_image_extension(path: &Path) -> bool {
matches!(
path.extension()
.and_then(|extension| extension.to_str())
.map(|extension| extension.to_ascii_lowercase()),
Some(extension) if extension == "iso" || extension == "img"
)
}
fn file_name_or_path(path: &Path) -> String {
path.file_name()
.and_then(|name| name.to_str())
.map(str::to_owned)
.unwrap_or_else(|| path.display().to_string())
}
fn format_path_display(path: &Path) -> String {
let display = path.display().to_string();
if let Some(home) = env::var_os("HOME").map(PathBuf::from) {
if let Ok(stripped) = path.strip_prefix(&home) {
if stripped.as_os_str().is_empty() {
return "~".to_owned();
}
return format!("~/{}", stripped.display());
}
}
display
}
fn initial_browser_directory(current_image_path: &str) -> PathBuf {
let current_image_path = current_image_path.trim();
if !current_image_path.is_empty() {
let path = Path::new(current_image_path);
if path.is_dir() {
return path.to_path_buf();
}
if let Some(parent) = path.parent().filter(|parent| parent.is_dir()) {
return parent.to_path_buf();
}
}
if let Some(home) = env::var_os("HOME")
.map(PathBuf::from)
.filter(|path| path.is_dir())
{
return home;
}
env::current_dir().unwrap_or_else(|_| PathBuf::from("/"))
}
fn scan_browser_directory(path: &Path) -> std::io::Result<Vec<BrowserEntry>> {
let mut entries = Vec::new();
for entry in fs::read_dir(path)? {
let entry = match entry {
Ok(entry) => entry,
Err(_) => continue,
};
let entry_path = entry.path();
let name = entry.file_name().to_string_lossy().to_string();
let kind = match entry.file_type() {
Ok(file_type) if file_type.is_dir() => BrowserEntryKind::Directory,
Ok(file_type) if file_type.is_file() && supported_image_extension(&entry_path) => {
BrowserEntryKind::Image
}
Ok(file_type) if file_type.is_file() => BrowserEntryKind::Other,
_ => continue,
};
entries.push(BrowserEntry {
name,
path: entry_path,
kind,
});
}
entries.sort_by(|left, right| {
left.kind.cmp(&right.kind).then_with(|| {
left.name
.to_ascii_lowercase()
.cmp(&right.name.to_ascii_lowercase())
})
});
Ok(entries)
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::{tempdir, NamedTempFile};
#[test]
fn image_ready_requires_existing_supported_file() {
let mut iso = NamedTempFile::with_suffix(".ISO").expect("temp iso");
writeln!(iso, "fake iso").expect("write temp iso");
let txt = NamedTempFile::with_suffix(".txt").expect("temp text file");
assert!(image_file_ready(&iso.path().display().to_string()));
assert!(!image_file_ready(&txt.path().display().to_string()));
assert!(!image_file_ready("/definitely/missing/archlinux.iso"));
assert!(!image_file_ready(""));
}
#[test]
fn initial_browser_directory_uses_current_file_parent_or_directory() {
let dir = tempdir().expect("temp dir");
let iso_path = dir.path().join("archlinux.iso");
fs::write(&iso_path, b"fake iso").expect("write iso");
assert_eq!(
initial_browser_directory(&iso_path.display().to_string()),
dir.path().to_path_buf()
);
assert_eq!(
initial_browser_directory(&dir.path().display().to_string()),
dir.path().to_path_buf()
);
}
#[test]
fn image_preview_state_reports_missing_and_unsupported_paths() {
assert_eq!(
image_preview_state("/tmp/motte-not-real.iso", 0.0).2,
"MISSING"
);
assert_eq!(
image_preview_state("/tmp/not-an-image.txt", 0.0).2,
"CHECK EXT"
);
}
#[test]
fn scan_browser_directory_prioritizes_folders_then_images() {
let dir = tempdir().expect("temp dir");
fs::create_dir(dir.path().join("z_folder")).expect("create folder");
fs::write(dir.path().join("archlinux.iso"), b"fake iso").expect("write iso");
fs::write(dir.path().join("notes.txt"), b"notes").expect("write notes");
let entries = scan_browser_directory(dir.path()).expect("scan dir");
let kinds = entries.iter().map(|entry| entry.kind).collect::<Vec<_>>();
assert_eq!(
kinds,
vec![
BrowserEntryKind::Directory,
BrowserEntryKind::Image,
BrowserEntryKind::Other
]
);
}
}