use crate::i18n::{MessageKey, Locale, tr};
use crate::state::{AppState, Message, WizardFileCheck, WizardState};
use lucide_icons::iced as icons;
use iced::widget::{button, column, container, row, text, text_input};
use iced::{Element, Length, Padding};
pub fn wizard_view(state: &AppState) -> Element<'_, Message> {
let locale = state.locale;
let wizard = match &state.wizard {
Some(w) => w,
None => return text("").into(),
};
let inner = match wizard {
WizardState::NotConfigured => page_input(locale, state, None, None),
WizardState::FileMissing { previous_dir, checks } => {
page_input(locale, state, Some(previous_dir), Some(checks))
}
WizardState::Checked { model_dir, checks, all_ok } => {
page_checked(locale, state, model_dir, checks, *all_ok)
}
WizardState::Ready { model_dir } => page_ready(locale, model_dir),
};
container(inner)
.width(Length::Fill)
.height(Length::Fill)
.padding(Padding::from([40.0, 32.0]))
.into()
}
fn page_input<'a>(
locale: Locale,
state: &'a AppState,
previous_dir: Option<&'a str>,
file_checks: Option<&'a [crate::state::WizardFileCheck]>,
) -> Element<'a, Message> {
let (title_key, body_key) = if previous_dir.is_some() {
(MessageKey::WizardTitleFileMissing, MessageKey::WizardBodyFileMissing)
} else {
(MessageKey::WizardTitleNotConfigured, MessageKey::WizardBodyNotConfigured)
};
let mut col = column![
text(tr(locale, title_key)).size(22),
text(tr(locale, body_key)).size(13),
]
.spacing(10);
if let Some(prev) = previous_dir {
col = col.push(
column![
text(tr(locale, MessageKey::WizardPreviousPathLabel)).size(12),
text(format!(" {prev}")).size(11),
]
.spacing(2),
);
} else {
col = col.push(
column![
text(tr(locale, MessageKey::WizardFilesNeededLabel)).size(12),
text(" onnx/model.onnx").size(11),
text(" tokenizer.json").size(11),
text(tr(locale, MessageKey::WizardDownloadHint)).size(11),
]
.spacing(2),
);
}
col = col.push(
text_input(
tr(locale, MessageKey::WizardPathInputPlaceholder),
&state.wizard_path_input,
)
.on_input(Message::WizardPathChanged)
.on_submit(Message::WizardValidate)
.padding(8),
);
col = col.push(
row![
button(iced::widget::row![icons::icon_scan_eye().size(14), iced::widget::text(tr(locale, MessageKey::WizardActionValidate)).size(13)].spacing(4))
.on_press(Message::WizardValidate),
]
.spacing(8),
);
col = col.push(
button(text(tr(locale, MessageKey::WizardActionSkip)).size(12))
.on_press(Message::WizardSkip),
);
if let Some(checks) = file_checks {
for fc in checks {
let (icon, label_suffix) = if fc.found {
("✓", "(found)")
} else {
("✗", "(missing)")
};
col = col.push(
text(format!("{icon} {} {label_suffix}", fc.relative_path)).size(12),
);
}
}
col.into()
}
fn page_checked<'a>(
locale: Locale,
state: &'a AppState,
model_dir: &'a str,
checks: &'a [WizardFileCheck],
all_ok: bool,
) -> Element<'a, Message> {
let title = tr(locale, MessageKey::WizardTitleValidating);
let mut col = column![
text(title).size(22),
text(model_dir).size(11),
]
.spacing(10);
for check in checks {
let status = if check.found {
let mb = check.size_mb.map(|m| format!(" {m:.1} MB")).unwrap_or_default();
format!(
"✓ {}{} {}",
check.relative_path,
mb,
tr(locale, MessageKey::WizardValidationOk)
)
} else {
format!(
"✗ {} {}",
check.relative_path,
tr(locale, MessageKey::WizardValidationFail)
)
};
col = col.push(text(status).size(12));
}
if all_ok {
col = col.push(
button(text(tr(locale, MessageKey::WizardActionUseModel)).size(13))
.on_press(Message::WizardAccept),
);
} else {
col = col.push(
text_input(
tr(locale, MessageKey::WizardPathInputPlaceholder),
&state.wizard_path_input,
)
.on_input(Message::WizardPathChanged)
.on_submit(Message::WizardValidate)
.padding(8),
);
col = col.push(
button(iced::widget::row![icons::icon_scan_eye().size(14), iced::widget::text(tr(locale, MessageKey::WizardActionValidate)).size(13)].spacing(4))
.on_press(Message::WizardValidate),
);
}
col = col.push(
button(text(tr(locale, MessageKey::WizardActionSkip)).size(12))
.on_press(Message::WizardSkip),
);
col.into()
}
fn page_ready<'a>(locale: Locale, model_dir: &'a str) -> Element<'a, Message> {
column![
text(tr(locale, MessageKey::WizardTitleReady)).size(22),
text(tr(locale, MessageKey::WizardReadyBody)).size(13),
text(model_dir).size(11),
button(text(tr(locale, MessageKey::WizardActionContinue)).size(13))
.on_press(Message::WizardAccept),
]
.spacing(10)
.into()
}