forked from 1b-agent/opencadstudio-upstream
feat(io): add user-approved recovery
This commit is contained in:
1 parent
25515fa33c
commit
9e90e50662
25 files changed
+2723
-198
No files matched your search
@@ -7,6 +7,7 @@ pub mod shortcuts;
|
||||
pub mod layers;
|
||||
pub mod update_notice;
|
||||
pub mod open_progress;
|
||||
pub mod recovery;
|
||||
pub mod options;
|
||||
pub mod attribute_editor;
|
||||
pub mod alias_editor;
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
use crate::app::Message;
|
||||
use crate::io::recovery::{RecoveryReport, RecoveryStatus};
|
||||
use iced::widget::{button, column, container, row, scrollable, text, Space};
|
||||
use iced::{Background, Border, Element, Fill, Theme};
|
||||
|
||||
fn muted_style(theme: &Theme) -> iced::widget::text::Style {
|
||||
iced::widget::text::Style {
|
||||
color: Some(theme.palette().background.base.text.scale_alpha(0.68)),
|
||||
}
|
||||
}
|
||||
|
||||
fn status_style(
|
||||
status: RecoveryStatus,
|
||||
) -> impl Fn(&Theme) -> iced::widget::text::Style + Copy {
|
||||
move |theme: &Theme| iced::widget::text::Style {
|
||||
color: Some(match status {
|
||||
RecoveryStatus::Recovered => theme.palette().warning.base.color,
|
||||
RecoveryStatus::Failed => theme.palette().danger.base.color,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn metric<'a>(label: String, value: String) -> Element<'a, Message> {
|
||||
container(
|
||||
column![
|
||||
text(label).size(10).style(muted_style),
|
||||
text(value).size(18),
|
||||
]
|
||||
.spacing(3),
|
||||
)
|
||||
.padding([10, 12])
|
||||
.width(Fill)
|
||||
.style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(theme.palette().background.weak.color)),
|
||||
border: Border {
|
||||
color: theme.palette().background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 5.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn view_window<'a>(
|
||||
report: &'a RecoveryReport,
|
||||
allow_save_copy: bool,
|
||||
sizing: crate::ui::modal::ModalSizing,
|
||||
) -> Element<'a, Message> {
|
||||
let recovered = report.status == RecoveryStatus::Recovered;
|
||||
let heading = if recovered {
|
||||
crate::tr!("recovery-opened-with-repairs")
|
||||
} else {
|
||||
crate::tr!("recovery-open-failed")
|
||||
};
|
||||
let description = if recovered {
|
||||
crate::tr!("recovery-repaired-description")
|
||||
} else {
|
||||
crate::tr!("recovery-failed-description")
|
||||
};
|
||||
let status = report.status;
|
||||
|
||||
let metrics = row![
|
||||
metric(
|
||||
crate::tr!("recovery-entities-checked"),
|
||||
report.entities_scanned.to_string(),
|
||||
),
|
||||
metric(
|
||||
crate::tr!("recovery-issues-found"),
|
||||
report.issues_found().to_string(),
|
||||
),
|
||||
metric(
|
||||
crate::tr!("recovery-entities-removed"),
|
||||
report.removed_total().to_string(),
|
||||
),
|
||||
metric(
|
||||
crate::tr!("recovery-references-checked"),
|
||||
report.references_checked.to_string(),
|
||||
),
|
||||
]
|
||||
.spacing(8)
|
||||
.width(Fill);
|
||||
|
||||
let mut details = column![].spacing(6);
|
||||
if report.referenced_entities_removed > 0 {
|
||||
details = details.push(
|
||||
text(format!(
|
||||
"{}: {}",
|
||||
crate::tr!("recovery-referenced-entities-removed"),
|
||||
report.referenced_entities_removed
|
||||
))
|
||||
.size(11),
|
||||
);
|
||||
}
|
||||
if report.references_missing > 0 || report.references_failed > 0 {
|
||||
details = details.push(
|
||||
text(format!(
|
||||
"{}: {}",
|
||||
crate::tr!("recovery-references-unavailable"),
|
||||
report
|
||||
.references_missing
|
||||
.saturating_add(report.references_failed)
|
||||
))
|
||||
.size(11),
|
||||
);
|
||||
}
|
||||
if let Some(error) = &report.error {
|
||||
details = details.push(text(error).size(11));
|
||||
}
|
||||
for (kind, message) in report.diagnostics.iter().take(100) {
|
||||
details = details.push(text(format!("[{kind}] {message}")).size(10));
|
||||
}
|
||||
if let Some(path) = &report.log_path {
|
||||
details = details.push(
|
||||
text(format!("{}: {}", crate::tr!("recovery-log-path"), path.display()))
|
||||
.size(10)
|
||||
.style(muted_style),
|
||||
);
|
||||
} else if let Some(error) = &report.log_error {
|
||||
details = details.push(
|
||||
text(format!(
|
||||
"{}: {error}",
|
||||
crate::tr!("recovery-log-write-failed")
|
||||
))
|
||||
.size(10)
|
||||
.style(status_style(RecoveryStatus::Failed)),
|
||||
);
|
||||
} else if cfg!(target_arch = "wasm32") {
|
||||
details = details.push(
|
||||
text(crate::tr!("recovery-log-download-ready"))
|
||||
.size(10)
|
||||
.style(muted_style),
|
||||
);
|
||||
}
|
||||
|
||||
let detail_panel = container(scrollable(details).height(Fill))
|
||||
.padding([10, 12])
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.style(container::bordered_box);
|
||||
|
||||
let mut actions = row![Space::new().width(Fill)].spacing(8);
|
||||
if recovered && report.save_as_required && allow_save_copy {
|
||||
actions = actions.push(
|
||||
button(text(crate::tr!("recovery-save-copy")).size(12))
|
||||
.on_press(Message::RecoverySaveAs)
|
||||
.style(button::primary)
|
||||
.padding([6, 14]),
|
||||
);
|
||||
}
|
||||
if report.log_path.is_some() || cfg!(target_arch = "wasm32") {
|
||||
actions = actions.push(
|
||||
button(text(crate::tr!("recovery-show-log")).size(12))
|
||||
.on_press(Message::RecoveryShowLog)
|
||||
.style(button::secondary)
|
||||
.padding([6, 14]),
|
||||
);
|
||||
}
|
||||
actions = actions.push(
|
||||
button(text(crate::tr!("action-close")).size(12))
|
||||
.on_press(Message::RecoveryClose)
|
||||
.style(button::secondary)
|
||||
.padding([6, 14]),
|
||||
);
|
||||
|
||||
container(
|
||||
column![
|
||||
text(heading).size(20).style(status_style(status)),
|
||||
text(&report.file_name).size(13),
|
||||
text(description).size(11).style(muted_style),
|
||||
Space::new().height(4),
|
||||
metrics,
|
||||
detail_panel,
|
||||
actions,
|
||||
]
|
||||
.spacing(10)
|
||||
.width(sizing.width)
|
||||
.height(sizing.height),
|
||||
)
|
||||
.padding([12, 16])
|
||||
.width(sizing.width)
|
||||
.height(sizing.height)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn view_prompt<'a>(
|
||||
file_name: &'a str,
|
||||
error: &'a str,
|
||||
sizing: crate::ui::modal::ModalSizing,
|
||||
) -> Element<'a, Message> {
|
||||
let actions = row![
|
||||
Space::new().width(Fill),
|
||||
button(text(crate::tr!("recovery-decline")).size(12))
|
||||
.on_press(Message::RecoveryDecline)
|
||||
.style(button::secondary)
|
||||
.padding([6, 14]),
|
||||
button(text(crate::tr!("recovery-attempt")).size(12))
|
||||
.on_press(Message::RecoveryAttempt)
|
||||
.style(button::primary)
|
||||
.padding([6, 14]),
|
||||
]
|
||||
.spacing(8);
|
||||
|
||||
container(
|
||||
column![
|
||||
text(crate::tr!("recovery-prompt-heading")).size(20),
|
||||
text(file_name).size(13),
|
||||
text(crate::tr!("recovery-prompt-description"))
|
||||
.size(11)
|
||||
.style(muted_style),
|
||||
container(scrollable(text(error).size(10)).height(Fill))
|
||||
.padding([10, 12])
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.style(container::bordered_box),
|
||||
actions,
|
||||
]
|
||||
.spacing(10)
|
||||
.width(sizing.width)
|
||||
.height(sizing.height),
|
||||
)
|
||||
.padding([12, 16])
|
||||
.into()
|
||||
}
|
||||
Reference in new issue
Block a user