Skip to content

Commit 7baf332

Browse files
committed
feat(ui): update styles for article, task, and publication views to use new, consistent lipgloss color palette
1 parent 19af9a3 commit 7baf332

File tree

9 files changed

+152
-98
lines changed

9 files changed

+152
-98
lines changed

internal/handlers/articles.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func (h *ArticleHandler) Close() error {
112112
func (h *ArticleHandler) Add(ctx context.Context, url string) error {
113113
existing, err := h.repos.Articles.GetByURL(ctx, url)
114114
if err == nil {
115-
ui.Warningln("Article already exists: %s (ID: %d)", ui.TitleColorStyle.Render(existing.Title), existing.ID)
115+
ui.Warningln("Article already exists: %s (ID: %d)", ui.TableTitleStyle.Render(existing.Title), existing.ID)
116116
return nil
117117
}
118118

@@ -153,9 +153,9 @@ func (h *ArticleHandler) Add(ctx context.Context, url string) error {
153153

154154
ui.Infoln("Article saved successfully!")
155155
ui.Infoln("ID: %d", id)
156-
ui.Infoln("Title: %s", ui.TitleColorStyle.Render(article.Title))
156+
ui.Infoln("Title: %s", ui.TableTitleStyle.Render(article.Title))
157157
if article.Author != "" {
158-
ui.Infoln("Author: %s", ui.HeaderColorStyle.Render(article.Author))
158+
ui.Infoln("Author: %s", ui.TableHeaderStyle.Render(article.Author))
159159
}
160160
if article.Date != "" {
161161
ui.Infoln("Date: %s", article.Date)
@@ -187,9 +187,9 @@ func (h *ArticleHandler) List(ctx context.Context, query string, author string,
187187
ui.Infoln("Found %d article(s):\n", len(articles))
188188
for _, article := range articles {
189189
ui.Infoln("ID: %d", article.ID)
190-
ui.Infoln("Title: %s", ui.TitleColorStyle.Render(article.Title))
190+
ui.Infoln("Title: %s", ui.TableTitleStyle.Render(article.Title))
191191
if article.Author != "" {
192-
ui.Infoln("Author: %s", ui.HeaderColorStyle.Render(article.Author))
192+
ui.Infoln("Author: %s", ui.TableHeaderStyle.Render(article.Author))
193193
}
194194
if article.Date != "" {
195195
ui.Infoln("Date: %s", article.Date)
@@ -208,9 +208,9 @@ func (h *ArticleHandler) View(ctx context.Context, id int64) error {
208208
return fmt.Errorf("failed to get article: %w", err)
209209
}
210210

211-
ui.Infoln("Title: %s", ui.TitleColorStyle.Render(article.Title))
211+
ui.Infoln("Title: %s", ui.TableTitleStyle.Render(article.Title))
212212
if article.Author != "" {
213-
ui.Infoln("Author: %s", ui.HeaderColorStyle.Render(article.Author))
213+
ui.Infoln("Author: %s", ui.TableHeaderStyle.Render(article.Author))
214214
}
215215
if article.Date != "" {
216216
ui.Infoln("Date: %s", article.Date)
@@ -302,7 +302,7 @@ func (h *ArticleHandler) Help() error {
302302
if err != nil {
303303
return fmt.Errorf("failed to get storage directory: %w", err)
304304
}
305-
ui.Headerln("%s %s", ui.HeaderColorStyle.Render("Storage directory:"), dir)
305+
ui.Headerln("%s %s", ui.TableHeaderStyle.Render("Storage directory:"), dir)
306306

307307
return nil
308308
}

internal/ui/common.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// TODO: create variants of colored output without icons
2-
// TODO: refactor existing (relevant) calls to old styles
3-
// TODO: k v wrappers
41
package ui
52

63
import (
@@ -9,12 +6,14 @@ import (
96
"github.com/charmbracelet/lipgloss"
107
)
118

9+
// Style constructor helpers
1210
func newStyle() lipgloss.Style { return lipgloss.NewStyle() }
1311
func newPStyle(v, h int) lipgloss.Style { return lipgloss.NewStyle().Padding(v, h) }
1412
func newBoldStyle() lipgloss.Style { return newStyle().Bold(true) }
1513
func newPBoldStyle(v, h int) lipgloss.Style { return newPStyle(v, h).Bold(true) }
1614
func newEmStyle() lipgloss.Style { return newStyle().Italic(true) }
1715

16+
// Rendering helpers (private, used by public API)
1817
func success(msg string) string { return SuccessStyle.Render("✓ " + msg) }
1918
func errorMsg(msg string) string { return ErrorStyle.Render("✗ " + msg) }
2019
func warning(msg string) string { return WarningStyle.Render("⚠ " + msg) }
@@ -25,7 +24,10 @@ func subtitle(msg string) string { return SubtitleStyle.Render(msg) }
2524
func box(content string) string { return BoxStyle.Render(content) }
2625
func errorBox(content string) string { return ErrorBoxStyle.Render(content) }
2726
func text(content string) string { return TextStyle.Render(content) }
27+
func muted(content string) string { return MutedStyle.Render(content) }
28+
func accent(content string) string { return AccentStyle.Render(content) }
2829
func header(content string) string { return HeaderStyle.Render(content) }
30+
func primary(content string) string { return PrimaryStyle.Render(content) }
2931

3032
// Success prints a formatted success message
3133
func Success(format string, a ...any) {
@@ -121,4 +123,34 @@ func Newline() { fmt.Println() }
121123
func Plain(format string, a ...any) { fmt.Print(text(fmt.Sprintf(format, a...))) }
122124
func Plainln(format string, a ...any) { fmt.Println(text(fmt.Sprintf(format, a...))) }
123125
func Header(format string, a ...any) { fmt.Print(header(fmt.Sprintf(format, a...))) }
124-
func Headerln(format string, a ...any) { fmt.Print(header(fmt.Sprintf(format, a...))) }
126+
func Headerln(format string, a ...any) { fmt.Println(header(fmt.Sprintf(format, a...))) }
127+
128+
// Muted prints muted/secondary text
129+
func Muted(format string, a ...any) {
130+
fmt.Print(muted(fmt.Sprintf(format, a...)))
131+
}
132+
133+
// Mutedln prints muted/secondary text with a newline
134+
func Mutedln(format string, a ...any) {
135+
fmt.Println(muted(fmt.Sprintf(format, a...)))
136+
}
137+
138+
// Accent prints accent-colored text
139+
func Accent(format string, a ...any) {
140+
fmt.Print(accent(fmt.Sprintf(format, a...)))
141+
}
142+
143+
// Accentln prints accent-colored text with a newline
144+
func Accentln(format string, a ...any) {
145+
fmt.Println(accent(fmt.Sprintf(format, a...)))
146+
}
147+
148+
// Primary prints primary-colored text
149+
func Primary(format string, a ...any) {
150+
fmt.Print(primary(fmt.Sprintf(format, a...)))
151+
}
152+
153+
// Primaryln prints primary-colored text with a newline
154+
func Primaryln(format string, a ...any) {
155+
fmt.Println(primary(fmt.Sprintf(format, a...)))
156+
}

internal/ui/data_list.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/charmbracelet/bubbles/key"
1212
"github.com/charmbracelet/bubbles/viewport"
1313
tea "github.com/charmbracelet/bubbletea"
14-
"github.com/charmbracelet/lipgloss"
1514
"github.com/stormlightlabs/noteleaf/internal/models"
1615
)
1716

@@ -315,7 +314,7 @@ func (m dataListModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
315314
func (m dataListModel) View() string {
316315
var s strings.Builder
317316

318-
style := lipgloss.NewStyle().Foreground(lipgloss.Color(Squid.Hex()))
317+
style := MutedStyle
319318

320319
if m.showingHelp {
321320
return m.help.View(m.keys)
@@ -328,7 +327,7 @@ func (m dataListModel) View() string {
328327
return s.String()
329328
}
330329

331-
s.WriteString(TitleColorStyle.Render(m.opts.Title))
330+
s.WriteString(TableTitleStyle.Render(m.opts.Title))
332331
if m.totalCount > 0 {
333332
s.WriteString(fmt.Sprintf(" (%d total)", m.totalCount))
334333
}
@@ -434,7 +433,7 @@ func defaultItemRenderer(item ListItem, selected bool) string {
434433
}
435434

436435
if selected {
437-
return SelectedColorStyle.Render(line)
436+
return TableSelectedStyle.Render(line)
438437
}
439438
return line
440439
}

internal/ui/data_table.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"github.com/charmbracelet/bubbles/help"
1111
"github.com/charmbracelet/bubbles/key"
1212
tea "github.com/charmbracelet/bubbletea"
13-
"github.com/charmbracelet/lipgloss"
1413
"github.com/stormlightlabs/noteleaf/internal/models"
1514
)
1615

@@ -258,7 +257,7 @@ func (m dataTableModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
258257
func (m dataTableModel) View() string {
259258
var s strings.Builder
260259

261-
style := lipgloss.NewStyle().Foreground(lipgloss.Color(Squid.Hex()))
260+
style := MutedStyle
262261

263262
if m.showingHelp {
264263
return m.help.View(m.keys)
@@ -271,7 +270,7 @@ func (m dataTableModel) View() string {
271270
return s.String()
272271
}
273272

274-
s.WriteString(TitleColorStyle.Render(m.opts.Title))
273+
s.WriteString(TableTitleStyle.Render(m.opts.Title))
275274
if m.totalCount > 0 {
276275
s.WriteString(fmt.Sprintf(" (%d total)", m.totalCount))
277276
}
@@ -300,11 +299,11 @@ func (m dataTableModel) View() string {
300299
headerParts[i] = fmt.Sprintf(format, field.Title)
301300
}
302301
headerLine := fmt.Sprintf(" %s", strings.Join(headerParts, " "))
303-
s.WriteString(HeaderColorStyle.Render(headerLine))
302+
s.WriteString(TableHeaderStyle.Render(headerLine))
304303
s.WriteString("\n")
305304

306305
totalWidth := 3 + len(strings.Join(headerParts, " "))
307-
s.WriteString(HeaderColorStyle.Render(strings.Repeat("─", totalWidth)))
306+
s.WriteString(TableHeaderStyle.Render(strings.Repeat("─", totalWidth)))
308307
s.WriteString("\n")
309308

310309
for i, record := range m.records {
@@ -335,7 +334,7 @@ func (m dataTableModel) View() string {
335334
line := fmt.Sprintf("%s%s", prefix, strings.Join(rowParts, " "))
336335

337336
if i == m.selected {
338-
s.WriteString(SelectedColorStyle.Render(line))
337+
s.WriteString(TableSelectedStyle.Render(line))
339338
} else {
340339
s.WriteString(style.Render(line))
341340
}

internal/ui/palette.go

Lines changed: 78 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -53,46 +53,82 @@ func NewPalette(isDark bool) *Palette {
5353
}
5454

5555
var (
56-
ColorPrimary = Thunder.Hex() // Blue
57-
ColorAccent = Cumin.Hex() // Yellow/Gold
58-
ColorError = Paprika.Hex() // Red/Pink
59-
ColorText = Salt.Hex() // Light text
60-
ColorBG = Pepper.Hex() // Dark background
61-
62-
PrimaryStyle = newStyle().Foreground(lipgloss.Color(ColorPrimary))
63-
AccentStyle = newStyle().Foreground(lipgloss.Color(ColorAccent))
64-
ErrorStyle = newStyle().Foreground(lipgloss.Color(ColorError))
65-
TextStyle = newStyle().Foreground(lipgloss.Color(ColorText))
66-
TitleStyle = newPBoldStyle(0, 1).Foreground(lipgloss.Color(ColorAccent))
67-
SubtitleStyle = newEmStyle().Foreground(lipgloss.Color(ColorPrimary))
68-
SuccessStyle = newBoldStyle().Foreground(lipgloss.Color(ColorPrimary))
69-
WarningStyle = newBoldStyle().Foreground(lipgloss.Color(ColorAccent))
70-
InfoStyle = newStyle().Foreground(lipgloss.Color(ColorText))
71-
BoxStyle = newPStyle(1, 2).Border(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color(ColorPrimary))
72-
ErrorBoxStyle = newPStyle(1, 2).Border(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color(ColorError))
73-
ListItemStyle = newStyle().Foreground(lipgloss.Color(ColorText)).PaddingLeft(2)
74-
SelectedItemStyle = newBoldStyle().Foreground(lipgloss.Color(ColorAccent)).PaddingLeft(2)
75-
HeaderStyle = newPBoldStyle(0, 1).Foreground(lipgloss.Color(ColorPrimary))
76-
CellStyle = newPStyle(0, 1).Foreground(lipgloss.Color(ColorText))
77-
78-
TaskTitleStyle = newBoldStyle().Foreground(lipgloss.Color(Salt.Hex()))
79-
TaskIDStyle = newStyle().Foreground(lipgloss.Color(Squid.Hex())).Width(8)
80-
81-
StatusPending = newStyle().Foreground(lipgloss.Color(Citron.Hex()))
82-
StatusCompleted = newStyle().Foreground(lipgloss.Color(Julep.Hex()))
83-
84-
PriorityHigh = newBoldStyle().Foreground(lipgloss.Color(Cherry.Hex()))
85-
PriorityMedium = newStyle().Foreground(lipgloss.Color(Citron.Hex()))
86-
PriorityLow = newStyle().Foreground(lipgloss.Color(Squid.Hex()))
87-
88-
MovieStyle = newBoldStyle().Foreground(lipgloss.Color(Coral.Hex()))
89-
TVStyle = newBoldStyle().Foreground(lipgloss.Color(Violet.Hex()))
90-
BookStyle = newBoldStyle().Foreground(lipgloss.Color(Guac.Hex()))
91-
MusicStyle = newBoldStyle().Foreground(lipgloss.Color(Lichen.Hex()))
92-
93-
TableStyle = newStyle().BorderStyle(lipgloss.NormalBorder()).BorderForeground(lipgloss.Color(Smoke.Hex()))
94-
SelectedStyle = newBoldStyle().Foreground(lipgloss.Color(Salt.Hex())).Background(lipgloss.Color(Squid.Hex()))
95-
TitleColorStyle = newBoldStyle().Foreground(lipgloss.Color("212"))
96-
SelectedColorStyle = newBoldStyle().Foreground(lipgloss.Color("0")).Background(lipgloss.Color("212"))
97-
HeaderColorStyle = newBoldStyle().Foreground(lipgloss.Color("240"))
56+
// Background colors (dark mode, Iceberg-inspired)
57+
ColorBGBase = Pepper.Hex() // #201F26 - Darkest base
58+
ColorBGSecondary = BBQ.Hex() // #2d2c35 - Secondary background
59+
ColorBGTertiary = Charcoal.Hex() // #3A3943 - Tertiary/elevated
60+
ColorBGInput = Iron.Hex() // #4D4C57 - Input fields/focus
61+
62+
// Text colors (light to dark hierarchy)
63+
ColorTextPrimary = Salt.Hex() // #F1EFEF - Primary text (brightest)
64+
ColorTextSecondary = Smoke.Hex() // #BFBCC8 - Secondary text
65+
ColorTextMuted = Squid.Hex() // #858392 - Muted/comments
66+
ColorTextDimmed = Oyster.Hex() // #605F6B - Dimmed text
67+
68+
// Semantic colors (Iceberg-inspired: cool blues/purples with warm accents)
69+
ColorPrimary = Malibu.Hex() // #00A4FF - Blue (primary accent)
70+
ColorSuccess = Julep.Hex() // #00FFB2 - Green (success/positive)
71+
ColorError = Sriracha.Hex() // #EB4268 - Red (errors)
72+
ColorWarning = Tang.Hex() // #FF985A - Orange (warnings)
73+
ColorInfo = Violet.Hex() // #C259FF - Purple (info)
74+
ColorAccent = Lichen.Hex() // #5CDFEA - Teal (secondary accent)
75+
76+
// Base styles
77+
PrimaryStyle = newStyle().Foreground(lipgloss.Color(ColorPrimary))
78+
SuccessStyle = newBoldStyle().Foreground(lipgloss.Color(ColorSuccess))
79+
ErrorStyle = newBoldStyle().Foreground(lipgloss.Color(ColorError))
80+
WarningStyle = newBoldStyle().Foreground(lipgloss.Color(ColorWarning))
81+
InfoStyle = newStyle().Foreground(lipgloss.Color(ColorTextSecondary))
82+
AccentStyle = newStyle().Foreground(lipgloss.Color(ColorAccent))
83+
TextStyle = newStyle().Foreground(lipgloss.Color(ColorTextPrimary))
84+
MutedStyle = newStyle().Foreground(lipgloss.Color(ColorTextMuted))
85+
TitleStyle = newPBoldStyle(0, 1).Foreground(lipgloss.Color(ColorPrimary))
86+
SubtitleStyle = newEmStyle().Foreground(lipgloss.Color(ColorAccent))
87+
88+
// Layout styles
89+
BoxStyle = newPStyle(1, 2).Border(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color(ColorPrimary))
90+
ErrorBoxStyle = newPStyle(1, 2).Border(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color(ColorError))
91+
HeaderStyle = newPBoldStyle(0, 1).Foreground(lipgloss.Color(ColorPrimary))
92+
CellStyle = newPStyle(0, 1).Foreground(lipgloss.Color(ColorTextPrimary))
93+
94+
// List styles
95+
ListItemStyle = newStyle().Foreground(lipgloss.Color(ColorTextPrimary)).PaddingLeft(2)
96+
SelectedItemStyle = newBoldStyle().Foreground(lipgloss.Color(ColorPrimary)).PaddingLeft(2)
97+
98+
// Table/data view styles (replacing ANSI code references)
99+
TableStyle = newStyle().BorderStyle(lipgloss.NormalBorder()).BorderForeground(lipgloss.Color(ColorTextMuted))
100+
TableHeaderStyle = newBoldStyle().Foreground(lipgloss.Color(ColorAccent))
101+
TableTitleStyle = newBoldStyle().Foreground(lipgloss.Color(ColorPrimary))
102+
TableSelectedStyle = newBoldStyle().Foreground(lipgloss.Color(ColorTextPrimary)).Background(lipgloss.Color(ColorBGInput))
103+
104+
// Task-specific styles
105+
TaskTitleStyle = newBoldStyle().Foreground(lipgloss.Color(ColorTextPrimary))
106+
TaskIDStyle = newStyle().Foreground(lipgloss.Color(ColorTextMuted)).Width(8)
107+
108+
// Status styles (Iceberg-inspired: muted → blue → red → green)
109+
StatusTodo = newStyle().Foreground(lipgloss.Color(ColorTextMuted)) // Gray (muted)
110+
StatusInProgress = newStyle().Foreground(lipgloss.Color(ColorPrimary)) // Blue (active)
111+
StatusBlocked = newStyle().Foreground(lipgloss.Color(ColorError)) // Red (blocked)
112+
StatusDone = newStyle().Foreground(lipgloss.Color(ColorSuccess)) // Green (success)
113+
StatusPending = newStyle().Foreground(lipgloss.Color(ColorWarning)) // Orange (pending)
114+
StatusCompleted = newStyle().Foreground(lipgloss.Color(ColorSuccess)) // Green (completed)
115+
StatusAbandoned = newStyle().Foreground(lipgloss.Color(ColorTextDimmed)) // Dimmed gray (abandoned)
116+
StatusDeleted = newStyle().Foreground(lipgloss.Color(Cherry.Hex())) // Dark red (deleted)
117+
118+
// Priority styles (Iceberg-inspired: red → orange → gray)
119+
PriorityHigh = newBoldStyle().Foreground(lipgloss.Color(Cherry.Hex())) // #FF388B - Bright red
120+
PriorityMedium = newStyle().Foreground(lipgloss.Color(Tang.Hex())) // #FF985A - Orange
121+
PriorityLow = newStyle().Foreground(lipgloss.Color(ColorAccent)) // Teal (low)
122+
PriorityNone = newStyle().Foreground(lipgloss.Color(ColorTextMuted)) // Gray (no priority)
123+
PriorityLegacy = newStyle().Foreground(lipgloss.Color(Urchin.Hex())) // #C337E0 - Magenta (legacy)
124+
125+
// Content type styles (distinctive colors for different media)
126+
MovieStyle = newBoldStyle().Foreground(lipgloss.Color(Coral.Hex())) // #FF577D - Pink/coral
127+
TVStyle = newBoldStyle().Foreground(lipgloss.Color(Violet.Hex())) // #C259FF - Purple
128+
BookStyle = newBoldStyle().Foreground(lipgloss.Color(Guac.Hex())) // #12C78F - Green
129+
MusicStyle = newBoldStyle().Foreground(lipgloss.Color(Lichen.Hex())) // #5CDFEA - Teal
130+
131+
// Diff styles
132+
AdditionStyle = newStyle().Foreground(lipgloss.Color(Pickle.Hex())) // #00A475 - Green
133+
DeletionStyle = newStyle().Foreground(lipgloss.Color(Pom.Hex())) // #AB2454 - Red
98134
)

internal/ui/publication_view.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ func (m publicationViewModel) View() string {
162162
status = "draft"
163163
}
164164

165-
title := TitleColorStyle.Render(fmt.Sprintf("%s (%s)", m.note.Title, status))
165+
title := TableTitleStyle.Render(fmt.Sprintf("%s (%s)", m.note.Title, status))
166166
content := m.viewport.View()
167-
help := lipgloss.NewStyle().Foreground(lipgloss.Color(Squid.Hex())).Render(m.help.View(m.keys))
167+
help := MutedStyle.Render(m.help.View(m.keys))
168168

169169
if !m.ready {
170170
return "\n Initializing..."

internal/ui/task_edit.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -350,13 +350,13 @@ func (m taskEditModel) View() string {
350350

351351
var content strings.Builder
352352

353-
title := TitleColorStyle.Render("Edit Task")
353+
title := TableTitleStyle.Render("Edit Task")
354354
content.WriteString(title + "\n\n")
355355

356356
for i, field := range m.fields {
357357
fieldStyle := lipgloss.NewStyle()
358358
if i == m.currentField && m.mode == fieldNavigation {
359-
fieldStyle = SelectedColorStyle
359+
fieldStyle = TableSelectedStyle
360360
}
361361

362362
switch field {
@@ -427,7 +427,7 @@ func (m taskEditModel) renderStatusPicker() string {
427427
for i, status := range statusOptions {
428428
style := lipgloss.NewStyle()
429429
if i == m.statusIndex {
430-
style = SelectedColorStyle
430+
style = TableSelectedStyle
431431
}
432432

433433
line := fmt.Sprintf("%s %s", FormatStatusIndicator(status), status)
@@ -460,7 +460,7 @@ func (m taskEditModel) renderPriorityPicker() string {
460460
for i, priority := range options {
461461
style := lipgloss.NewStyle()
462462
if i == m.priorityIndex {
463-
style = SelectedColorStyle
463+
style = TableSelectedStyle
464464
}
465465

466466
var line string

0 commit comments

Comments
 (0)