Skip to content

Commit

Permalink
feat: add basic themeing colors and md (#80)
Browse files Browse the repository at this point in the history
adds color options for the app title, selected item and markdown viewer.
Currently basic but could be fleshed out more in future if desired

closes #76
  • Loading branch information
guyfedwards authored Jun 4, 2024
1 parent f131439 commit 3f0f4d6
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 24 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ Automatically mark items as read on selection or navigation through items.
```yaml
autoread: true
```
### Theme
Theme allows some basic color overrides in the feed view and then setting a custom markdown render theme for the overall markdown view. `theme.glamour` can be one of "dark", "dracula", "light", "pink", "ascii" or "notty". See [here](https://github.com/charmbracelet/glamour/tree/master/styles/gallery) for previews and more info.
Colors can be hex or ASCII codes, they will be coerced depending on your terminal color settings.
```yaml
theme:
glamour: dark
titleColor: "62"
selectedItemColor: "170"
filterColor: "#555555"
```

### Backends
As well as adding feeds directly, you can pull in feeds from another source. You can add multiple backends and the feeds will all be added.
Expand Down
34 changes: 30 additions & 4 deletions internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/glamour"
"github.com/charmbracelet/glamour/ansi"

"github.com/guyfedwards/nom/v2/internal/config"
"github.com/guyfedwards/nom/v2/internal/rss"
Expand Down Expand Up @@ -188,7 +189,7 @@ func (c Commands) TUI() error {
es = append(es, fmt.Sprintf("Error fetching %s: %s", e.FeedURL, e.Err))
}

if err := Render(items, c, es); err != nil {
if err := Render(items, c, es, c.config); err != nil {
return fmt.Errorf("commands.TUI: %w", err)
}

Expand Down Expand Up @@ -401,15 +402,36 @@ func (c Commands) GetGlamourisedArticle(ID int) (string, error) {
}
}

content, err := glamouriseItem(article)
content, err := glamouriseItem(article, c.config.Theme)
if err != nil {
return "", fmt.Errorf("[commands.go] GetGlamourisedArticle: %w", err)
}

return content, nil
}

func glamouriseItem(item store.Item) (string, error) {
func getStyleConfigWithOverrides(theme config.Theme) (sc ansi.StyleConfig) {
switch theme.Glamour {
case "light":
sc = glamour.LightStyleConfig
case "dracula":
sc = glamour.DraculaStyleConfig
case "pink":
sc = glamour.PinkStyleConfig
case "ascii":
sc = glamour.ASCIIStyleConfig
case "notty":
sc = glamour.NoTTYStyleConfig
default:
sc = glamour.DarkStyleConfig
}

sc.H1.BackgroundColor = &theme.TitleColor

return sc
}

func glamouriseItem(item store.Item, theme config.Theme) (string, error) {
var mdown string

mdown += "# " + item.Title
Expand All @@ -424,7 +446,11 @@ func glamouriseItem(item store.Item) (string, error) {
mdown += "\n\n"
mdown += htmlToMd(item.Content)

out, err := glamour.Render(mdown, "dark")
r, _ := glamour.NewTermRenderer(
glamour.WithStyles(getStyleConfigWithOverrides(theme)),
)

out, err := r.Render(mdown)
if err != nil {
return "", fmt.Errorf("GlamouriseItem: %w", err)
}
Expand Down
31 changes: 22 additions & 9 deletions internal/commands/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ import (
"github.com/sahilm/fuzzy"
"golang.org/x/term"

"github.com/guyfedwards/nom/v2/internal/config"
"github.com/guyfedwards/nom/v2/internal/store"
)

var (
appStyle = lipgloss.NewStyle().Padding(1, 0, 0, 0).Margin(0)
titleStyle = list.DefaultStyles().Title.Margin(0).Width(5)
itemStyle = lipgloss.NewStyle().PaddingLeft(4)
selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170"))
selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2)
readStyle = lipgloss.NewStyle().PaddingLeft(4).Foreground(lipgloss.Color("240"))
selectedReadStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170"))
selectedReadStyle = lipgloss.NewStyle().PaddingLeft(2)
favouriteStyle = itemStyle.Copy().PaddingLeft(2).Bold(true)
selectedFavouriteStyle = selectedItemStyle.Copy().Bold(true)
paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
Expand All @@ -48,7 +49,9 @@ type TUIItem struct {

func (i TUIItem) FilterValue() string { return fmt.Sprintf("%s||%s", i.Title, i.FeedName) }

type itemDelegate struct{}
type itemDelegate struct {
theme config.Theme
}

func (d itemDelegate) Height() int { return 1 }
func (d itemDelegate) Spacing() int { return 0 }
Expand Down Expand Up @@ -84,9 +87,9 @@ func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list
return selectedReadStyle.Render("> " + strings.Join(s, " "))
}
if i.Favourite {
return selectedFavouriteStyle.Render("* " + strings.Join(s, " "))
return selectedFavouriteStyle.Foreground(lipgloss.Color(d.theme.SelectedItemColor)).Render("* " + strings.Join(s, " "))
}
return selectedItemStyle.Render("> " + strings.Join(s, " "))
return selectedItemStyle.Foreground(lipgloss.Color(d.theme.SelectedItemColor)).Render("> " + strings.Join(s, " "))
}
}

Expand All @@ -100,6 +103,7 @@ type model struct {
selectedArticle *int
viewport viewport.Model
errors []string
cfg config.Config
}

func (m model) Init() tea.Cmd {
Expand Down Expand Up @@ -552,27 +556,36 @@ func CustomFilter(term string, targets []string) []list.Rank {

const defaultTitle = "nom"

func Render(items []list.Item, cmds Commands, errors []string) error {
func Render(items []list.Item, cmds Commands, errors []string, cfg config.Config) error {
const defaultWidth = 20
_, ts, _ := term.GetSize(int(os.Stdout.Fd()))
_, y := appStyle.GetFrameSize()
height := ts - y

appStyle.Height(height)

l := list.New(items, itemDelegate{}, defaultWidth, height)
l := list.New(items, itemDelegate{theme: cfg.Theme}, defaultWidth, height)
l.SetShowStatusBar(false)
l.Title = defaultTitle
l.Styles.Title = titleStyle
l.Styles.Title = titleStyle.Background(lipgloss.Color(cfg.Theme.TitleColor))
l.Styles.PaginationStyle = paginationStyle
l.Styles.HelpStyle = helpStyle

l.FilterInput.PromptStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(cfg.Theme.FilterColor))
l.Filter = CustomFilter

ListKeyMap.SetOverrides(&l)

vp := viewport.New(78, height)

m := model{list: l, commands: cmds, viewport: vp, errors: errors, help: help.New()}
m := model{
cfg: cfg,
commands: cmds,
errors: errors,
help: help.New(),
list: l,
viewport: vp,
}

if _, err := tea.NewProgram(m, tea.WithAltScreen()).Run(); err != nil {
return fmt.Errorf("tui.Render: %w", err)
Expand Down
52 changes: 41 additions & 11 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,28 @@ type Opener struct {
Cmd string `yaml:"cmd"`
}

type Theme struct {
Glamour string `yaml:"glamour,omitempty"`
TitleColor string `yaml:"titleColor,omitempty"`
FilterColor string `yaml:"filterColor,omitempty"`
SelectedItemColor string `yaml:"selectedItemColor,omitempty"`
}

// need to add to Load() below if loading from config file
type Config struct {
configPath string
ConfigDir string `yaml:"-"`
Pager string `yaml:"pager,omitempty"`
Feeds []Feed `yaml:"feeds"`
// Preview feeds are distinguished from Feeds because we don't want to inadvertenly write those into the config file.
PreviewFeeds []Feed `yaml:"previewfeeds,omitempty"`
Backends *Backends `yaml:"backends,omitempty"`
ShowRead bool `yaml:"showread,omitempty"`
AutoRead bool `yaml:"autoread,omitempty"`
configPath string
ShowFavourites bool
Openers []Opener `yaml:"openers,omitempty"`
Version string
ConfigDir string `yaml:"-"`
Pager string `yaml:"pager,omitempty"`
Feeds []Feed `yaml:"feeds"`
// Preview feeds are distinguished from Feeds because we don't want to inadvertenly write those into the config file.
PreviewFeeds []Feed `yaml:"previewfeeds,omitempty"`
Backends *Backends `yaml:"backends,omitempty"`
ShowRead bool `yaml:"showread,omitempty"`
AutoRead bool `yaml:"autoread,omitempty"`
Openers []Opener `yaml:"openers,omitempty"`
Theme Theme `yaml:"theme,omitempty"`
}

func (c *Config) ToggleShowRead() {
Expand Down Expand Up @@ -89,6 +97,12 @@ func New(configPath string, pager string, previewFeeds []string, version string)
Pager: pager,
Feeds: []Feed{},
PreviewFeeds: f,
Theme: Theme{
Glamour: "dark",
SelectedItemColor: "170",
TitleColor: "62",
FilterColor: "62",
},
}, nil
}

Expand Down Expand Up @@ -116,9 +130,25 @@ func (c *Config) Load() error {

c.ShowRead = fileConfig.ShowRead
c.AutoRead = fileConfig.AutoRead

c.Feeds = fileConfig.Feeds
c.Openers = fileConfig.Openers

if fileConfig.Theme.Glamour != "" {
c.Theme.Glamour = fileConfig.Theme.Glamour
}

if fileConfig.Theme.SelectedItemColor != "" {
c.Theme.SelectedItemColor = fileConfig.Theme.SelectedItemColor
}

if fileConfig.Theme.TitleColor != "" {
c.Theme.TitleColor = fileConfig.Theme.TitleColor
}

if fileConfig.Theme.FilterColor != "" {
c.Theme.FilterColor = fileConfig.Theme.FilterColor
}

// only set pager if it's not defined already, config file is lower
// precidence than flags/env that can be passed to New
if c.Pager == "" {
Expand Down

0 comments on commit 3f0f4d6

Please sign in to comment.