init
This commit is contained in:
commit
97fd7e7b1a
13 changed files with 2230 additions and 0 deletions
164
wrapping_delegate.go
Normal file
164
wrapping_delegate.go
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/charmbracelet/bubbles/list"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// WrappingDelegate is a delegate that wraps text instead of truncating it.
|
||||
// It's based on the DefaultDelegate but modified to use text wrapping.
|
||||
type WrappingDelegate struct {
|
||||
ShowDescription bool
|
||||
Styles list.DefaultItemStyles
|
||||
UpdateFunc func(tea.Msg, *list.Model) tea.Cmd
|
||||
ShortHelpFunc func() []key.Binding
|
||||
FullHelpFunc func() [][]key.Binding
|
||||
height int
|
||||
spacing int
|
||||
}
|
||||
|
||||
// NewWrappingDelegate creates a new delegate with wrapping behavior and default styles.
|
||||
func NewWrappingDelegate() WrappingDelegate {
|
||||
const defaultHeight = 2
|
||||
const defaultSpacing = 1
|
||||
return WrappingDelegate{
|
||||
ShowDescription: true,
|
||||
Styles: list.NewDefaultItemStyles(),
|
||||
height: defaultHeight,
|
||||
spacing: defaultSpacing,
|
||||
}
|
||||
}
|
||||
|
||||
// SetHeight sets delegate's preferred height.
|
||||
func (d *WrappingDelegate) SetHeight(i int) {
|
||||
d.height = i
|
||||
}
|
||||
|
||||
// Height returns the delegate's preferred height.
|
||||
// This has effect only if ShowDescription is true,
|
||||
// otherwise height is always 1.
|
||||
func (d WrappingDelegate) Height() int {
|
||||
if d.ShowDescription {
|
||||
return d.height
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// SetSpacing sets the delegate's spacing.
|
||||
func (d *WrappingDelegate) SetSpacing(i int) {
|
||||
d.spacing = i
|
||||
}
|
||||
|
||||
// Spacing returns the delegate's spacing.
|
||||
func (d WrappingDelegate) Spacing() int {
|
||||
return d.spacing
|
||||
}
|
||||
|
||||
// Update checks whether the delegate's UpdateFunc is set and calls it.
|
||||
func (d WrappingDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
|
||||
if d.UpdateFunc == nil {
|
||||
return nil
|
||||
}
|
||||
return d.UpdateFunc(msg, m)
|
||||
}
|
||||
|
||||
// wrapText wraps text to the specified width using lipgloss
|
||||
func wrapText(text string, width int, style lipgloss.Style) string {
|
||||
if width <= 0 {
|
||||
return text
|
||||
}
|
||||
return style.Width(width).Render(text)
|
||||
}
|
||||
|
||||
// Render prints an item with text wrapping instead of truncation.
|
||||
func (d WrappingDelegate) Render(w io.Writer, m list.Model, index int, item list.Item) {
|
||||
var (
|
||||
title, desc string
|
||||
matchedRunes []int
|
||||
s = &d.Styles
|
||||
)
|
||||
|
||||
if i, ok := item.(list.DefaultItem); ok {
|
||||
title = i.Title()
|
||||
desc = i.Description()
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
if m.Width() <= 0 {
|
||||
// short-circuit
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate available text width
|
||||
textwidth := m.Width() - s.NormalTitle.GetPaddingLeft() - s.NormalTitle.GetPaddingRight()
|
||||
|
||||
// Conditions
|
||||
var (
|
||||
isSelected = index == m.Index()
|
||||
emptyFilter = m.FilterState() == list.Filtering && m.FilterValue() == ""
|
||||
isFiltered = m.FilterState() == list.Filtering || m.FilterState() == list.FilterApplied
|
||||
)
|
||||
|
||||
if isFiltered && index < len(m.VisibleItems()) {
|
||||
// Get indices of matched characters
|
||||
matchedRunes = m.MatchesForItem(index)
|
||||
}
|
||||
|
||||
// Apply styling and wrapping based on state
|
||||
if emptyFilter {
|
||||
title = wrapText(title, textwidth, s.DimmedTitle)
|
||||
if d.ShowDescription {
|
||||
desc = wrapText(desc, textwidth, s.DimmedDesc)
|
||||
}
|
||||
} else if isSelected && m.FilterState() != list.Filtering {
|
||||
if isFiltered {
|
||||
// Highlight matches
|
||||
unmatched := s.SelectedTitle.Inline(true)
|
||||
matched := unmatched.Inherit(s.FilterMatch)
|
||||
title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched)
|
||||
}
|
||||
title = wrapText(title, textwidth, s.SelectedTitle)
|
||||
if d.ShowDescription {
|
||||
desc = wrapText(desc, textwidth, s.SelectedDesc)
|
||||
}
|
||||
} else {
|
||||
if isFiltered {
|
||||
// Highlight matches
|
||||
unmatched := s.NormalTitle.Inline(true)
|
||||
matched := unmatched.Inherit(s.FilterMatch)
|
||||
title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched)
|
||||
}
|
||||
title = wrapText(title, textwidth, s.NormalTitle)
|
||||
if d.ShowDescription {
|
||||
desc = wrapText(desc, textwidth, s.NormalDesc)
|
||||
}
|
||||
}
|
||||
|
||||
if d.ShowDescription {
|
||||
fmt.Fprintf(w, "%s\n%s", title, desc) //nolint: errcheck
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "%s", title) //nolint: errcheck
|
||||
}
|
||||
|
||||
// ShortHelp returns the delegate's short help.
|
||||
func (d WrappingDelegate) ShortHelp() []key.Binding {
|
||||
if d.ShortHelpFunc != nil {
|
||||
return d.ShortHelpFunc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FullHelp returns the delegate's full help.
|
||||
func (d WrappingDelegate) FullHelp() [][]key.Binding {
|
||||
if d.FullHelpFunc != nil {
|
||||
return d.FullHelpFunc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue