use crate::app::App;
use crate::ui::theme::{CATPPUCCIN_MOCHA, mocha};
use crate::ui::tr;
use ratatui::{
Frame,
layout::{Constraint, Direction, Layout, Rect},
style::{Modifier, Style},
text::{Line, Span},
widgets::{Block, BorderType, Borders, Paragraph, Row, Table, Wrap},
};
pub fn draw(f: &mut Frame, app: &App, area: Rect) {
let lang = &app.tool_config.language;
let config_block = Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(CATPPUCCIN_MOCHA.border))
.title(Span::styled(
tr("config_title", lang),
Style::default()
.fg(CATPPUCCIN_MOCHA.text)
.add_modifier(Modifier::BOLD),
));
let block_inner = config_block.inner(area);
f.render_widget(config_block, area);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(10), Constraint::Length(6)])
.split(block_inner);
let cfg = &app.config;
let fields = vec![
(tr("config_proj_name", lang), cfg.name.clone()),
(tr("config_chip_type", lang), cfg.chip_type.clone()),
(tr("config_baud_rate", lang), cfg.baud_rate.to_string()),
(tr("config_flash_mode", lang), cfg.flash_mode.clone()),
(tr("config_flash_freq", lang), cfg.flash_freq.clone()),
(tr("config_flash_size", lang), cfg.flash_size.clone()),
(
tr("config_bootloader_offset", lang),
cfg.bootloader_offset.clone(),
),
(
tr("config_bootloader_path", lang),
cfg.bootloader_path.clone(),
),
(
tr("config_partitions_offset", lang),
cfg.partitions_offset.clone(),
),
(
tr("config_partitions_path", lang),
cfg.partitions_path.clone(),
),
(
tr("config_otadata_offset", lang),
cfg.otadata_offset.clone(),
),
(tr("config_otadata_path", lang), cfg.otadata_path.clone()),
(tr("config_app_offset", lang), cfg.app_offset.clone()),
(tr("config_app_path", lang), cfg.app_path.clone()),
(tr("config_nvs_offset", lang), cfg.nvs_offset.clone()),
(tr("config_verify_method", lang), cfg.verify_method.clone()),
(tr("config_blank_check", lang), cfg.blank_check.to_string()),
(tr("config_erase_mode", lang), cfg.erase_mode.clone()),
(
tr("config_incremental_programming", lang),
cfg.incremental_programming.to_string(),
),
(tr("config_secure_boot", lang), cfg.secure_boot.to_string()),
(
tr("config_flash_encryption", lang),
cfg.flash_encryption.to_string(),
),
(
tr("config_lock_after_flash", lang),
cfg.lock_after_flash.to_string(),
),
(tr("config_operator_role", lang), cfg.operator_role.clone()),
(
tr("config_firmware_version", lang),
cfg.firmware_version.clone(),
),
(tr("config_sn_prefix", lang), cfg.sn_prefix.clone()),
(tr("config_lot_code", lang), cfg.lot_code.clone()),
(tr("config_mes_endpoint", lang), cfg.mes_endpoint.clone()),
(
tr("config_label_template", lang),
cfg.label_template.clone(),
),
(
tr("config_qa_test_script", lang),
cfg.qa_test_script.clone(),
),
(
tr("config_do_not_chg_bin", lang),
cfg.do_not_chg_bin.to_string(),
),
];
let mut rows = Vec::new();
for (i, (label, val)) in fields.iter().enumerate() {
let is_selected = app.selected_config_field == i;
let label_span = Span::styled(
*label,
if is_selected {
Style::default()
.fg(CATPPUCCIN_MOCHA.accent)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(CATPPUCCIN_MOCHA.text_muted)
},
);
let val_span = if is_selected {
if app.is_editing_config {
Span::styled(
format!("{}â–ˆ", app.edit_buffer), Style::default()
.fg(CATPPUCCIN_MOCHA.accent)
.add_modifier(Modifier::BOLD),
)
} else {
let color = if app.admin_mode {
CATPPUCCIN_MOCHA.accent
} else {
CATPPUCCIN_MOCHA.primary
};
Span::styled(val, Style::default().fg(color).add_modifier(Modifier::BOLD))
}
} else {
Span::styled(val, Style::default().fg(CATPPUCCIN_MOCHA.text))
};
let row_bg = if is_selected {
mocha::SURFACE0
} else {
mocha::BASE
};
rows.push(
Row::new(vec![table_cell(label_span), table_cell(val_span)])
.style(Style::default().bg(row_bg)),
);
}
let widths = [Constraint::Length(22), Constraint::Min(20)];
let table = Table::new(rows, widths)
.block(Block::default().borders(Borders::NONE))
.style(Style::default().bg(mocha::BASE));
f.render_widget(table, chunks[0]);
let selected_idx = app
.selected_config_field
.min(fields.len().saturating_sub(1));
let (selected_label, selected_val) = &fields[selected_idx];
let display_val = if app.is_editing_config {
app.edit_buffer.clone()
} else {
selected_val.clone()
};
let inspector_block = Block::default()
.borders(Borders::TOP)
.border_style(Style::default().fg(CATPPUCCIN_MOCHA.border))
.title(Span::styled(
tr("config_inspector_title", lang),
Style::default()
.fg(CATPPUCCIN_MOCHA.accent)
.add_modifier(Modifier::BOLD),
));
let mut inspector_lines = Vec::new();
let clean_label = selected_label.trim_end_matches(':');
let status_span = if app.admin_mode {
if app.is_editing_config {
Span::styled(
tr("config_status_editing", lang),
Style::default()
.fg(CATPPUCCIN_MOCHA.accent)
.add_modifier(Modifier::BOLD),
)
} else {
Span::styled(
tr("config_status_unlocked", lang),
Style::default()
.fg(CATPPUCCIN_MOCHA.success)
.add_modifier(Modifier::BOLD),
)
}
} else {
Span::styled(
tr("config_status_locked", lang),
Style::default()
.fg(CATPPUCCIN_MOCHA.danger)
.add_modifier(Modifier::BOLD),
)
};
inspector_lines.push(Line::from(vec![
Span::styled(
tr("config_parameter", lang),
Style::default().fg(CATPPUCCIN_MOCHA.text_muted),
),
Span::styled(
clean_label,
Style::default()
.fg(CATPPUCCIN_MOCHA.text)
.add_modifier(Modifier::BOLD),
),
Span::styled(
tr("config_status", lang),
Style::default().fg(CATPPUCCIN_MOCHA.text_muted),
),
status_span,
]));
inspector_lines.push(Line::from(""));
let val_color = if app.is_editing_config {
CATPPUCCIN_MOCHA.accent
} else if app.admin_mode {
CATPPUCCIN_MOCHA.success
} else {
CATPPUCCIN_MOCHA.primary
};
inspector_lines.push(Line::from(vec![
Span::styled(
tr("config_value", lang),
Style::default().fg(CATPPUCCIN_MOCHA.text_muted),
),
Span::styled(
display_val,
Style::default().fg(val_color).add_modifier(Modifier::BOLD),
),
]));
inspector_lines.push(Line::from(""));
let guide_spans = if !app.admin_mode {
vec![
Span::styled(
tr("config_guide", lang),
Style::default().fg(CATPPUCCIN_MOCHA.primary),
),
Span::styled(
tr("config_guide_locked", lang),
Style::default().fg(CATPPUCCIN_MOCHA.text_muted),
),
]
} else if app.is_editing_config {
vec![
Span::styled(
tr("config_guide", lang),
Style::default().fg(CATPPUCCIN_MOCHA.accent),
),
Span::styled(
tr("config_guide_editing", lang),
Style::default().fg(CATPPUCCIN_MOCHA.text_muted),
),
]
} else {
vec![
Span::styled(
tr("config_guide", lang),
Style::default().fg(CATPPUCCIN_MOCHA.success),
),
Span::styled(
tr("config_guide_unlocked", lang),
Style::default().fg(CATPPUCCIN_MOCHA.text_muted),
),
]
};
inspector_lines.push(Line::from(guide_spans));
let admin_help = Paragraph::new(inspector_lines)
.block(inspector_block)
.wrap(Wrap { trim: true });
f.render_widget(admin_help, chunks[1]);
}
fn table_cell<'a>(span: Span<'a>) -> Line<'a> {
Line::from(span)
}