renderTab renders a single tab.
(entry TabEntry, number int)
| 177 | |
| 178 | // renderTab renders a single tab. |
| 179 | func (t *TabBar) renderTab(entry TabEntry, number int) string { |
| 180 | var content strings.Builder |
| 181 | |
| 182 | // Build tab content: name + indicators |
| 183 | name := entry.Name |
| 184 | maxNameLen := 8 |
| 185 | if len(name) > maxNameLen { |
| 186 | name = name[:maxNameLen-1] + "." |
| 187 | } |
| 188 | |
| 189 | // State indicator |
| 190 | var stateIndicator string |
| 191 | switch entry.LoopState { |
| 192 | case loop.LoopStateRunning: |
| 193 | stateIndicator = fmt.Sprintf(" ▶ %d", entry.Iteration) |
| 194 | case loop.LoopStatePaused: |
| 195 | stateIndicator = " ⏸" |
| 196 | case loop.LoopStateComplete: |
| 197 | stateIndicator = " ✓" |
| 198 | case loop.LoopStateError: |
| 199 | stateIndicator = " ✗" |
| 200 | default: |
| 201 | // Show progress for ready state |
| 202 | if entry.Total > 0 { |
| 203 | stateIndicator = fmt.Sprintf(" [%d/%d]", entry.Completed, entry.Total) |
| 204 | // Add checkmark if all complete |
| 205 | if entry.Completed == entry.Total { |
| 206 | stateIndicator += " ✓" |
| 207 | } |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | // Active indicator |
| 212 | activeIndicator := "" |
| 213 | if entry.IsActive { |
| 214 | activeIndicator = "◉ " |
| 215 | } |
| 216 | |
| 217 | content.WriteString(activeIndicator) |
| 218 | content.WriteString(name) |
| 219 | if entry.Branch != "" { |
| 220 | branch := entry.Branch |
| 221 | maxBranchLen := 20 |
| 222 | if len(branch) > maxBranchLen { |
| 223 | branch = branch[:maxBranchLen-1] + "…" |
| 224 | } |
| 225 | content.WriteString(" [") |
| 226 | content.WriteString(branch) |
| 227 | content.WriteString("]") |
| 228 | } |
| 229 | content.WriteString(stateIndicator) |
| 230 | |
| 231 | tabContent := content.String() |
| 232 | |
| 233 | // Choose style based on state |
| 234 | var style lipgloss.Style |
| 235 | if entry.IsActive { |
| 236 | style = TabActiveStyle |