| 309 | } |
| 310 | |
| 311 | func (c *manager) View() string { |
| 312 | if !c.visible { |
| 313 | return "" |
| 314 | } |
| 315 | |
| 316 | var lines []string |
| 317 | |
| 318 | if len(c.filteredItems) == 0 { |
| 319 | if c.loading { |
| 320 | lines = append(lines, styles.CompletionNoResultsStyle.Render("Loading…")) |
| 321 | } else { |
| 322 | lines = append(lines, styles.CompletionNoResultsStyle.Render("No results")) |
| 323 | } |
| 324 | } else { |
| 325 | visibleStart := c.scrollOffset |
| 326 | visibleEnd := min(c.scrollOffset+maxItems, len(c.filteredItems)) |
| 327 | |
| 328 | maxLabelLen := 0 |
| 329 | for i := visibleStart; i < visibleEnd; i++ { |
| 330 | labelLen := lipgloss.Width(c.filteredItems[i].Label) |
| 331 | if labelLen > maxLabelLen { |
| 332 | maxLabelLen = labelLen |
| 333 | } |
| 334 | } |
| 335 | |
| 336 | for i := visibleStart; i < visibleEnd; i++ { |
| 337 | item := c.filteredItems[i] |
| 338 | isSelected := i == c.selected |
| 339 | |
| 340 | itemStyle := styles.CompletionNormalStyle |
| 341 | descStyle := styles.CompletionDescStyle |
| 342 | if isSelected { |
| 343 | itemStyle = styles.CompletionSelectedStyle |
| 344 | descStyle = styles.CompletionSelectedDescStyle |
| 345 | } |
| 346 | |
| 347 | // Pad label to maxLabelLen so descriptions align |
| 348 | paddedLabel := item.Label + strings.Repeat(" ", maxLabelLen+1-lipgloss.Width(item.Label)) |
| 349 | text := paddedLabel |
| 350 | if item.Description != "" { |
| 351 | text += " " + descStyle.Render(item.Description) |
| 352 | } |
| 353 | |
| 354 | lines = append(lines, itemStyle.Width(c.width-6).Render(text)) |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | content := lipgloss.JoinVertical(lipgloss.Left, lines...) |
| 359 | return styles.CompletionBoxStyle.Render(content) |
| 360 | } |
| 361 | |
| 362 | func (c *manager) GetLayers() []*lipgloss.Layer { |
| 363 | if !c.visible { |