mirror of
				https://github.com/Maks1mS/bubbles.git
				synced 2025-11-03 23:21:22 +03:00 
			
		
		
		
	TextInput now uses Lip Gloss for styling
This commit is contained in:
		@@ -8,15 +8,12 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/atotto/clipboard"
 | 
						"github.com/atotto/clipboard"
 | 
				
			||||||
	tea "github.com/charmbracelet/bubbletea"
 | 
						tea "github.com/charmbracelet/bubbletea"
 | 
				
			||||||
 | 
						"github.com/charmbracelet/lipgloss"
 | 
				
			||||||
	rw "github.com/mattn/go-runewidth"
 | 
						rw "github.com/mattn/go-runewidth"
 | 
				
			||||||
	"github.com/muesli/termenv"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const defaultBlinkSpeed = time.Millisecond * 530
 | 
					const defaultBlinkSpeed = time.Millisecond * 530
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// color is a helper for returning colors.
 | 
					 | 
				
			||||||
var color func(s string) termenv.Color = termenv.ColorProfile().Color
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// blinkMsg and blinkCanceled are used to manage cursor blinking.
 | 
					// blinkMsg and blinkCanceled are used to manage cursor blinking.
 | 
				
			||||||
type blinkMsg struct{}
 | 
					type blinkMsg struct{}
 | 
				
			||||||
type blinkCanceled struct{}
 | 
					type blinkCanceled struct{}
 | 
				
			||||||
