feat(effect): add WizardKey and StartImportWizard effects
Add WizardKey effect to handle key bindings for navigating wizard steps: Preview, SelectArrayPath, ReviewProposals, ConfigureDates, DefineFormulas, and NameModel. Add StartImportWizard effect to initialize the wizard by reading and parsing a JSON file. Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
This commit is contained in:
154
src/ui/effect.rs
154
src/ui/effect.rs
@ -340,6 +340,160 @@ impl Effect for SaveAs {
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatch a key event to the import wizard.
|
||||
/// The wizard has its own internal state machine; this effect handles
|
||||
/// all wizard key interactions and App-level side effects.
|
||||
#[derive(Debug)]
|
||||
pub struct WizardKey {
|
||||
pub key_code: crossterm::event::KeyCode,
|
||||
}
|
||||
impl Effect for WizardKey {
|
||||
fn apply(&self, app: &mut App) {
|
||||
use crate::import::wizard::WizardStep;
|
||||
|
||||
let Some(wizard) = &mut app.wizard else {
|
||||
return;
|
||||
};
|
||||
|
||||
match &wizard.step.clone() {
|
||||
WizardStep::Preview => match self.key_code {
|
||||
crossterm::event::KeyCode::Enter | crossterm::event::KeyCode::Char(' ') => {
|
||||
wizard.advance()
|
||||
}
|
||||
crossterm::event::KeyCode::Esc => {
|
||||
app.mode = AppMode::Normal;
|
||||
app.wizard = None;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
WizardStep::SelectArrayPath => match self.key_code {
|
||||
crossterm::event::KeyCode::Up | crossterm::event::KeyCode::Char('k') => {
|
||||
wizard.move_cursor(-1)
|
||||
}
|
||||
crossterm::event::KeyCode::Down | crossterm::event::KeyCode::Char('j') => {
|
||||
wizard.move_cursor(1)
|
||||
}
|
||||
crossterm::event::KeyCode::Enter => wizard.confirm_path(),
|
||||
crossterm::event::KeyCode::Esc => {
|
||||
app.mode = AppMode::Normal;
|
||||
app.wizard = None;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
WizardStep::ReviewProposals => match self.key_code {
|
||||
crossterm::event::KeyCode::Up | crossterm::event::KeyCode::Char('k') => {
|
||||
wizard.move_cursor(-1)
|
||||
}
|
||||
crossterm::event::KeyCode::Down | crossterm::event::KeyCode::Char('j') => {
|
||||
wizard.move_cursor(1)
|
||||
}
|
||||
crossterm::event::KeyCode::Char(' ') => wizard.toggle_proposal(),
|
||||
crossterm::event::KeyCode::Char('c') => wizard.cycle_proposal_kind(),
|
||||
crossterm::event::KeyCode::Enter => wizard.advance(),
|
||||
crossterm::event::KeyCode::Esc => {
|
||||
app.mode = AppMode::Normal;
|
||||
app.wizard = None;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
WizardStep::ConfigureDates => match self.key_code {
|
||||
crossterm::event::KeyCode::Up | crossterm::event::KeyCode::Char('k') => {
|
||||
wizard.move_cursor(-1)
|
||||
}
|
||||
crossterm::event::KeyCode::Down | crossterm::event::KeyCode::Char('j') => {
|
||||
wizard.move_cursor(1)
|
||||
}
|
||||
crossterm::event::KeyCode::Char(' ') => wizard.toggle_date_component(),
|
||||
crossterm::event::KeyCode::Enter => wizard.advance(),
|
||||
crossterm::event::KeyCode::Esc => {
|
||||
app.mode = AppMode::Normal;
|
||||
app.wizard = None;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
WizardStep::DefineFormulas => {
|
||||
if wizard.formula_editing {
|
||||
match self.key_code {
|
||||
crossterm::event::KeyCode::Enter => wizard.confirm_formula(),
|
||||
crossterm::event::KeyCode::Esc => wizard.cancel_formula_edit(),
|
||||
crossterm::event::KeyCode::Backspace => wizard.pop_formula_char(),
|
||||
crossterm::event::KeyCode::Char(c) => wizard.push_formula_char(c),
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
match self.key_code {
|
||||
crossterm::event::KeyCode::Char('n') => wizard.start_formula_edit(),
|
||||
crossterm::event::KeyCode::Char('d') => wizard.delete_formula(),
|
||||
crossterm::event::KeyCode::Up | crossterm::event::KeyCode::Char('k') => {
|
||||
wizard.move_cursor(-1)
|
||||
}
|
||||
crossterm::event::KeyCode::Down | crossterm::event::KeyCode::Char('j') => {
|
||||
wizard.move_cursor(1)
|
||||
}
|
||||
crossterm::event::KeyCode::Enter => wizard.advance(),
|
||||
crossterm::event::KeyCode::Esc => {
|
||||
app.mode = AppMode::Normal;
|
||||
app.wizard = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
WizardStep::NameModel => match self.key_code {
|
||||
crossterm::event::KeyCode::Char(c) => wizard.push_name_char(c),
|
||||
crossterm::event::KeyCode::Backspace => wizard.pop_name_char(),
|
||||
crossterm::event::KeyCode::Enter => match wizard.build_model() {
|
||||
Ok(mut model) => {
|
||||
model.normalize_view_state();
|
||||
app.model = model;
|
||||
app.formula_cursor = 0;
|
||||
app.dirty = true;
|
||||
app.status_msg = "Import successful! Press :w <path> to save.".to_string();
|
||||
app.mode = AppMode::Normal;
|
||||
app.wizard = None;
|
||||
}
|
||||
Err(e) => {
|
||||
if let Some(w) = &mut app.wizard {
|
||||
w.message = Some(format!("Error: {e}"));
|
||||
}
|
||||
}
|
||||
},
|
||||
crossterm::event::KeyCode::Esc => {
|
||||
app.mode = AppMode::Normal;
|
||||
app.wizard = None;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
WizardStep::Done => {
|
||||
app.mode = AppMode::Normal;
|
||||
app.wizard = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Start the import wizard from a JSON file path.
|
||||
#[derive(Debug)]
|
||||
pub struct StartImportWizard(pub String);
|
||||
impl Effect for StartImportWizard {
|
||||
fn apply(&self, app: &mut App) {
|
||||
match std::fs::read_to_string(&self.0) {
|
||||
Ok(content) => match serde_json::from_str::<serde_json::Value>(&content) {
|
||||
Ok(json) => {
|
||||
app.wizard = Some(crate::import::wizard::ImportWizard::new(json));
|
||||
app.mode = AppMode::ImportWizard;
|
||||
}
|
||||
Err(e) => {
|
||||
app.status_msg = format!("JSON parse error: {e}");
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
app.status_msg = format!("Cannot read file: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExportCsv(pub PathBuf);
|
||||
impl Effect for ExportCsv {
|
||||
|
||||
Reference in New Issue
Block a user