par-term-settings-ui 0.20.0

Settings UI for par-term terminal emulator
Documentation
//! Session Logging section for the advanced settings tab.
//!
//! Covers: auto-log enable, log format, log directory, redact passwords.

use crate::SettingsUI;
use crate::search::SearchTag;
use crate::section::keyword_section;
use par_term_config::SessionLogFormat;
use std::collections::HashSet;

pub(crate) fn show_logging_section(
    ui: &mut egui::Ui,
    settings: &mut SettingsUI,
    changes_this_frame: &mut bool,
    collapsed: &mut HashSet<String>,
) {
    keyword_section(
        ui,
        "Output Recording",
        "advanced_logging",
        &[
            "session logging",
            "asciicast",
            "asciinema",
            "plain text",
            "html",
            "auto-log",
            "log directory",
            "sensitive",
            "credentials",
        ],
        true,
        collapsed,
        |ui| {
            ui.label(
                "Automatically record terminal output for later review, debugging, or sharing.",
            );
            ui.add_space(8.0);

            let mut auto_log = settings.config.session_log.auto_log_sessions;
            if ui
                .checkbox(&mut auto_log, "Record every tab's output automatically")
                .search_tag(&["auto_log_sessions"])
                .on_hover_text("When enabled, all terminal output is logged to files")
                .changed()
            {
                settings.config.session_log.auto_log_sessions = auto_log;
                settings.has_changes = true;
                *changes_this_frame = true;
            }

            ui.add_space(8.0);

            ui.horizontal(|ui| {
                ui.label("Log format:");

                let current_format = settings.config.session_log.session_log_format;
                let format_name = current_format.display_name();

                let format_combo = egui::ComboBox::from_id_salt("advanced_session_log_format")
                    .width(180.0)
                    .selected_text(format_name)
                    .show_ui(ui, |ui| {
                        for format in SessionLogFormat::all() {
                            if ui
                                .selectable_label(current_format == *format, format.display_name())
                                .clicked()
                                && current_format != *format
                            {
                                settings.config.session_log.session_log_format = *format;
                                settings.has_changes = true;
                                *changes_this_frame = true;
                            }
                        }
                    });
                format_combo.response.search_tag(&["session_log_format"]);
            });

            ui.add_space(4.0);
            ui.label(
                egui::RichText::new(match settings.config.session_log.session_log_format {
                    SessionLogFormat::Plain => {
                        "Plain text without escape sequences - smallest files"
                    }
                    SessionLogFormat::Html => "HTML with colors preserved - viewable in browser",
                    SessionLogFormat::Asciicast => "asciinema format - can be replayed or shared",
                    SessionLogFormat::AsciicastV3 => {
                        "asciinema v3 - adds inline graphics; image pixels bypass credential redaction"
                    }
                })
                .weak(),
            );

            ui.add_space(8.0);

            ui.horizontal(|ui| {
                ui.label("Log directory:");
                let mut dir = settings.config.session_log.session_log_directory.clone();
                let response = ui
                    .add(
                        egui::TextEdit::singleline(&mut dir)
                            .desired_width(300.0)
                            .hint_text("~/.local/share/par-term/logs/"),
                    )
                    .search_tag(&["session_log_directory"]);
                if response.changed() {
                    settings.config.session_log.session_log_directory = dir;
                    settings.has_changes = true;
                    *changes_this_frame = true;
                }
            });

            let resolved_path = settings.config.logs_dir();
            ui.label(
                egui::RichText::new(format!("Resolved: {}", resolved_path.display()))
                    .weak()
                    .small(),
            );

            ui.add_space(8.0);

            let mut redact = settings.config.session_log.session_log_redact_passwords;
            if ui
                .checkbox(&mut redact, "Redact passwords in recordings")
                .search_tag(&["session_log_redact_passwords"])
                .on_hover_text(
                    "Detects password prompts (sudo, ssh, etc.) and replaces \
                     keyboard input with a redaction marker. Prevents passwords \
                     from being written to output recordings on disk.",
                )
                .changed()
            {
                settings.config.session_log.session_log_redact_passwords = redact;
                settings.has_changes = true;
                *changes_this_frame = true;
            }

            if !settings.config.session_log.session_log_redact_passwords {
                ui.label(
                    egui::RichText::new(
                        "\u{26a0} Warning: Recordings may contain passwords and credentials",
                    )
                    .color(egui::Color32::from_rgb(255, 193, 7))
                    .small(),
                );
            }
        },
    );
}