Hide backup panel when no backup data is present
- Add has_data() method to BackupWidget to check if backup metrics exist - Modify dashboard layout to conditionally show backup panel only when data exists - When no backup data: system panel takes full left side height - When backup data exists: system and backup panels share left side equally Prevents empty backup panel from taking up screen space unnecessarily.
This commit is contained in:
parent
8023da2c1e
commit
d4531ef2e8
@ -275,21 +275,40 @@ impl TuiApp {
|
|||||||
])
|
])
|
||||||
.split(main_chunks[1]);
|
.split(main_chunks[1]);
|
||||||
|
|
||||||
// Left side: system on top, backup on bottom (equal height)
|
// Check if backup panel should be shown
|
||||||
let left_chunks = ratatui::layout::Layout::default()
|
let show_backup = if let Some(hostname) = self.current_host.clone() {
|
||||||
.direction(Direction::Vertical)
|
let host_widgets = self.get_or_create_host_widgets(&hostname);
|
||||||
.constraints([
|
host_widgets.backup_widget.has_data()
|
||||||
Constraint::Percentage(ThemeLayout::SYSTEM_PANEL_HEIGHT), // System section
|
} else {
|
||||||
Constraint::Percentage(ThemeLayout::BACKUP_PANEL_HEIGHT), // Backup section
|
false
|
||||||
])
|
};
|
||||||
.split(content_chunks[0]);
|
|
||||||
|
// Left side: dynamic layout based on backup data availability
|
||||||
|
let left_chunks = if show_backup {
|
||||||
|
// Show both system and backup panels
|
||||||
|
ratatui::layout::Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Percentage(ThemeLayout::SYSTEM_PANEL_HEIGHT), // System section
|
||||||
|
Constraint::Percentage(ThemeLayout::BACKUP_PANEL_HEIGHT), // Backup section
|
||||||
|
])
|
||||||
|
.split(content_chunks[0])
|
||||||
|
} else {
|
||||||
|
// Show only system panel (full height)
|
||||||
|
ratatui::layout::Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([Constraint::Percentage(100)]) // System section takes full height
|
||||||
|
.split(content_chunks[0])
|
||||||
|
};
|
||||||
|
|
||||||
// Render title bar
|
// Render title bar
|
||||||
self.render_btop_title(frame, main_chunks[0], metric_store);
|
self.render_btop_title(frame, main_chunks[0], metric_store);
|
||||||
|
|
||||||
// Render new panel layout
|
// Render new panel layout
|
||||||
self.render_system_panel(frame, left_chunks[0], metric_store);
|
self.render_system_panel(frame, left_chunks[0], metric_store);
|
||||||
self.render_backup_panel(frame, left_chunks[1]);
|
if show_backup && left_chunks.len() > 1 {
|
||||||
|
self.render_backup_panel(frame, left_chunks[1]);
|
||||||
|
}
|
||||||
|
|
||||||
// Render services widget for current host
|
// Render services widget for current host
|
||||||
if let Some(hostname) = self.current_host.clone() {
|
if let Some(hostname) = self.current_host.clone() {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use ratatui::{
|
|||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use super::Widget;
|
use super::Widget;
|
||||||
use crate::ui::theme::{Typography, StatusIcons};
|
use crate::ui::theme::{StatusIcons, Typography};
|
||||||
|
|
||||||
/// Backup widget displaying backup status, services, and repository information
|
/// Backup widget displaying backup status, services, and repository information
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -73,7 +73,13 @@ impl BackupWidget {
|
|||||||
has_data: false,
|
has_data: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the backup widget has any data to display
|
||||||
|
pub fn has_data(&self) -> bool {
|
||||||
|
self.has_data
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Format duration for display
|
/// Format duration for display
|
||||||
fn format_duration(&self) -> String {
|
fn format_duration(&self) -> String {
|
||||||
match self.duration_seconds {
|
match self.duration_seconds {
|
||||||
@ -89,7 +95,7 @@ impl BackupWidget {
|
|||||||
None => "—".to_string(),
|
None => "—".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format timestamp for display
|
/// Format timestamp for display
|
||||||
fn format_last_run(&self) -> String {
|
fn format_last_run(&self) -> String {
|
||||||
match self.last_run_timestamp {
|
match self.last_run_timestamp {
|
||||||
@ -106,8 +112,7 @@ impl BackupWidget {
|
|||||||
None => "—".to_string(),
|
None => "—".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Format disk usage in format "usedGB/totalGB"
|
/// Format disk usage in format "usedGB/totalGB"
|
||||||
fn format_repo_size(&self) -> String {
|
fn format_repo_size(&self) -> String {
|
||||||
match (self.backup_disk_used_gb, self.backup_disk_total_gb) {
|
match (self.backup_disk_used_gb, self.backup_disk_total_gb) {
|
||||||
@ -123,7 +128,7 @@ impl BackupWidget {
|
|||||||
_ => "—".to_string(),
|
_ => "—".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format size with proper units (xxxkB/MB/GB/TB)
|
/// Format size with proper units (xxxkB/MB/GB/TB)
|
||||||
fn format_size_with_proper_units(size_gb: f32) -> String {
|
fn format_size_with_proper_units(size_gb: f32) -> String {
|
||||||
if size_gb >= 1000.0 {
|
if size_gb >= 1000.0 {
|
||||||
@ -137,7 +142,7 @@ impl BackupWidget {
|
|||||||
let size_mb = size_gb * 1024.0;
|
let size_mb = size_gb * 1024.0;
|
||||||
format!("{:.1}MB", size_mb)
|
format!("{:.1}MB", size_mb)
|
||||||
} else if size_gb >= 0.000001 {
|
} else if size_gb >= 0.000001 {
|
||||||
// kB range (size_gb * 1024 * 1024 = kB)
|
// kB range (size_gb * 1024 * 1024 = kB)
|
||||||
let size_kb = size_gb * 1024.0 * 1024.0;
|
let size_kb = size_gb * 1024.0 * 1024.0;
|
||||||
format!("{:.0}kB", size_kb)
|
format!("{:.0}kB", size_kb)
|
||||||
} else {
|
} else {
|
||||||
@ -146,9 +151,7 @@ impl BackupWidget {
|
|||||||
format!("{:.0}B", size_bytes)
|
format!("{:.0}B", size_bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Format product name display
|
/// Format product name display
|
||||||
fn format_product_name(&self) -> String {
|
fn format_product_name(&self) -> String {
|
||||||
if let Some(ref product_name) = self.backup_disk_product_name {
|
if let Some(ref product_name) = self.backup_disk_product_name {
|
||||||
@ -157,7 +160,7 @@ impl BackupWidget {
|
|||||||
"P/N: Unknown".to_string()
|
"P/N: Unknown".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format serial number display
|
/// Format serial number display
|
||||||
fn format_serial_number(&self) -> String {
|
fn format_serial_number(&self) -> String {
|
||||||
if let Some(ref serial) = self.backup_disk_serial_number {
|
if let Some(ref serial) = self.backup_disk_serial_number {
|
||||||
@ -166,12 +169,12 @@ impl BackupWidget {
|
|||||||
"S/N: Unknown".to_string()
|
"S/N: Unknown".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract service name from metric name (e.g., "backup_service_gitea_status" -> "gitea")
|
/// Extract service name from metric name (e.g., "backup_service_gitea_status" -> "gitea")
|
||||||
fn extract_service_name(metric_name: &str) -> Option<String> {
|
fn extract_service_name(metric_name: &str) -> Option<String> {
|
||||||
if metric_name.starts_with("backup_service_") {
|
if metric_name.starts_with("backup_service_") {
|
||||||
let name_part = &metric_name[15..]; // Remove "backup_service_" prefix
|
let name_part = &metric_name[15..]; // Remove "backup_service_" prefix
|
||||||
|
|
||||||
// Try to extract service name by removing known suffixes
|
// Try to extract service name by removing known suffixes
|
||||||
if let Some(service_name) = name_part.strip_suffix("_status") {
|
if let Some(service_name) = name_part.strip_suffix("_status") {
|
||||||
Some(service_name.to_string())
|
Some(service_name.to_string())
|
||||||
@ -196,23 +199,31 @@ impl Widget for BackupWidget {
|
|||||||
fn update_from_metrics(&mut self, metrics: &[&Metric]) {
|
fn update_from_metrics(&mut self, metrics: &[&Metric]) {
|
||||||
debug!("Backup widget updating with {} metrics", metrics.len());
|
debug!("Backup widget updating with {} metrics", metrics.len());
|
||||||
for metric in metrics {
|
for metric in metrics {
|
||||||
debug!("Backup metric: {} = {:?} (status: {:?})", metric.name, metric.value, metric.status);
|
debug!(
|
||||||
|
"Backup metric: {} = {:?} (status: {:?})",
|
||||||
|
metric.name, metric.value, metric.status
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also debug the service_data after processing
|
// Also debug the service_data after processing
|
||||||
debug!("Processing individual service metrics...");
|
debug!("Processing individual service metrics...");
|
||||||
|
|
||||||
// Log how many metrics are backup service metrics
|
// Log how many metrics are backup service metrics
|
||||||
let service_metric_count = metrics.iter()
|
let service_metric_count = metrics
|
||||||
|
.iter()
|
||||||
.filter(|m| m.name.starts_with("backup_service_"))
|
.filter(|m| m.name.starts_with("backup_service_"))
|
||||||
.count();
|
.count();
|
||||||
debug!("Found {} backup_service_ metrics out of {} total backup metrics",
|
debug!(
|
||||||
service_metric_count, metrics.len());
|
"Found {} backup_service_ metrics out of {} total backup metrics",
|
||||||
|
service_metric_count,
|
||||||
|
metrics.len()
|
||||||
|
);
|
||||||
|
|
||||||
// Reset service metrics
|
// Reset service metrics
|
||||||
self.service_metrics.clear();
|
self.service_metrics.clear();
|
||||||
let mut service_data: std::collections::HashMap<String, ServiceMetricData> = std::collections::HashMap::new();
|
let mut service_data: std::collections::HashMap<String, ServiceMetricData> =
|
||||||
|
std::collections::HashMap::new();
|
||||||
|
|
||||||
for metric in metrics {
|
for metric in metrics {
|
||||||
match metric.name.as_str() {
|
match metric.name.as_str() {
|
||||||
"backup_overall_status" => {
|
"backup_overall_status" => {
|
||||||
@ -263,15 +274,20 @@ impl Widget for BackupWidget {
|
|||||||
_ => {
|
_ => {
|
||||||
// Handle individual service metrics
|
// Handle individual service metrics
|
||||||
if let Some(service_name) = Self::extract_service_name(&metric.name) {
|
if let Some(service_name) = Self::extract_service_name(&metric.name) {
|
||||||
debug!("Extracted service name '{}' from metric '{}'", service_name, metric.name);
|
debug!(
|
||||||
let entry = service_data.entry(service_name.clone()).or_insert_with(|| ServiceMetricData {
|
"Extracted service name '{}' from metric '{}'",
|
||||||
name: service_name,
|
service_name, metric.name
|
||||||
status: Status::Unknown,
|
);
|
||||||
exit_code: None,
|
let entry = service_data.entry(service_name.clone()).or_insert_with(|| {
|
||||||
archive_count: None,
|
ServiceMetricData {
|
||||||
repo_size_gb: None,
|
name: service_name,
|
||||||
|
status: Status::Unknown,
|
||||||
|
exit_code: None,
|
||||||
|
archive_count: None,
|
||||||
|
repo_size_gb: None,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if metric.name.ends_with("_status") {
|
if metric.name.ends_with("_status") {
|
||||||
entry.status = metric.status;
|
entry.status = metric.status;
|
||||||
debug!("Set status for {}: {:?}", entry.name, entry.status);
|
debug!("Set status for {}: {:?}", entry.name, entry.status);
|
||||||
@ -279,35 +295,50 @@ impl Widget for BackupWidget {
|
|||||||
entry.exit_code = metric.value.as_i64();
|
entry.exit_code = metric.value.as_i64();
|
||||||
} else if metric.name.ends_with("_archive_count") {
|
} else if metric.name.ends_with("_archive_count") {
|
||||||
entry.archive_count = metric.value.as_i64();
|
entry.archive_count = metric.value.as_i64();
|
||||||
debug!("Set archive_count for {}: {:?}", entry.name, entry.archive_count);
|
debug!(
|
||||||
|
"Set archive_count for {}: {:?}",
|
||||||
|
entry.name, entry.archive_count
|
||||||
|
);
|
||||||
} else if metric.name.ends_with("_repo_size_gb") {
|
} else if metric.name.ends_with("_repo_size_gb") {
|
||||||
entry.repo_size_gb = metric.value.as_f32();
|
entry.repo_size_gb = metric.value.as_f32();
|
||||||
debug!("Set repo_size_gb for {}: {:?}", entry.name, entry.repo_size_gb);
|
debug!(
|
||||||
|
"Set repo_size_gb for {}: {:?}",
|
||||||
|
entry.name, entry.repo_size_gb
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!("Could not extract service name from metric: {}", metric.name);
|
debug!(
|
||||||
|
"Could not extract service name from metric: {}",
|
||||||
|
metric.name
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert service data to sorted vector
|
// Convert service data to sorted vector
|
||||||
let mut services: Vec<ServiceMetricData> = service_data.into_values().collect();
|
let mut services: Vec<ServiceMetricData> = service_data.into_values().collect();
|
||||||
services.sort_by(|a, b| a.name.cmp(&b.name));
|
services.sort_by(|a, b| a.name.cmp(&b.name));
|
||||||
self.service_metrics = services;
|
self.service_metrics = services;
|
||||||
|
|
||||||
self.has_data = !metrics.is_empty();
|
self.has_data = !metrics.is_empty();
|
||||||
|
|
||||||
debug!("Backup widget updated: status={:?}, services={}, total_size={:?}GB",
|
debug!(
|
||||||
self.overall_status, self.service_metrics.len(), self.total_repo_size_gb);
|
"Backup widget updated: status={:?}, services={}, total_size={:?}GB",
|
||||||
|
self.overall_status,
|
||||||
|
self.service_metrics.len(),
|
||||||
|
self.total_repo_size_gb
|
||||||
|
);
|
||||||
|
|
||||||
// Debug individual service data
|
// Debug individual service data
|
||||||
for service in &self.service_metrics {
|
for service in &self.service_metrics {
|
||||||
debug!("Service {}: status={:?}, archives={:?}, size={:?}GB",
|
debug!(
|
||||||
service.name, service.status, service.archive_count, service.repo_size_gb);
|
"Service {}: status={:?}, archives={:?}, size={:?}GB",
|
||||||
|
service.name, service.status, service.archive_count, service.repo_size_gb
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, frame: &mut Frame, area: Rect) {
|
fn render(&mut self, frame: &mut Frame, area: Rect) {
|
||||||
// Split area into header and services list
|
// Split area into header and services list
|
||||||
let chunks = Layout::default()
|
let chunks = Layout::default()
|
||||||
@ -317,10 +348,10 @@ impl Widget for BackupWidget {
|
|||||||
Constraint::Min(0), // Service list
|
Constraint::Min(0), // Service list
|
||||||
])
|
])
|
||||||
.split(area);
|
.split(area);
|
||||||
|
|
||||||
// Render backup overview
|
// Render backup overview
|
||||||
self.render_backup_overview(frame, chunks[0]);
|
self.render_backup_overview(frame, chunks[0]);
|
||||||
|
|
||||||
// Render services list
|
// Render services list
|
||||||
self.render_services_list(frame, chunks[1]);
|
self.render_services_list(frame, chunks[1]);
|
||||||
}
|
}
|
||||||
@ -341,14 +372,14 @@ impl BackupWidget {
|
|||||||
Constraint::Min(0), // Remaining space
|
Constraint::Min(0), // Remaining space
|
||||||
])
|
])
|
||||||
.split(area);
|
.split(area);
|
||||||
|
|
||||||
// "Latest backup" header
|
// "Latest backup" header
|
||||||
let header_para = Paragraph::new("Latest backup:")
|
let header_para = Paragraph::new("Latest backup:").style(Typography::widget_title());
|
||||||
.style(Typography::widget_title());
|
|
||||||
frame.render_widget(header_para, content_chunks[0]);
|
frame.render_widget(header_para, content_chunks[0]);
|
||||||
|
|
||||||
// Status line
|
// Status line
|
||||||
let status_text = format!("Status: {}",
|
let status_text = format!(
|
||||||
|
"Status: {}",
|
||||||
match self.overall_status {
|
match self.overall_status {
|
||||||
Status::Ok => "OK",
|
Status::Ok => "OK",
|
||||||
Status::Warning => "Warning",
|
Status::Warning => "Warning",
|
||||||
@ -359,72 +390,71 @@ impl BackupWidget {
|
|||||||
let status_spans = StatusIcons::create_status_spans(self.overall_status, &status_text);
|
let status_spans = StatusIcons::create_status_spans(self.overall_status, &status_text);
|
||||||
let status_para = Paragraph::new(ratatui::text::Line::from(status_spans));
|
let status_para = Paragraph::new(ratatui::text::Line::from(status_spans));
|
||||||
frame.render_widget(status_para, content_chunks[1]);
|
frame.render_widget(status_para, content_chunks[1]);
|
||||||
|
|
||||||
// Duration and last run
|
// Duration and last run
|
||||||
let time_text = format!("Duration: {} • Last: {}",
|
let time_text = format!(
|
||||||
self.format_duration(),
|
"Duration: {} • Last: {}",
|
||||||
|
self.format_duration(),
|
||||||
self.format_last_run()
|
self.format_last_run()
|
||||||
);
|
);
|
||||||
let time_para = Paragraph::new(time_text)
|
let time_para = Paragraph::new(time_text).style(Typography::secondary());
|
||||||
.style(Typography::secondary());
|
|
||||||
frame.render_widget(time_para, content_chunks[2]);
|
frame.render_widget(time_para, content_chunks[2]);
|
||||||
|
|
||||||
// Repository size
|
// Repository size
|
||||||
let size_text = format!("Disk usage: {}", self.format_repo_size());
|
let size_text = format!("Disk usage: {}", self.format_repo_size());
|
||||||
let size_para = Paragraph::new(size_text)
|
let size_para = Paragraph::new(size_text).style(Typography::secondary());
|
||||||
.style(Typography::secondary());
|
|
||||||
frame.render_widget(size_para, content_chunks[3]);
|
frame.render_widget(size_para, content_chunks[3]);
|
||||||
|
|
||||||
// Product name
|
// Product name
|
||||||
let product_text = self.format_product_name();
|
let product_text = self.format_product_name();
|
||||||
let product_para = Paragraph::new(product_text)
|
let product_para = Paragraph::new(product_text).style(Typography::secondary());
|
||||||
.style(Typography::secondary());
|
|
||||||
frame.render_widget(product_para, content_chunks[4]);
|
frame.render_widget(product_para, content_chunks[4]);
|
||||||
|
|
||||||
// Serial number
|
// Serial number
|
||||||
let serial_text = self.format_serial_number();
|
let serial_text = self.format_serial_number();
|
||||||
let serial_para = Paragraph::new(serial_text)
|
let serial_para = Paragraph::new(serial_text).style(Typography::secondary());
|
||||||
.style(Typography::secondary());
|
|
||||||
frame.render_widget(serial_para, content_chunks[5]);
|
frame.render_widget(serial_para, content_chunks[5]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render services list section
|
/// Render services list section
|
||||||
fn render_services_list(&self, frame: &mut Frame, area: Rect) {
|
fn render_services_list(&self, frame: &mut Frame, area: Rect) {
|
||||||
if area.height < 1 {
|
if area.height < 1 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let available_lines = area.height as usize;
|
let available_lines = area.height as usize;
|
||||||
let services_to_show = self.service_metrics.iter().take(available_lines);
|
let services_to_show = self.service_metrics.iter().take(available_lines);
|
||||||
|
|
||||||
let mut y_offset = 0;
|
let mut y_offset = 0;
|
||||||
for service in services_to_show {
|
for service in services_to_show {
|
||||||
if y_offset >= available_lines {
|
if y_offset >= available_lines {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let service_area = Rect {
|
let service_area = Rect {
|
||||||
x: area.x,
|
x: area.x,
|
||||||
y: area.y + y_offset as u16,
|
y: area.y + y_offset as u16,
|
||||||
width: area.width,
|
width: area.width,
|
||||||
height: 1,
|
height: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let service_info = if let (Some(archives), Some(size_gb)) = (service.archive_count, service.repo_size_gb) {
|
let service_info = if let (Some(archives), Some(size_gb)) =
|
||||||
|
(service.archive_count, service.repo_size_gb)
|
||||||
|
{
|
||||||
let size_str = Self::format_size_with_proper_units(size_gb);
|
let size_str = Self::format_size_with_proper_units(size_gb);
|
||||||
format!(" {}archives {}", archives, size_str)
|
format!(" {}archives {}", archives, size_str)
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let service_text = format!("{}{}", service.name, service_info);
|
let service_text = format!("{}{}", service.name, service_info);
|
||||||
let service_spans = StatusIcons::create_status_spans(service.status, &service_text);
|
let service_spans = StatusIcons::create_status_spans(service.status, &service_text);
|
||||||
let service_para = Paragraph::new(ratatui::text::Line::from(service_spans));
|
let service_para = Paragraph::new(ratatui::text::Line::from(service_spans));
|
||||||
|
|
||||||
frame.render_widget(service_para, service_area);
|
frame.render_widget(service_para, service_area);
|
||||||
y_offset += 1;
|
y_offset += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are more services than we can show, indicate that
|
// If there are more services than we can show, indicate that
|
||||||
if self.service_metrics.len() > available_lines {
|
if self.service_metrics.len() > available_lines {
|
||||||
let more_count = self.service_metrics.len() - available_lines;
|
let more_count = self.service_metrics.len() - available_lines;
|
||||||
@ -435,11 +465,10 @@ impl BackupWidget {
|
|||||||
width: area.width,
|
width: area.width,
|
||||||
height: 1,
|
height: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let more_text = format!("... and {} more services", more_count);
|
let more_text = format!("... and {} more services", more_count);
|
||||||
let more_para = Paragraph::new(more_text)
|
let more_para = Paragraph::new(more_text).style(Typography::muted());
|
||||||
.style(Typography::muted());
|
|
||||||
|
|
||||||
frame.render_widget(more_para, last_line_area);
|
frame.render_widget(more_para, last_line_area);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -450,4 +479,4 @@ impl Default for BackupWidget {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user