@@ -61,18 +58,21 @@ const (
 | 
				
			|||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	Err error
 | 
						Err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// General settings
 | 
						// General settings.
 | 
				
			||||||
	Prompt        string
 | 
						Prompt        string
 | 
				
			||||||
	Placeholder   string
 | 
						Placeholder   string
 | 
				
			||||||
	Cursor        string
 | 
						Cursor        string
 | 
				
			||||||
	BlinkSpeed    time.Duration
 | 
						BlinkSpeed    time.Duration
 | 
				
			||||||
	TextColor        string
 | 
					 | 
				
			||||||
	BackgroundColor  string
 | 
					 | 
				
			||||||
	PlaceholderColor string
 | 
					 | 
				
			||||||
	CursorColor      string
 | 
					 | 
				
			||||||
	EchoMode      EchoMode
 | 
						EchoMode      EchoMode
 | 
				
			||||||
	EchoCharacter rune
 | 
						EchoCharacter rune
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Styles.
 | 
				
			||||||
 | 
						PromptStyle      lipgloss.Style
 | 
				
			||||||
 | 
						TextStyle        lipgloss.Style
 | 
				
			||||||
 | 
						BackgroundStyle  lipgloss.Style
 | 
				
			||||||
 | 
						PlaceholderStyle lipgloss.Style
 | 
				
			||||||
 | 
						CursorStyle      lipgloss.Style
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// CharLimit is the maximum amount of characters this input element will
 | 
						// CharLimit is the maximum amount of characters this input element will
 | 
				
			||||||
	// accept. If 0 or less, there's no limit.
 | 
						// accept. If 0 or less, there's no limit.
 | 
				
			||||||
	CharLimit int
 | 
						CharLimit int
 | 
				
			||||||
@@ -111,13 +111,10 @@ type Model struct {
 | 
				
			|||||||
func NewModel() Model {
 | 
					func NewModel() Model {
 | 
				
			||||||
	return Model{
 | 
						return Model{
 | 
				
			||||||
		Prompt:           "> ",
 | 
							Prompt:           "> ",
 | 
				
			||||||
		Placeholder:      "",
 | 
					 | 
				
			||||||
		BlinkSpeed:       defaultBlinkSpeed,
 | 
							BlinkSpeed:       defaultBlinkSpeed,
 | 
				
			||||||
		TextColor:        "",
 | 
					 | 
				
			||||||
		PlaceholderColor: "240",
 | 
					 | 
				
			||||||
		CursorColor:      "",
 | 
					 | 
				
			||||||
		EchoCharacter:    '*',
 | 
							EchoCharacter:    '*',
 | 
				
			||||||
		CharLimit:        0,
 | 
							CharLimit:        0,
 | 
				
			||||||
 | 
							PlaceholderStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("240")),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		value:      nil,
 | 
							value:      nil,
 | 
				
			||||||
		focus:      false,
 | 
							focus:      false,
 | 
				
			||||||
@@ -291,26 +288,6 @@ func (m *Model) handleOverflow() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// colorText colorizes a given string according to the TextColor value of the
 | 
					 | 
				
			||||||
// model.
 | 
					 | 
				
			||||||
func (m *Model) colorText(s string) string {
 | 
					 | 
				
			||||||
	return termenv.
 | 
					 | 
				
			||||||
		String(s).
 | 
					 | 
				
			||||||
		Foreground(color(m.TextColor)).
 | 
					 | 
				
			||||||
		Background(color(m.BackgroundColor)).
 | 
					 | 
				
			||||||
		String()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// colorPlaceholder colorizes a given string according to the TextColor value
 | 
					 | 
				
			||||||
// of the model.
 | 
					 | 
				
			||||||
func (m *Model) colorPlaceholder(s string) string {
 | 
					 | 
				
			||||||
	return termenv.
 | 
					 | 
				
			||||||
		String(s).
 | 
					 | 
				
			||||||
		Foreground(color(m.PlaceholderColor)).
 | 
					 | 
				
			||||||
		Background(color(m.BackgroundColor)).
 | 
					 | 
				
			||||||
		String()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// deleteWordLeft deletes the word left to the cursor. Returns whether or not
 | 
					// deleteWordLeft deletes the word left to the cursor. Returns whether or not
 | 
				
			||||||
// the cursor blink should be reset.
 | 
					// the cursor blink should be reset.
 | 
				
			||||||
func (m *Model) deleteWordLeft() bool {
 | 
					func (m *Model) deleteWordLeft() bool {
 | 
				
			||||||
@@ -564,13 +541,15 @@ func (m Model) View() string {
 | 
				
			|||||||
		return m.placeholderView()
 | 
							return m.placeholderView()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						styleText := m.TextStyle.Inline(true).Render
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	value := m.value[m.offset:m.offsetRight]
 | 
						value := m.value[m.offset:m.offsetRight]
 | 
				
			||||||
	pos := max(0, m.pos-m.offset)
 | 
						pos := max(0, m.pos-m.offset)
 | 
				
			||||||
	v := m.colorText(m.echoTransform(string(value[:pos])))
 | 
						v := styleText(m.echoTransform(string(value[:pos])))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if pos < len(value) {
 | 
						if pos < len(value) {
 | 
				
			||||||
		v += m.cursorView(m.echoTransform(string(value[pos]))) // cursor and text under it
 | 
							v += m.cursorView(m.echoTransform(string(value[pos]))) // cursor and text under it
 | 
				
			||||||
		v += m.colorText(m.echoTransform(string(value[pos+1:]))) // text after cursor
 | 
							v += styleText(m.echoTransform(string(value[pos+1:]))) // text after cursor
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		v += m.cursorView(" ")
 | 
							v += m.cursorView(" ")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -578,18 +557,15 @@ func (m Model) View() string {
 | 
				
			|||||||
	// If a max width and background color were set fill the empty spaces with
 | 
						// If a max width and background color were set fill the empty spaces with
 | 
				
			||||||
	// the background color.
 | 
						// the background color.
 | 
				
			||||||
	valWidth := rw.StringWidth(string(value))
 | 
						valWidth := rw.StringWidth(string(value))
 | 
				
			||||||
	if m.Width > 0 && len(m.BackgroundColor) > 0 && valWidth <= m.Width {
 | 
						if m.Width > 0 && valWidth <= m.Width {
 | 
				
			||||||
		padding := max(0, m.Width-valWidth)
 | 
							padding := max(0, m.Width-valWidth)
 | 
				
			||||||
		if valWidth+padding <= m.Width && pos < len(value) {
 | 
							if valWidth+padding <= m.Width && pos < len(value) {
 | 
				
			||||||
			padding++
 | 
								padding++
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		v += strings.Repeat(
 | 
							v += styleText(strings.Repeat(" ", padding))
 | 
				
			||||||
			termenv.String(" ").Background(color(m.BackgroundColor)).String(),
 | 
					 | 
				
			||||||
			padding,
 | 
					 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return m.Prompt + v
 | 
						return m.PromptStyle.Render(m.Prompt) + v
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// placeholderView returns the prompt and placeholder view, if any.
 | 
					// placeholderView returns the prompt and placeholder view, if any.
 | 
				
			||||||
@@ -597,37 +573,28 @@ func (m Model) placeholderView() string {
 | 
				
			|||||||
	var (
 | 
						var (
 | 
				
			||||||
		v     string
 | 
							v     string
 | 
				
			||||||
		p     = m.Placeholder
 | 
							p     = m.Placeholder
 | 
				
			||||||
 | 
							style = m.PlaceholderStyle.Inline(true).Render
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Cursor
 | 
						// Cursor
 | 
				
			||||||
	if m.blink && m.PlaceholderColor != "" {
 | 
						if m.blink {
 | 
				
			||||||
		v += m.cursorView(m.colorPlaceholder(p[:1]))
 | 
							v += m.cursorView(style(p[:1]))
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		v += m.cursorView(p[:1])
 | 
							v += m.cursorView(p[:1])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// The rest of the placeholder text
 | 
						// The rest of the placeholder text
 | 
				
			||||||
	v += m.colorPlaceholder(p[1:])
 | 
						v += style(p[1:])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return m.Prompt + v
 | 
						return m.PromptStyle.Render(m.Prompt) + v
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// cursorView styles the cursor.
 | 
					// cursorView styles the cursor.
 | 
				
			||||||
func (m Model) cursorView(v string) string {
 | 
					func (m Model) cursorView(v string) string {
 | 
				
			||||||
	if m.blink {
 | 
						if m.blink {
 | 
				
			||||||
		if m.TextColor != "" || m.BackgroundColor != "" {
 | 
							return m.TextStyle.Render(v)
 | 
				
			||||||
			return termenv.String(v).
 | 
					 | 
				
			||||||
				Foreground(color(m.TextColor)).
 | 
					 | 
				
			||||||
				Background(color(m.BackgroundColor)).
 | 
					 | 
				
			||||||
				String()
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		return v
 | 
						return m.CursorStyle.Inline(true).Reverse(true).Render(v)
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return termenv.String(v).
 | 
					 | 
				
			||||||
		Foreground(color(m.CursorColor)).
 | 
					 | 
				
			||||||
		Background(color(m.BackgroundColor)).
 | 
					 | 
				
			||||||
		Reverse().
 | 
					 | 
				
			||||||
		String()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// blinkCmd is an internal command used to manage cursor blinking.
 | 
					// blinkCmd is an internal command used to manage cursor blinking.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user