From 1cea06e14be42009001b3dcc35e2e68dcca0ed76 Mon Sep 17 00:00:00 2001 From: Edward Langley Date: Tue, 14 Apr 2026 01:26:29 -0700 Subject: [PATCH] fix(records): allow adding rows in empty records view Add helper methods to CmdContext to clarify layout state and synthetic record presence. Update AddRecordRow to check for records mode generally rather than requiring a synthetic record at the current cursor, which allows adding the first row to an empty records view. Includes a regression test for the empty records view scenario. Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf) --- src/command/cmd/core.rs | 13 +++++++++++++ src/command/cmd/grid.rs | 7 +------ src/command/cmd/mode.rs | 6 +----- src/ui/app.rs | 29 +++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/command/cmd/core.rs b/src/command/cmd/core.rs index 049d7dc..fa868c0 100644 --- a/src/command/cmd/core.rs +++ b/src/command/cmd/core.rs @@ -48,9 +48,22 @@ pub struct CmdContext<'a> { } impl<'a> CmdContext<'a> { + /// Return true when the current layout is a records-mode layout. + pub fn is_records_mode(&self) -> bool { + self.layout.is_records_mode() + } + pub fn cell_key(&self) -> Option { self.layout.cell_key(self.selected.0, self.selected.1) } + + /// Return synthetic record coordinates for the current cursor, if any. + pub fn synthetic_record_at_cursor(&self) -> Option<(usize, String)> { + self.cell_key() + .as_ref() + .and_then(crate::view::synthetic_record_info) + } + pub fn row_count(&self) -> usize { self.layout.row_count() } diff --git a/src/command/cmd/grid.rs b/src/command/cmd/grid.rs index cce6573..a0176ab 100644 --- a/src/command/cmd/grid.rs +++ b/src/command/cmd/grid.rs @@ -443,12 +443,7 @@ impl Cmd for AddRecordRow { "add-record-row" } fn execute(&self, ctx: &CmdContext) -> Vec> { - let is_records = ctx - .cell_key() - .as_ref() - .and_then(crate::view::synthetic_record_info) - .is_some(); - if !is_records { + if !ctx.is_records_mode() { return vec![effect::set_status( "add-record-row only works in records mode", )]; diff --git a/src/command/cmd/mode.rs b/src/command/cmd/mode.rs index db269ad..0a705f3 100644 --- a/src/command/cmd/mode.rs +++ b/src/command/cmd/mode.rs @@ -228,11 +228,7 @@ impl Cmd for EditOrDrill { .unwrap_or(false) }); // In records mode (synthetic key), always edit directly — no drilling. - let is_synthetic = ctx - .cell_key() - .as_ref() - .and_then(crate::view::synthetic_record_info) - .is_some(); + let is_synthetic = ctx.synthetic_record_at_cursor().is_some(); let is_aggregated = !is_synthetic && regular_none; if is_aggregated { let Some(key) = ctx.cell_key().clone() else { diff --git a/src/ui/app.rs b/src/ui/app.rs index 137c800..1fec708 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -712,6 +712,35 @@ mod tests { ); } + /// Regression: pressing `o` in an empty records view should create the + /// first synthetic row instead of only entering edit mode on empty space. + #[test] + fn add_record_row_in_empty_records_view_creates_first_row() { + let mut m = Model::new("T"); + m.add_category("Region").unwrap(); + m.category_mut("Region").unwrap().add_item("East"); + let mut app = App::new(m, None); + + app.handle_key(KeyEvent::new(KeyCode::Char('R'), KeyModifiers::NONE)) + .unwrap(); + assert!(app.layout.is_records_mode(), "R should enter records mode"); + assert_eq!(app.layout.row_count(), 0, "fresh records view starts empty"); + + app.handle_key(KeyEvent::new(KeyCode::Char('o'), KeyModifiers::NONE)) + .unwrap(); + + assert_eq!( + app.layout.row_count(), + 1, + "o should create the first record row in an empty records view" + ); + assert!( + matches!(app.mode, AppMode::Editing { .. }), + "o should leave the app in edit mode, got {:?}", + app.mode + ); + } + #[test] fn command_mode_buffer_cleared_on_reentry() { use crossterm::event::KeyEvent;