mirror of
https://github.com/anotherhadi/ilovetui.git
synced 2026-08-21 12:05:49 +02:00
Change style package, new components, ...
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# bubbles
|
||||
|
||||
Themed constructors for every official `bubbles/v2` component: `help`, `textarea`, `textinput`,
|
||||
`list`, `table`, `filepicker`, `spinner`, `progress`, `paginator`, `viewport`. Each wraps the
|
||||
official `New`, then applies `style.S` colors on top.
|
||||
|
||||
- `spinner`, `table`, `progress` forward any `opts` to the upstream `New` before the theme is
|
||||
applied, so the theme's colors always win over anything conflicting in `opts`.
|
||||
- `ViewportView` renders a `viewport.Model` with a themed scrollbar thumb in the left gutter,
|
||||
shown only when content overflows.
|
||||
- `NewList`/`NewDefaultDelegate` theme both the list chrome and the selection/filter colors of its
|
||||
default delegate.
|
||||
|
||||
Custom components in this repo (`tabs`, `modal`, `drawer`, `notification`, `helpbar`) build any
|
||||
official component they need through here, never through `charm.land/bubbles/v2` directly.
|
||||
|
||||
See `examples/bubbles`.
|
||||
@@ -0,0 +1 @@
|
||||
package bubbles
|
||||
@@ -0,0 +1,25 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/filepicker"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
func NewFilePicker() filepicker.Model {
|
||||
f := filepicker.New()
|
||||
s := filepicker.DefaultStyles()
|
||||
s.Cursor = s.Cursor.Foreground(style.S.Primary)
|
||||
s.DisabledCursor = s.DisabledCursor.Foreground(style.S.Subtle)
|
||||
s.Symlink = s.Symlink.Foreground(style.S.Warning)
|
||||
s.Directory = s.Directory.Foreground(style.S.Primary)
|
||||
s.File = s.File.Foreground(style.S.Text)
|
||||
s.DisabledFile = s.DisabledFile.Foreground(style.S.Subtle)
|
||||
s.Permission = s.Permission.Foreground(style.S.Muted)
|
||||
s.Selected = s.Selected.Foreground(style.S.Primary)
|
||||
s.DisabledSelected = s.DisabledSelected.Foreground(style.S.Subtle)
|
||||
s.FileSize = s.FileSize.Foreground(style.S.Muted)
|
||||
s.EmptyDirectory = s.EmptyDirectory.Foreground(style.S.Subtle)
|
||||
f.Styles = s
|
||||
return f
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/help"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
func NewHelp() help.Model {
|
||||
h := help.New()
|
||||
h.Styles.ShortKey = lipgloss.NewStyle().Foreground(style.S.Primary)
|
||||
h.Styles.ShortDesc = lipgloss.NewStyle().Foreground(style.S.Muted)
|
||||
h.Styles.ShortSeparator = lipgloss.NewStyle().Foreground(style.S.Subtle)
|
||||
h.Styles.FullKey = lipgloss.NewStyle().Foreground(style.S.Primary)
|
||||
h.Styles.FullDesc = lipgloss.NewStyle().Foreground(style.S.Muted)
|
||||
h.Styles.FullSeparator = lipgloss.NewStyle().Foreground(style.S.Subtle)
|
||||
h.Styles.Ellipsis = lipgloss.NewStyle().Foreground(style.S.Subtle)
|
||||
return h
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/list"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
func NewList(items []list.Item, width, height int) list.Model {
|
||||
m := list.New(items, NewDefaultDelegate(), width, height)
|
||||
m.Styles = themedListStyles()
|
||||
return m
|
||||
}
|
||||
|
||||
func themedListStyles() list.Styles {
|
||||
s := list.DefaultStyles(true)
|
||||
s.Title = s.Title.Background(style.S.Primary).Foreground(style.S.Background)
|
||||
s.Spinner = s.Spinner.Foreground(style.S.Primary)
|
||||
s.Filter = themedTextInputStyles()
|
||||
s.DefaultFilterCharacterMatch = s.DefaultFilterCharacterMatch.Foreground(style.S.Primary)
|
||||
s.StatusBar = s.StatusBar.Foreground(style.S.Muted)
|
||||
s.StatusEmpty = s.StatusEmpty.Foreground(style.S.Subtle)
|
||||
s.StatusBarActiveFilter = s.StatusBarActiveFilter.Foreground(style.S.Text)
|
||||
s.StatusBarFilterCount = s.StatusBarFilterCount.Foreground(style.S.Subtle)
|
||||
s.NoItems = s.NoItems.Foreground(style.S.Subtle)
|
||||
s.ArabicPagination = s.ArabicPagination.Foreground(style.S.Subtle)
|
||||
s.ActivePaginationDot = s.ActivePaginationDot.Foreground(style.S.Primary)
|
||||
s.InactivePaginationDot = s.InactivePaginationDot.Foreground(style.S.Subtle)
|
||||
s.DividerDot = s.DividerDot.Foreground(style.S.Subtle)
|
||||
return s
|
||||
}
|
||||
|
||||
func NewDefaultDelegate() list.DefaultDelegate {
|
||||
d := list.NewDefaultDelegate()
|
||||
d.Styles.NormalTitle = d.Styles.NormalTitle.Foreground(style.S.Text)
|
||||
d.Styles.NormalDesc = d.Styles.NormalDesc.Foreground(style.S.Muted)
|
||||
d.Styles.SelectedTitle = d.Styles.SelectedTitle.
|
||||
BorderForeground(style.S.Primary).
|
||||
Foreground(style.S.Primary)
|
||||
d.Styles.SelectedDesc = d.Styles.SelectedDesc.
|
||||
BorderForeground(style.S.Primary).
|
||||
Foreground(style.S.Primary)
|
||||
d.Styles.DimmedTitle = d.Styles.DimmedTitle.Foreground(style.S.Subtle)
|
||||
d.Styles.DimmedDesc = d.Styles.DimmedDesc.Foreground(style.S.SubtleBg)
|
||||
d.Styles.FilterMatch = d.Styles.FilterMatch.Foreground(style.S.Primary)
|
||||
return d
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/paginator"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
func NewPaginator() paginator.Model {
|
||||
p := paginator.New()
|
||||
p.Type = paginator.Dots
|
||||
p.ActiveDot = lipgloss.NewStyle().Foreground(style.S.Primary).Render("•")
|
||||
p.InactiveDot = lipgloss.NewStyle().Foreground(style.S.Subtle).Render("•")
|
||||
return p
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/progress"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
func NewProgress(opts ...progress.Option) progress.Model {
|
||||
allOpts := append([]progress.Option{
|
||||
progress.WithColors(style.S.Subtle, style.S.Primary),
|
||||
}, opts...)
|
||||
p := progress.New(allOpts...)
|
||||
p.EmptyColor = style.S.SubtleBg
|
||||
return p
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/spinner"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
func NewSpinner(opts ...spinner.Option) spinner.Model {
|
||||
s := spinner.New(opts...)
|
||||
s.Style = lipgloss.NewStyle().Foreground(style.S.Primary)
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/table"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
func NewTable(opts ...table.Option) table.Model {
|
||||
t := table.New(opts...)
|
||||
s := table.DefaultStyles()
|
||||
s.Header = s.Header.Foreground(style.S.Primary)
|
||||
s.Cell = s.Cell.Foreground(style.S.Text)
|
||||
s.Selected = s.Selected.Foreground(style.S.Primary)
|
||||
t.SetStyles(s)
|
||||
return t
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/textarea"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
func NewTextarea(showLineNumbers bool) textarea.Model {
|
||||
ta := textarea.New()
|
||||
ta.Prompt = ""
|
||||
ta.ShowLineNumbers = showLineNumbers
|
||||
ta.CharLimit = 0
|
||||
ta.EndOfBufferCharacter = '~'
|
||||
ts := ta.Styles()
|
||||
ts.Focused.Base = lipgloss.NewStyle()
|
||||
ts.Blurred.Base = lipgloss.NewStyle()
|
||||
ts.Focused.Text = lipgloss.NewStyle().Foreground(style.S.Text)
|
||||
ts.Focused.CursorLine = lipgloss.NewStyle().Background(style.S.Selection).Foreground(style.S.Text)
|
||||
ts.Focused.CursorLineNumber = lipgloss.NewStyle().Background(style.S.Selection).Foreground(style.S.Primary).Bold(true)
|
||||
ts.Focused.LineNumber = lipgloss.NewStyle().Foreground(style.S.Subtle)
|
||||
ts.Focused.Placeholder = lipgloss.NewStyle().Foreground(style.S.Subtle)
|
||||
ts.Focused.EndOfBuffer = lipgloss.NewStyle().Foreground(style.S.SubtleBg)
|
||||
ts.Blurred.Text = lipgloss.NewStyle().Foreground(style.S.Muted)
|
||||
ts.Blurred.LineNumber = lipgloss.NewStyle().Foreground(style.S.SubtleBg)
|
||||
ts.Blurred.Placeholder = lipgloss.NewStyle().Foreground(style.S.Subtle)
|
||||
ts.Blurred.EndOfBuffer = lipgloss.NewStyle().Foreground(style.S.SubtleBg)
|
||||
ta.SetStyles(ts)
|
||||
return ta
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/textinput"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
func NewTextInput() textinput.Model {
|
||||
t := textinput.New()
|
||||
t.SetStyles(themedTextInputStyles())
|
||||
return t
|
||||
}
|
||||
|
||||
func themedTextInputStyles() textinput.Styles {
|
||||
s := textinput.DefaultStyles(true)
|
||||
s.Focused.Text = s.Focused.Text.Foreground(style.S.Text)
|
||||
s.Focused.Placeholder = s.Focused.Placeholder.Foreground(style.S.Subtle)
|
||||
s.Focused.Suggestion = s.Focused.Suggestion.Foreground(style.S.Subtle)
|
||||
s.Focused.Prompt = s.Focused.Prompt.Foreground(style.S.Primary)
|
||||
s.Blurred.Text = s.Blurred.Text.Foreground(style.S.Muted)
|
||||
s.Blurred.Placeholder = s.Blurred.Placeholder.Foreground(style.S.Subtle)
|
||||
s.Blurred.Suggestion = s.Blurred.Suggestion.Foreground(style.S.Subtle)
|
||||
s.Blurred.Prompt = s.Blurred.Prompt.Foreground(style.S.Subtle)
|
||||
s.Cursor.Color = style.S.Primary
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/viewport"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
const ViewportGutterWidth = 2
|
||||
|
||||
func NewViewport() viewport.Model {
|
||||
vp := viewport.New()
|
||||
vp.MouseWheelEnabled = false
|
||||
return vp
|
||||
}
|
||||
|
||||
func ViewportView(vp *viewport.Model) string {
|
||||
height := vp.Height()
|
||||
total := vp.TotalLineCount()
|
||||
blank := lipgloss.NewStyle().Width(ViewportGutterWidth).Render("")
|
||||
|
||||
if height <= 0 || total <= height {
|
||||
vp.LeftGutterFunc = func(viewport.GutterContext) string { return blank }
|
||||
return vp.View()
|
||||
}
|
||||
|
||||
yOffset := vp.YOffset()
|
||||
thumbSize := max(1, height*height/total)
|
||||
thumbStart := 0
|
||||
if maxOffset := total - height; maxOffset > 0 {
|
||||
thumbStart = yOffset * (height - thumbSize) / maxOffset
|
||||
}
|
||||
|
||||
trackStyle := lipgloss.NewStyle().Foreground(style.S.Subtle)
|
||||
thumbStyle := lipgloss.NewStyle().Foreground(style.S.Primary)
|
||||
|
||||
vp.LeftGutterFunc = func(ctx viewport.GutterContext) string {
|
||||
pos := ctx.Index - yOffset
|
||||
if pos >= thumbStart && pos < thumbStart+thumbSize {
|
||||
return thumbStyle.Render("█") + " "
|
||||
}
|
||||
return trackStyle.Render("│") + " "
|
||||
}
|
||||
|
||||
return vp.View()
|
||||
}
|
||||
Reference in New Issue
Block a user