Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions crates/openshell-tui/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ fn draw_sandbox_screen(frame: &mut Frame<'_>, app: &mut App, area: Rect) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Percentage(20), // metadata
Constraint::Percentage(80), // policy or logs
Constraint::Length(sandbox_detail::required_height(app)), // metadata
Constraint::Min(0), // policy or logs
])
.split(area);

Expand Down Expand Up @@ -737,4 +737,29 @@ mod tests {
assert_eq!(rendered, expected);
assert!(!rendered.contains("ALPHA"));
}

#[tokio::test]
async fn sandbox_delete_confirmation_is_visible_in_standard_terminal() {
let mut app = test_app();
app.screen = Screen::Sandbox;
app.focus = Focus::SandboxPolicy;
app.sandbox_names = vec!["test-sandbox".to_string()];
app.sandbox_count = 1;
app.confirm_delete = true;
let mut terminal = Terminal::new(TestBackend::new(100, 24)).unwrap();

terminal.draw(|frame| draw(frame, &mut app)).unwrap();

let text: String = terminal
.backend()
.buffer()
.content()
.iter()
.map(ratatui::buffer::Cell::symbol)
.collect();
assert!(
text.contains("Delete sandbox 'test-sandbox'?"),
"sandbox screen was: {text:?}"
);
}
}
43 changes: 33 additions & 10 deletions crates/openshell-tui/src/ui/sandbox_detail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,37 @@ use ratatui::widgets::{Block, Borders, Padding, Paragraph};

use crate::app::App;

const BASE_CONTENT_ROWS: u16 = 6;
const BORDER_ROWS: u16 = 2;

fn pending_draft_count(app: &App) -> usize {
let cached = app
.sandbox_draft_counts
.get(app.sandbox_selected)
.copied()
.unwrap_or(0);
if cached > 0 {
cached
} else {
app.draft_chunks
.iter()
.filter(|chunk| chunk.status == "pending")
.count()
}
}

/// Return the rows needed to render every metadata line without clipping.
pub(super) fn required_height(app: &App) -> u16 {
let policy_rows = u16::from(app.sandbox_policy_is_global);
let action_rows = if app.confirm_delete {
2 // spacer plus confirmation
} else {
u16::from(pending_draft_count(app) > 0)
};

BASE_CONTENT_ROWS + policy_rows + action_rows + BORDER_ROWS
}

/// Draw a compact metadata pane for the currently selected sandbox.
///
/// This is non-interactive (no focus state) — always rendered with the
Expand Down Expand Up @@ -37,16 +68,8 @@ pub fn draw(frame: &mut Frame<'_>, app: &App, area: Rect) {
};

// Count pending draft recommendations for this sandbox.
let pending_count = app.sandbox_draft_counts.get(idx).copied().unwrap_or(0);
// Also check the live draft_chunks when on the sandbox screen (more up-to-date).
let pending_count = if pending_count > 0 {
pending_count
} else {
app.draft_chunks
.iter()
.filter(|c| c.status == "pending")
.count()
};
// Fall back to the live chunks when the dashboard cache has no pending count.
let pending_count = pending_draft_count(app);

// Row 1: Name + Status + optional draft badge
let mut row1_spans = vec![
Expand Down
Loading