(&mut self, frame: &mut Frame, area: Rect, theme: &Theme)
| 531 | // ── Rendering ── |
| 532 | |
| 533 | pub fn render(&mut self, frame: &mut Frame, area: Rect, theme: &Theme) { |
| 534 | if !self.visible { |
| 535 | self.last_area = None; |
| 536 | return; |
| 537 | } |
| 538 | |
| 539 | // Calculate popup dimensions |
| 540 | let popup_width = area.width.saturating_sub(4).min(60); |
| 541 | // Fixed height: use up to 70% of terminal height, with reasonable bounds |
| 542 | let max_popup_height = (area.height as f32 * 0.7) as u16; |
| 543 | // Content: 1 search + 1 separator + rows + 1 hint + 2 borders |
| 544 | let ideal_height = (self.rows.len() as u16 + 5).min(max_popup_height); |
| 545 | let popup_height = ideal_height.max(8).min(area.height.saturating_sub(2)); |
| 546 | if popup_height < 6 || popup_width < 20 { |
| 547 | self.last_area = None; |
| 548 | return; |
| 549 | } |
| 550 | |
| 551 | let popup_x = area.x + (area.width.saturating_sub(popup_width)) / 2; |
| 552 | let popup_y = area.y + (area.height.saturating_sub(popup_height)) / 2; |
| 553 | let popup_area = Rect { |
| 554 | x: popup_x, |
| 555 | y: popup_y, |
| 556 | width: popup_width, |
| 557 | height: popup_height, |
| 558 | }; |
| 559 | self.last_area = Some(popup_area); |
| 560 | |
| 561 | // Draw background + border |
| 562 | let block = Block::default() |
| 563 | .borders(Borders::ALL) |
| 564 | .border_style(theme.style(StyleKind::Primary)) |
| 565 | .style(Style::default().bg(theme.background)) |
| 566 | .title(" Command Palette "); |
| 567 | |
| 568 | frame.render_widget(Clear, popup_area); |
| 569 | frame.render_widget(block, popup_area); |
| 570 | |
| 571 | // Inner area (inside border) |
| 572 | let inner = Rect { |
| 573 | x: popup_area.x + 1, |
| 574 | y: popup_area.y + 1, |
| 575 | width: popup_area.width.saturating_sub(2), |
| 576 | height: popup_area.height.saturating_sub(2), |
| 577 | }; |
| 578 | |
| 579 | if inner.height < 4 || inner.width < 4 { |
| 580 | return; |
| 581 | } |
| 582 | |
| 583 | // Row 0: Search box |
| 584 | let search_display = if self.search_input.is_empty() { |
| 585 | Line::from(vec![ |
| 586 | Span::styled( |
| 587 | "> ", |
| 588 | theme.style(StyleKind::Primary).add_modifier(Modifier::BOLD), |
| 589 | ), |
| 590 | Span::styled("Search commands...", theme.style(StyleKind::Muted)), |
nothing calls this directly
no test coverage detected