| name | add-input-mode |
| description | Step-by-step checklist for adding a new input mode to Panoptes. Follow the checklist to add the enum variant, create the handler in the appropriate module, and update the dispatcher.
|
Add Input Mode Skill
Step-by-step checklist for adding a new input mode to Panoptes.
Steps
1. Add InputMode Enum Variant
In src/app/input_mode.rs, add the new variant:
pub enum InputMode {
NewMode,
}
2. Determine Handler Location
Choose the appropriate module based on the mode type:
| Mode Type | Handler Location |
|---|
| Text input (naming, paths) | src/input/text_input.rs |
| Confirmation dialogs | src/input/dialogs.rs |
| Session interaction | src/input/session_mode.rs |
| Multi-step wizards | src/wizards/<wizard>/handlers.rs |
3. Create Handler Function
In the chosen module, add the handler:
pub async fn handle_new_mode(app: &mut App, key: KeyEvent) -> AppResult<()> {
match key.code {
KeyCode::Char(c) => {
}
KeyCode::Enter => {
app.state.input_mode = InputMode::Normal;
}
KeyCode::Esc => {
app.state.input_mode = InputMode::Normal;
}
KeyCode::Backspace => {
}
_ => {}
}
Ok(())
}
4. Add Dispatch Case
In src/input/dispatcher.rs, in handle_key_event(), add:
InputMode::NewMode => {
text_input::handle_new_mode(app, key).await?;
}
5. Add State Fields (if needed)
If the mode needs to track state (e.g., text buffer, cursor position), add fields to AppState in src/app/state.rs:
pub struct AppState {
pub new_mode_buffer: String,
pub new_mode_cursor: usize,
}
6. Add Trigger Logic
Add code to enter the new mode from normal mode. Usually in the relevant view's input handler:
KeyCode::Char('n') => {
app.state.input_mode = InputMode::NewMode;
app.state.new_mode_buffer.clear();
}
7. Update Rendering (if needed)
If the mode has visual feedback (input box, cursor), update the view's render function to display it when in this mode.
Common Patterns
Text Input Mode
pub async fn handle_text_mode(app: &mut App, key: KeyEvent) -> AppResult<()> {
match key.code {
KeyCode::Char(c) => {
app.state.text_buffer.insert(app.state.cursor_pos, c);
app.state.cursor_pos += 1;
}
KeyCode::Backspace if app.state.cursor_pos > 0 => {
app.state.cursor_pos -= 1;
app.state.text_buffer.remove(app.state.cursor_pos);
}
KeyCode::Left if app.state.cursor_pos > 0 => {
app.state.cursor_pos -= 1;
}
KeyCode::Right if app.state.cursor_pos < app.state.text_buffer.len() => {
app.state.cursor_pos += 1;
}
KeyCode::Enter => {
let input = app.state.text_buffer.clone();
app.state.input_mode = InputMode::Normal;
}
KeyCode::Esc => {
app.state.input_mode = InputMode::Normal;
}
_ => {}
}
Ok(())
}
Confirmation Dialog Mode
pub async fn handle_confirm_mode(app: &mut App, key: KeyEvent) -> AppResult<()> {
match key.code {
KeyCode::Char('y') | KeyCode::Char('Y') => {
app.state.input_mode = InputMode::Normal;
}
KeyCode::Char('n') | KeyCode::Char('N') | KeyCode::Esc => {
app.state.input_mode = InputMode::Normal;
}
_ => {}
}
Ok(())
}
Verification
- Run
cargo build to check for compile errors
- Run
cargo clippy -- -D warnings
- Test the mode:
- Enter the mode via the trigger
- Verify input handling works
- Verify Esc cancels properly
- Verify Enter/confirmation works