LPM-138 Refactor Tui with composable modals and use canvas Refactors the TUI (dashboard + kanban) to use a shared, composable modal system with canvas-based overlay rendering, while consolidating styling into internal/style and expanding issue interactions (e.g., comments). Changes: Introduces a new pkg/tui/modal system (manager/stack + multiple modal types) and overlays modals using Lipgloss compositor layers. Refactors dashboard/kanban views and input handling to use the modal manager + focus manager instead of per-modal boolean state. Consolidates TUI styling by moving from pkg/tui/styles to internal/style and updates components to use the new style package; adds a shared footer renderer.
44 lines
932 B
Go
44 lines
932 B
Go
package truncate
|
|
|
|
import "charm.land/lipgloss/v2"
|
|
|
|
// TruncateToWidth trims the given text so that its rendered width does not
|
|
// exceed maxWidth. If truncation occurs and there is room, an ellipsis is
|
|
// appended to indicate that the text was shortened.
|
|
func TruncateToWidth(text string, maxWidth int) string {
|
|
if maxWidth <= 0 {
|
|
return ""
|
|
}
|
|
|
|
if lipgloss.Width(text) <= maxWidth {
|
|
return text
|
|
}
|
|
|
|
const ellipsis = "…"
|
|
ellipsisWidth := lipgloss.Width(ellipsis)
|
|
|
|
if ellipsisWidth > maxWidth {
|
|
runes := []rune(text)
|
|
if len(runes) > 0 {
|
|
firstChar := string(runes[0])
|
|
if lipgloss.Width(firstChar) <= maxWidth {
|
|
return firstChar
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
runes := []rune(text)
|
|
lastSafe := 0
|
|
for i := range runes {
|
|
candidate := string(runes[:i+1])
|
|
if lipgloss.Width(candidate)+ellipsisWidth > maxWidth {
|
|
break
|
|
}
|
|
lastSafe = i + 1
|
|
}
|
|
|
|
current := string(runes[:lastSafe])
|
|
return current + ellipsis
|
|
}
|