mirror of
https://github.com/anotherhadi/ilovetui.git
synced 2026-08-21 12:05:49 +02:00
init layout, notifications, tabs & more
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
// Package bubbles provides themed constructors for official bubbles/v2
|
||||
// components (help, textarea, textinput, list, table, filepicker, spinner,
|
||||
// progress, paginator, viewport). Each constructor mirrors the official
|
||||
// component's own New function, then applies the shared ilovetui/style
|
||||
// theme on top.
|
||||
package bubbles
|
||||
@@ -0,0 +1,26 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/filepicker"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
// NewFilePicker returns a filepicker.Model styled with the active theme.
|
||||
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,21 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/help"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
// NewHelp returns a help.Model styled with the active theme.
|
||||
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,13 @@
|
||||
package bubbles
|
||||
|
||||
import "strings"
|
||||
|
||||
// SplitH splits totalHeight into top and bottom sections, accounting for the
|
||||
// height of statusBar (measured by newline count).
|
||||
func SplitH(totalHeight int, statusBar string, ratio float64) (top, bottom int) {
|
||||
statusH := strings.Count(statusBar, "\n") + 1
|
||||
available := totalHeight - statusH
|
||||
top = int(float64(available) * ratio)
|
||||
bottom = available - top
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/list"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
// NewList returns a list.Model styled with the active theme, using
|
||||
// NewDefaultDelegate for item rendering.
|
||||
func NewList(items []list.Item, width, height int) list.Model {
|
||||
m := list.New(items, NewDefaultDelegate(), width, height)
|
||||
m.Styles = themedListStyles()
|
||||
return m
|
||||
}
|
||||
|
||||
// themedListStyles builds list.Styles from the active theme.
|
||||
func themedListStyles() list.Styles {
|
||||
// isDark only affects a couple of fallback colors below, all of which
|
||||
// are overridden regardless.
|
||||
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
|
||||
}
|
||||
|
||||
// NewDefaultDelegate returns a list.DefaultDelegate styled with the active
|
||||
// theme, for use with NewList or a custom list.Model.
|
||||
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,17 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/paginator"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
// NewPaginator returns a dot-style paginator.Model styled with the active theme.
|
||||
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,19 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/progress"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
// NewProgress returns a progress.Model with a themed fill, blending from
|
||||
// style.S.Subtle to style.S.Primary. Any opts are forwarded to progress.New;
|
||||
// pass progress.WithColors to override the default blend.
|
||||
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,16 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/spinner"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
// NewSpinner returns a spinner.Model styled with the active theme. Any opts
|
||||
// are forwarded to spinner.New before the theme is applied.
|
||||
func NewSpinner(opts ...spinner.Option) spinner.Model {
|
||||
s := spinner.New(opts...)
|
||||
s.Style = lipgloss.NewStyle().Foreground(style.S.Primary)
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/table"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
// NewTable returns a table.Model styled with the active theme. Any opts are
|
||||
// forwarded to table.New before the theme is applied.
|
||||
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,33 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/textarea"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
// NewTextarea returns a textarea.Model styled with the active theme.
|
||||
// Set showLineNumbers to true to display line numbers in the gutter.
|
||||
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,32 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"charm.land/bubbles/v2/textinput"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
// NewTextInput returns a textinput.Model styled with the active theme.
|
||||
func NewTextInput() textinput.Model {
|
||||
t := textinput.New()
|
||||
t.SetStyles(themedTextInputStyles())
|
||||
return t
|
||||
}
|
||||
|
||||
// themedTextInputStyles builds textinput.Styles from the active theme.
|
||||
// Shared with NewList, which themes its filter input the same way.
|
||||
func themedTextInputStyles() textinput.Styles {
|
||||
// isDark only affects textinput.DefaultStyles' Blurred.Text color, which
|
||||
// we override below regardless.
|
||||
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,38 @@
|
||||
package bubbles
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"charm.land/bubbles/v2/viewport"
|
||||
"charm.land/lipgloss/v2"
|
||||
|
||||
"github.com/anotherhadi/ilovetui/style"
|
||||
)
|
||||
|
||||
// NewViewport returns a viewport.Model with mouse wheel disabled.
|
||||
func NewViewport() viewport.Model {
|
||||
vp := viewport.New()
|
||||
vp.MouseWheelEnabled = false
|
||||
return vp
|
||||
}
|
||||
|
||||
// ViewportView renders the viewport and appends a subtle scroll indicator
|
||||
// on the last visible line when the user has not reached the bottom.
|
||||
func ViewportView(vp *viewport.Model) string {
|
||||
v := vp.View()
|
||||
if vp.AtBottom() {
|
||||
return v
|
||||
}
|
||||
lines := strings.Split(v, "\n")
|
||||
if len(lines) == 0 {
|
||||
return v
|
||||
}
|
||||
arrow := lipgloss.NewStyle().Foreground(style.S.Subtle).Render("↓")
|
||||
arrowW := lipgloss.Width(arrow)
|
||||
inner := vp.Width() - 2*arrowW
|
||||
if inner < 0 {
|
||||
inner = 0
|
||||
}
|
||||
lines[len(lines)-1] = arrow + strings.Repeat(" ", inner) + arrow
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
Reference in New Issue
Block a user