(&mut self, frame: &mut Frame, area: Rect, theme: &Theme)
| 349 | // ── Rendering ── |
| 350 | |
| 351 | pub fn render(&mut self, frame: &mut Frame, area: Rect, theme: &Theme) { |
| 352 | if !self.visible { |
| 353 | self.last_area = None; |
| 354 | return; |
| 355 | } |
| 356 | |
| 357 | let popup_width = area.width.saturating_sub(4).min(64); |
| 358 | let max_popup_height = (area.height as f32 * 0.75) as u16; |
| 359 | let ideal_height = (self.rows.len() as u16 + 4).min(max_popup_height); // +4: border*2, title, hint |
| 360 | let popup_height = ideal_height.max(8).min(area.height.saturating_sub(2)); |
| 361 | if popup_height < 6 || popup_width < 20 { |
| 362 | self.last_area = None; |
| 363 | return; |
| 364 | } |
| 365 | |
| 366 | let popup_x = area.x + (area.width.saturating_sub(popup_width)) / 2; |
| 367 | let popup_y = area.y + (area.height.saturating_sub(popup_height)) / 2; |
| 368 | let popup_area = Rect { |
| 369 | x: popup_x, |
| 370 | y: popup_y, |
| 371 | width: popup_width, |
| 372 | height: popup_height, |
| 373 | }; |
| 374 | self.last_area = Some(popup_area); |
| 375 | |
| 376 | let block = Block::default() |
| 377 | .borders(Borders::ALL) |
| 378 | .border_style(theme.style(StyleKind::Primary)) |
| 379 | .style(Style::default().bg(theme.background)) |
| 380 | .title(" Add Model \u{2015} Select Provider "); |
| 381 | |
| 382 | frame.render_widget(Clear, popup_area); |
| 383 | frame.render_widget(block, popup_area); |
| 384 | |
| 385 | let inner = Rect { |
| 386 | x: popup_area.x + 1, |
| 387 | y: popup_area.y + 1, |
| 388 | width: popup_area.width.saturating_sub(2), |
| 389 | height: popup_area.height.saturating_sub(2), |
| 390 | }; |
| 391 | |
| 392 | if inner.height < 3 || inner.width < 4 { |
| 393 | return; |
| 394 | } |
| 395 | |
| 396 | // Content area (reserve 1 row for hint at bottom) |
| 397 | let content_height = inner.height.saturating_sub(1) as usize; |
| 398 | self.visible_rows = content_height; |
| 399 | |
| 400 | // Clamp scroll |
| 401 | if self.rows.len() <= content_height { |
| 402 | self.scroll_offset = 0; |
| 403 | } else { |
| 404 | let max_offset = self.rows.len() - content_height; |
| 405 | self.scroll_offset = self.scroll_offset.min(max_offset); |
| 406 | } |
| 407 | |
| 408 | let visible_end = (self.scroll_offset + content_height).min(self.rows.len()); |
nothing calls this directly
no test coverage detected