diff --git a/go.mod b/go.mod index 000e2fb..6a4c33b 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,11 @@ go 1.18 require ( github.com/atotto/clipboard v0.1.4 - github.com/charmbracelet/bubbles v0.18.1-0.20240513171522-f4f674440a65 - github.com/charmbracelet/bubbletea v0.26.3-0.20240510203954-a434f25ff7d3 + github.com/charmbracelet/bubbles v0.18.1-0.20240513205629-dab412b45aa1 + github.com/charmbracelet/bubbletea v0.26.3-0.20240513205551-cf6db9e1506a github.com/charmbracelet/charm v0.8.7 github.com/charmbracelet/glamour v0.6.0 - github.com/charmbracelet/lipgloss v0.10.1-0.20240510203806-a6dc2ab509a8 + github.com/charmbracelet/lipgloss v0.10.1-0.20240513205513-d059cfa7d6f0 github.com/charmbracelet/x/editor v0.0.0-20231116172829-450eedbca1ab github.com/dustin/go-humanize v1.0.1 github.com/mattn/go-runewidth v0.0.15 @@ -17,7 +17,6 @@ require ( github.com/muesli/gitcha v0.2.0 github.com/muesli/go-app-paths v0.2.2 github.com/muesli/reflow v0.3.0 - github.com/muesli/termenv v0.15.2 github.com/sahilm/fuzzy v0.1.1 github.com/segmentio/ksuid v1.0.4 github.com/spf13/cobra v1.8.0 @@ -53,6 +52,7 @@ require ( github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/sasquatch v0.0.0-20200811221207-66979d92330a // indirect + github.com/muesli/termenv v0.15.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect @@ -74,3 +74,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/charmbracelet/bubbletea => ../bubbletea + +replace github.com/charmbracelet/lipgloss => ../lipgloss diff --git a/go.sum b/go.sum index feb0afa..f29d792 100644 --- a/go.sum +++ b/go.sum @@ -60,12 +60,14 @@ github.com/calmh/randomart v1.1.0/go.mod h1:DQUbPVyP+7PAs21w/AnfMKG5NioxS3TbZ2F9 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/charmbracelet/bubbles v0.7.5/go.mod h1:IRTORFvhEI6OUH7WhN2Ks8Z8miNGimk1BE6cmHijOkM= -github.com/charmbracelet/bubbles v0.18.1-0.20240513171522-f4f674440a65 h1:dtKXN44T1Cr81tXyto7HlAg/WXp5/w/r4l7EVlrLH3A= -github.com/charmbracelet/bubbles v0.18.1-0.20240513171522-f4f674440a65/go.mod h1:zOuREWAmYnZw3MIahm43VN7IkQEIQc+Lzn7u6OO5MHo= +github.com/charmbracelet/bubbles v0.18.1-0.20240513203235-56c40cbccc32 h1:zjuJv7tymyivNHrHvT8fm25znb2TLKpxEtNxVCnIlys= +github.com/charmbracelet/bubbles v0.18.1-0.20240513203235-56c40cbccc32/go.mod h1:qUvOZdUERUQFIzbNf7A7wOO0T467L9xw7x8yIYioBu4= +github.com/charmbracelet/bubbles v0.18.1-0.20240513205629-dab412b45aa1 h1:s1XBf/Kmu+Bo3giU0yq8dtrRon0nU9Aa7W55MnpyKyQ= +github.com/charmbracelet/bubbles v0.18.1-0.20240513205629-dab412b45aa1/go.mod h1:rB0bt1GJpY/UgUubN2hkaG6ZiPBKtI7tv6cyp2eQSfY= github.com/charmbracelet/bubbletea v0.12.2/go.mod h1:3gZkYELUOiEUOp0bTInkxguucy/xRbGSOcbMs1geLxg= github.com/charmbracelet/bubbletea v0.23.2/go.mod h1:FaP3WUivcTM0xOKNmhciz60M6I+weYLF76mr1JyI7sM= -github.com/charmbracelet/bubbletea v0.26.3-0.20240510203954-a434f25ff7d3 h1:Bg0eRBAgsWh84rpAq5X+e52TMAfloxoWeSJkYp9Pyw0= -github.com/charmbracelet/bubbletea v0.26.3-0.20240510203954-a434f25ff7d3/go.mod h1:jxN9TKmttdNsUKWSjjQQonHIY4nCuTBNdkDn3/Bpf7A= +github.com/charmbracelet/bubbletea v0.26.3-0.20240513203730-1ef18e60b708 h1:RggxF6hXLvOAayLXBpAruL4YF3epdNj2Gs4w6piT8WM= +github.com/charmbracelet/bubbletea v0.26.3-0.20240513203730-1ef18e60b708/go.mod h1:jxN9TKmttdNsUKWSjjQQonHIY4nCuTBNdkDn3/Bpf7A= github.com/charmbracelet/charm v0.8.7 h1:FJ9b7IxWUWHOPR72zS/QJLEqtudOB2Mwfc+Sir0eZR8= github.com/charmbracelet/charm v0.8.7/go.mod h1:ApJYwJljEjODkOYJgFDzbUqztLrCWQct9zyPD+xcVr4= github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= @@ -167,6 +169,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -355,6 +358,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU= github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= @@ -379,6 +383,7 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= @@ -393,6 +398,7 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -416,6 +422,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -450,6 +457,7 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= @@ -472,6 +480,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -519,6 +528,8 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -590,6 +601,7 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/main.go b/main.go index 3f6ec87..86a089c 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,6 @@ import ( "github.com/charmbracelet/glamour" "github.com/charmbracelet/glow/ui" "github.com/charmbracelet/glow/utils" - _ "github.com/charmbracelet/lipgloss/defaults" // Use std input defaults for lipgloss "github.com/meowgorithm/babyenv" gap "github.com/muesli/go-app-paths" "github.com/spf13/cobra" diff --git a/ui/pager.go b/ui/pager.go index 95f6025..2fc2f19 100644 --- a/ui/pager.go +++ b/ui/pager.go @@ -138,12 +138,12 @@ type pagerModel struct { func newPagerModel(common *commonModel) pagerModel { // Init viewport - vp := viewport.New(0, 0) + vp := viewport.New(common.ctx, 0, 0) vp.YPosition = 0 vp.HighPerformanceRendering = config.HighPerformancePager // Text input for notes/memos - ti := textinput.New() + ti := textinput.New(common.ctx) ti.Prompt = " > " ti.PromptStyle = pagerNoteInputPromptStyle ti.TextStyle = pagerNoteInputStyle @@ -152,7 +152,7 @@ func newPagerModel(common *commonModel) pagerModel { ti.Focus() // Text input for search - sp := spinner.New() + sp := spinner.New(common.ctx) sp.Style = spinnerStyle return pagerModel{ @@ -219,7 +219,7 @@ func (m *pagerModel) unload() { m.textInput.Reset() } -func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) { +func (m pagerModel) update(ctx tea.Context, msg tea.Msg) (pagerModel, tea.Cmd) { var ( cmd tea.Cmd cmds []tea.Cmd @@ -339,7 +339,7 @@ func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) { // full lifetime. In either case we need to spin the spinner // irrespective of it's more fine-grained visibility rules. var cmd tea.Cmd - m.spinner, cmd = m.spinner.Update(msg) + m.spinner, cmd = m.spinner.Update(ctx, msg) cmds = append(cmds, cmd) } else if m.state == pagerStateStashSuccess { // Successful stash. Stop spinning and update accordingly. @@ -396,10 +396,10 @@ func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) { switch m.state { case pagerStateSetNote: - m.textInput, cmd = m.textInput.Update(msg) + m.textInput, cmd = m.textInput.Update(ctx, msg) cmds = append(cmds, cmd) default: - m.viewport, cmd = m.viewport.Update(msg) + m.viewport, cmd = m.viewport.Update(ctx, msg) cmds = append(cmds, cmd) } @@ -414,16 +414,16 @@ func (m pagerModel) spinnerVisible() bool { return now.After(windowStart) && now.Before(windowEnd) } -func (m pagerModel) View() string { +func (m pagerModel) View(ctx tea.Context) string { var b strings.Builder - fmt.Fprint(&b, m.viewport.View()+"\n") + fmt.Fprint(&b, m.viewport.View(ctx)+"\n") // Footer switch m.state { case pagerStateSetNote: m.setNoteView(&b) default: - m.statusBarView(&b) + m.statusBarView(ctx, &b) } if m.showHelp { @@ -433,7 +433,7 @@ func (m pagerModel) View() string { return b.String() } -func (m pagerModel) statusBarView(b *strings.Builder) { +func (m pagerModel) statusBarView(ctx tea.Context, b *strings.Builder) { const ( minPercent float64 = 0.0 maxPercent float64 = 1.0 @@ -444,7 +444,7 @@ func (m pagerModel) statusBarView(b *strings.Builder) { showStatusMessage := m.state == pagerStateStatusMessage // Logo - logo := glowLogoView(" Glow ") + logo := m.common.styles.LogoStyle.Render(" Glow ") // Scroll percent percent := math.Max(minPercent, math.Min(maxPercent, m.viewport.ScrollPercent())) @@ -468,7 +468,7 @@ func (m pagerModel) statusBarView(b *strings.Builder) { if m.state == pagerStateStashing || m.state == pagerStateStashSuccess { var spinner string if m.spinnerVisible() { - spinner = m.spinner.View() + spinner = m.spinner.View(ctx) } statusIndicator = statusBarNoteStyle(" ") + spinner } else if isStashed && showStatusMessage { @@ -528,7 +528,7 @@ func (m pagerModel) statusBarView(b *strings.Builder) { func (m pagerModel) setNoteView(b *strings.Builder) { fmt.Fprint(b, noteHeading) - fmt.Fprint(b, m.textInput.View()) + fmt.Fprint(b, m.textInput.View(m.common.ctx)) } func (m pagerModel) helpView() (s string) { diff --git a/ui/stash.go b/ui/stash.go index c61ac32..9d0c78d 100644 --- a/ui/stash.go +++ b/ui/stash.go @@ -14,7 +14,6 @@ import ( "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/charm" - "github.com/charmbracelet/lipgloss" "github.com/muesli/reflow/ansi" "github.com/muesli/reflow/truncate" "github.com/sahilm/fuzzy" @@ -33,26 +32,6 @@ var ( alreadyStashedStatusMessage = statusMessage{subtleStatusMessage, "Already stashed"} ) -var ( - dividerDot = darkGrayFg.SetString(" • ") - dividerBar = darkGrayFg.SetString(" │ ") - offlineHeaderNote = darkGrayFg.SetString("(Offline)") - - logoStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#ECFD65")). - Background(fuchsia). - Bold(true) - - stashSpinnerStyle = lipgloss.NewStyle(). - Foreground(gray) - stashInputPromptStyle = lipgloss.NewStyle(). - Foreground(yellowGreen). - MarginRight(1) - stashInputCursorStyle = lipgloss.NewStyle(). - Foreground(fuchsia). - MarginRight(1) -) - // MSG type ( @@ -134,41 +113,41 @@ type statusMessage struct { message string } -func initSections() { +func initSections(c *commonModel) { sections = map[sectionKey]section{ localSection: { key: localSection, docTypes: NewDocTypeSet(LocalDoc), - paginator: newStashPaginator(), + paginator: newStashPaginator(c), }, stashedSection: { key: stashedSection, docTypes: NewDocTypeSet(StashedDoc, ConvertedDoc), - paginator: newStashPaginator(), + paginator: newStashPaginator(c), }, newsSection: { key: newsSection, docTypes: NewDocTypeSet(NewsDoc), - paginator: newStashPaginator(), + paginator: newStashPaginator(c), }, filterSection: { key: filterSection, docTypes: DocTypeSet{}, - paginator: newStashPaginator(), + paginator: newStashPaginator(c), }, } } -// String returns a styled version of the status message appropriate for the +// Render returns a styled version of the status message appropriate for the // given context. -func (s statusMessage) String() string { +func (s statusMessage) Render(st *styles) string { switch s.status { case subtleStatusMessage: - return dimGreenFg(s.message) + return st.DimGreenFg(s.message) case errorStatusMessage: - return redFg(s.message) + return st.RedFg(s.message) default: - return greenFg(s.message) + return st.GreenFg(s.message) } } @@ -514,21 +493,21 @@ func (m *stashModel) moveCursorDown() { // INIT func newStashModel(common *commonModel) stashModel { - sp := spinner.New() + sp := spinner.New(common.ctx) sp.Spinner = spinner.Line - sp.Style = stashSpinnerStyle + sp.Style = common.styles.StashSpinnerStyle - ni := textinput.New() + ni := textinput.New(common.ctx) ni.Prompt = "Memo:" - ni.PromptStyle = stashInputPromptStyle - ni.CursorStyle = stashInputCursorStyle + ni.PromptStyle = common.styles.StashInputPromptStyle + ni.CursorStyle = common.styles.StashInputCursorStyle ni.CharLimit = noteCharacterLimit ni.Focus() - si := textinput.New() + si := textinput.New(common.ctx) si.Prompt = "Find:" - si.PromptStyle = stashInputPromptStyle - si.CursorStyle = stashInputCursorStyle + si.PromptStyle = common.styles.StashInputPromptStyle + si.CursorStyle = common.styles.StashInputCursorStyle si.CharLimit = noteCharacterLimit si.Focus() @@ -563,17 +542,17 @@ func newStashModel(common *commonModel) stashModel { return m } -func newStashPaginator() paginator.Model { - p := paginator.New() +func newStashPaginator(common *commonModel) paginator.Model { + p := paginator.New(common.ctx) p.Type = paginator.Dots - p.ActiveDot = brightGrayFg("•") - p.InactiveDot = darkGrayFg.Render("•") + p.ActiveDot = common.styles.BrightGrayFg("•") + p.InactiveDot = common.styles.DarkGrayFg("•") return p } // UPDATE -func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) { +func (m stashModel) update(ctx tea.Context, msg tea.Msg) (stashModel, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { @@ -651,7 +630,7 @@ func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) { case spinner.TickMsg: if m.shouldSpin() { var cmd tea.Cmd - m.spinner, cmd = m.spinner.Update(msg) + m.spinner, cmd = m.spinner.Update(ctx, msg) cmds = append(cmds, cmd) } @@ -919,7 +898,7 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd { // Update paginator. Pagination key handling is done here, but it could // also be moved up to this level, in which case we'd use model methods // like model.PageUp(). - newPaginatorModel, cmd := m.paginator().Update(msg) + newPaginatorModel, cmd := m.paginator().Update(m.common.ctx, msg) m.setPaginator(newPaginatorModel) cmds = append(cmds, cmd) @@ -1071,7 +1050,7 @@ func (m *stashModel) handleFiltering(msg tea.Msg) tea.Cmd { } // Update the filter text input component - newFilterInputModel, inputCmd := m.filterInput.Update(msg) + newFilterInputModel, inputCmd := m.filterInput.Update(m.common.ctx, msg) currentFilterVal := m.filterInput.Value() newFilterVal := newFilterInputModel.Value() m.filterInput = newFilterInputModel @@ -1128,7 +1107,7 @@ func (m *stashModel) handleNoteInput(msg tea.Msg) tea.Cmd { } // Update the note text input component - newNoteInputModel, noteInputCmd := m.noteInput.Update(msg) + newNoteInputModel, noteInputCmd := m.noteInput.Update(m.common.ctx, msg) m.noteInput = newNoteInputModel cmds = append(cmds, noteInputCmd) @@ -1137,26 +1116,26 @@ func (m *stashModel) handleNoteInput(msg tea.Msg) tea.Cmd { // VIEW -func (m stashModel) view() string { +func (m stashModel) view(ctx tea.Context) string { var s string switch m.viewState { case stashStateShowingError: - return errorView(m.err, false) + return errorView(&m.common.styles, m.err, false) case stashStateLoadingDocument: - s += " " + m.spinner.View() + " Loading document..." + s += " " + m.spinner.View(ctx) + " Loading document..." case stashStateReady: loadingIndicator := " " if m.shouldSpin() { - loadingIndicator = m.spinner.View() + loadingIndicator = m.spinner.View(ctx) } var header string switch m.selectionState { case selectionPromptingDelete: - header = redFg("Delete this item from your stash? ") + faintRedFg("(y/N)") + header = m.common.styles.RedFg("Delete this item from your stash? ") + m.common.styles.FaintRedFg("(y/N)") case selectionSettingNote: - header = yellowFg("Set the memo for this item?") + header = m.common.styles.YellowFg("Set the memo for this item?") } // Only draw the normal header if we're not using the header area for @@ -1168,13 +1147,13 @@ func (m stashModel) view() string { // Rules for the logo, filter and status message. logoOrFilter := " " if m.showStatusMessage && m.filterState == filtering { - logoOrFilter += m.statusMessage.String() + logoOrFilter += m.statusMessage.Render(&m.common.styles) } else if m.filterState == filtering { - logoOrFilter += m.filterInput.View() + logoOrFilter += m.filterInput.View(ctx) } else { - logoOrFilter += glowLogoView(" Glow ") + logoOrFilter += m.common.styles.LogoStyle.Render(" Glow ") if m.showStatusMessage { - logoOrFilter += " " + m.statusMessage.String() + logoOrFilter += " " + m.statusMessage.Render(&m.common.styles) } } logoOrFilter = truncate.StringWithTail(logoOrFilter, uint(m.common.width-1), ellipsis) @@ -1195,7 +1174,7 @@ func (m stashModel) view() string { var pagination string if m.paginator().TotalPages > 1 { - pagination = m.paginator().View() + pagination = m.paginator().View(ctx) // If the dot pagination is wider than the width of the window // use the arabic paginator. @@ -1210,7 +1189,7 @@ func (m stashModel) view() string { // pointers in our model should be refactored away. var p paginator.Model = *(m.paginator()) p.Type = paginator.Arabic - pagination = paginationStyle.Render(p.View()) + pagination = m.common.styles.PaginationStyle.Render(p.View(ctx)) } // We could also look at m.stashFullyLoaded and add an indicator @@ -1232,10 +1211,6 @@ func (m stashModel) view() string { return "\n" + indent(s, stashIndent) } -func glowLogoView(text string) string { - return logoStyle.Render(text) -} - func (m stashModel) headerView() string { localCount := m.countMarkdowns(LocalDoc) stashedCount := m.countMarkdowns(StashedDoc) + m.countMarkdowns(ConvertedDoc) @@ -1246,7 +1221,7 @@ func (m stashModel) headerView() string { // Filter results if m.filterState == filtering { if localCount+stashedCount+newsCount == 0 { - return grayFg("Nothing found.") + return m.common.styles.GrayFg("Nothing found.") } if localCount > 0 { sections = append(sections, fmt.Sprintf("%d local", localCount)) @@ -1256,22 +1231,22 @@ func (m stashModel) headerView() string { } for i := range sections { - sections[i] = grayFg(sections[i]) + sections[i] = m.common.styles.GrayFg(sections[i]) } - return strings.Join(sections, dividerDot.String()) + return strings.Join(sections, m.common.styles.DividerDot) } if m.loadingDone() && len(m.markdowns) == 0 { var maybeOffline string if m.common.authStatus == authFailed { - maybeOffline = " " + offlineHeaderNote.String() + maybeOffline = " " + m.common.styles.OfflineHeaderNote } if m.stashedOnly() { - return subtleStyle.Render("Can’t load stash") + maybeOffline + return m.common.styles.SubtleStyle.Render("Can’t load stash") + maybeOffline } - return subtleStyle.Render("No markdown files found") + maybeOffline + return m.common.styles.SubtleStyle.Render("No markdown files found") + maybeOffline } // Tabs @@ -1299,16 +1274,16 @@ func (m stashModel) headerView() string { } if m.sectionIndex == i && len(m.sections) > 1 { - s = selectedTabStyle.Render(s) + s = m.common.styles.SelectedTabStyle.Render(s) } else { - s = tabStyle.Render(s) + s = m.common.styles.TabStyle.Render(s) } sections = append(sections, s) } - s := strings.Join(sections, dividerBar.String()) + s := strings.Join(sections, m.common.styles.DividerBar) if m.common.authStatus == authFailed { - s += dividerDot.String() + offlineHeaderNote.String() + s += m.common.styles.DividerDot + m.common.styles.OfflineHeaderNote } return s @@ -1322,7 +1297,7 @@ func (m stashModel) populatedView() string { // Empty states if len(mds) == 0 { f := func(s string) { - b.WriteString(" " + grayFg(s)) + b.WriteString(" " + m.common.styles.GrayFg(s)) } switch m.sections[m.sectionIndex].key { diff --git a/ui/stashhelp.go b/ui/stashhelp.go index 2b30c2e..92f8b19 100644 --- a/ui/stashhelp.go +++ b/ui/stashhelp.go @@ -30,7 +30,7 @@ func newHelpColumn(pairs ...string) (h helpColumn) { } // render returns styled and formatted rows from keys and values. -func (h helpColumn) render(height int) (rows []string) { +func (h helpColumn) render(st *styles, height int) (rows []string) { keyWidth, valWidth := h.maxWidths() for i := 0; i < height; i++ { @@ -44,11 +44,11 @@ func (h helpColumn) render(height int) (rows []string) { switch k { case "s": - k = greenFg(k) - v = semiDimGreenFg(v) + k = st.GreenFg(k) + v = st.SemiDimGreenFg(v) default: - k = grayFg(k) - v = midGrayFg(v) + k = st.GrayFg(k) + v = st.MidGrayFg(v) } } b.WriteString(k) @@ -196,12 +196,13 @@ func (m stashModel) renderHelp(groups ...[]string) (string, int) { // would otherwise wrap to two lines. Help view entries should come in as pairs, // with the first being the key and the second being the help text. func (m stashModel) miniHelpView(entries ...string) string { + st := &m.common.styles if len(entries) == 0 { return "" } var ( - truncationChar = subtleStyle.Render("…") + truncationChar = st.SubtleStyle.Render("…") truncationWidth = ansi.PrintableRuneWidth(truncationChar) ) @@ -221,17 +222,17 @@ func (m stashModel) miniHelpView(entries ...string) string { switch k { case "s": - k = greenFg(k) - v = semiDimGreenFg(v) + k = st.GreenFg(k) + v = st.SemiDimGreenFg(v) default: - k = grayFg(k) - v = midGrayFg(v) + k = st.GrayFg(k) + v = st.MidGrayFg(v) } next = fmt.Sprintf("%s %s", k, v) if i < len(entries)-2 { - next += dividerDot.String() + next += st.DividerDot } // Only this (and the following) help text items if we have the @@ -269,7 +270,7 @@ func (m stashModel) fullHelpView(groups ...[]string) string { // Build columns for _, c := range columns { - renderedCols = append(renderedCols, c.render(tallestCol)) + renderedCols = append(renderedCols, c.render(&m.common.styles, tallestCol)) } // Merge columns diff --git a/ui/stashitem.go b/ui/stashitem.go index d853fc7..8e50498 100644 --- a/ui/stashitem.go +++ b/ui/stashitem.go @@ -46,6 +46,7 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { isSelected := index == m.cursor() isFiltering := m.filterState == filtering singleFilteredItem := isFiltering && len(m.getVisibleMarkdowns()) == 1 + styles := &m.common.styles // If there are multiple items being filtered don't highlight a selected // item in the results. If we've filtered down to one item, however, @@ -55,33 +56,33 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { switch m.selectionState { case selectionPromptingDelete: - gutter = faintRedFg(verticalLine) - icon = faintRedFg(icon) - title = redFg(title) - date = faintRedFg(date) + gutter = styles.FaintRedFg(verticalLine) + icon = styles.FaintRedFg(icon) + title = styles.RedFg(title) + date = styles.FaintRedFg(date) case selectionSettingNote: - gutter = dullYellowFg(verticalLine) + gutter = styles.DullYellowFg(verticalLine) icon = "" - title = m.noteInput.View() - date = dullYellowFg(date) + title = m.noteInput.View(m.common.ctx) + date = styles.DullYellowFg(date) default: if m.common.latestFileStashed == md.stashID && m.statusMessage == stashingStatusMessage { - gutter = greenFg(verticalLine) - icon = dimGreenFg(icon) - title = greenFg(title) - date = semiDimGreenFg(date) + gutter = styles.GreenFg(verticalLine) + icon = styles.DimGreenFg(icon) + title = styles.GreenFg(title) + date = styles.SemiDimGreenFg(date) } else { - gutter = dullFuchsiaFg(verticalLine) - icon = dullFuchsiaFg(icon) + gutter = styles.DullFuchsiaFg(verticalLine) + icon = styles.DullFuchsiaFg(icon) if m.currentSection().key == filterSection && m.filterState == filterApplied || singleFilteredItem { s := lipgloss.NewStyle().Foreground(fuchsia) title = styleFilteredText(title, m.filterInput.Value(), s, s.Copy().Underline(true)) } else { - title = fuchsiaFg(title) + title = styles.FuchsiaFg(title) } - date = dullFuchsiaFg(date) + date = styles.DullFuchsiaFg(date) } } } else { @@ -91,35 +92,35 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { if m.common.latestFileStashed == md.stashID && m.statusMessage == stashingStatusMessage { - icon = dimGreenFg(icon) - title = greenFg(title) - date = semiDimGreenFg(date) + icon = styles.DimGreenFg(icon) + title = styles.GreenFg(title) + date = styles.SemiDimGreenFg(date) } else if md.docType == NewsDoc { if isFiltering && m.filterInput.Value() == "" { - title = dimIndigoFg(title) - date = dimSubtleIndigoFg(date) + title = styles.DimIndigoFg(title) + date = styles.DimSubtleIndigoFg(date) } else { s := lipgloss.NewStyle().Foreground(indigo) title = styleFilteredText(title, m.filterInput.Value(), s, s.Copy().Underline(true)) - date = subtleIndigoFg(date) + date = styles.SubtleIndigoFg(date) } } else if isFiltering && m.filterInput.Value() == "" { - icon = dimGreenFg(icon) + icon = styles.DimGreenFg(icon) if title == noMemoTitle { - title = dimBrightGrayFg(title) + title = styles.DimBrightGrayFg(title) } else { - title = dimNormalFg(title) + title = styles.DimNormalFg(title) } - date = dimBrightGrayFg(date) + date = styles.DimBrightGrayFg(date) } else { - icon = greenFg(icon) + icon = styles.GreenFg(icon) if title == noMemoTitle { - title = brightGrayFg(title) + title = styles.BrightGrayFg(title) } else { s := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#1a1a1a", Dark: "#dddddd"}) title = styleFilteredText(title, m.filterInput.Value(), s, s.Copy().Underline(true)) } - date = brightGrayFg(date) + date = styles.BrightGrayFg(date) } } diff --git a/ui/styles.go b/ui/styles.go index e5a6c83..f4a759e 100644 --- a/ui/styles.go +++ b/ui/styles.go @@ -1,6 +1,10 @@ package ui -import . "github.com/charmbracelet/lipgloss" //nolint: revive +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + . "github.com/charmbracelet/lipgloss" +) //nolint: revive // Colors. var ( @@ -30,55 +34,125 @@ var ( dimGreen = AdaptiveColor{Light: "#72D2B0", Dark: "#0B5137"} ) +type styler func(...string) string + // Ulimately, we'll transition to named styles. // nolint:deadcode,unused,varcheck -var ( - normalFg = NewStyle().Foreground(normal).Render - dimNormalFg = NewStyle().Foreground(normalDim).Render +type styles struct { + NormalFg styler + DimNormalFg styler - brightGrayFg = NewStyle().Foreground(brightGray).Render - dimBrightGrayFg = NewStyle().Foreground(dimBrightGray).Render + BrightGrayFg styler + DimBrightGrayFg styler - grayFg = NewStyle().Foreground(gray).Render - midGrayFg = NewStyle().Foreground(midGray).Render - darkGrayFg = NewStyle().Foreground(darkGray) + GrayFg styler + MidGrayFg styler + DarkGrayFg styler - greenFg = NewStyle().Foreground(green).Render - semiDimGreenFg = NewStyle().Foreground(semiDimGreen).Render - dimGreenFg = NewStyle().Foreground(dimGreen).Render + GreenFg styler + SemiDimGreenFg styler + DimGreenFg styler - fuchsiaFg = NewStyle().Foreground(fuchsia).Render - dimFuchsiaFg = NewStyle().Foreground(dimFuchsia).Render + FuchsiaFg styler + DimFuchsiaFg styler - dullFuchsiaFg = NewStyle().Foreground(dullFuchsia).Render - dimDullFuchsiaFg = NewStyle().Foreground(dimDullFuchsia).Render + DullFuchsiaFg styler + DimDullFuchsiaFg styler - indigoFg = NewStyle().Foreground(fuchsia).Render - dimIndigoFg = NewStyle().Foreground(dimIndigo).Render + IndigoFg styler + DimIndigoFg styler - subtleIndigoFg = NewStyle().Foreground(subtleIndigo).Render - dimSubtleIndigoFg = NewStyle().Foreground(dimSubtleIndigo).Render + SubtleIndigoFg styler + DimSubtleIndigoFg styler - yellowFg = NewStyle().Foreground(yellowGreen).Render // renders light green on light backgrounds - dullYellowFg = NewStyle().Foreground(dullYellowGreen).Render // renders light green on light backgrounds - redFg = NewStyle().Foreground(red).Render - faintRedFg = NewStyle().Foreground(faintRed).Render -) + YellowFg styler + DullYellowFg styler + RedFg styler + FaintRedFg styler -var ( - tabStyle = NewStyle(). - Foreground(AdaptiveColor{Light: "#909090", Dark: "#626262"}) + TabStyle Style + SelectedTabStyle Style + ErrorTitleStyle Style + SubtleStyle Style + PaginationStyle Style - selectedTabStyle = NewStyle(). - Foreground(AdaptiveColor{Light: "#333333", Dark: "#979797"}) + LogoStyle Style + StashSpinnerStyle Style + StashInputPromptStyle Style + StashInputCursorStyle Style - errorTitleStyle = NewStyle(). + DividerDot string + DividerBar string + OfflineHeaderNote string +} + +func defaultStyles(ctx tea.Context) styles { + darkGrayFg := ctx.NewStyle().Foreground(darkGray).Render + subtleStyle := ctx.NewStyle(). + Foreground(AdaptiveColor{Light: "#9B9B9B", Dark: "#5C5C5C"}) + + return styles{ + NormalFg: ctx.NewStyle().Foreground(normal).Render, + DimNormalFg: ctx.NewStyle().Foreground(normalDim).Render, + + BrightGrayFg: ctx.NewStyle().Foreground(brightGray).Render, + DimBrightGrayFg: ctx.NewStyle().Foreground(dimBrightGray).Render, + + GrayFg: ctx.NewStyle().Foreground(gray).Render, + MidGrayFg: ctx.NewStyle().Foreground(midGray).Render, + DarkGrayFg: darkGrayFg, + + GreenFg: ctx.NewStyle().Foreground(green).Render, + SemiDimGreenFg: ctx.NewStyle().Foreground(semiDimGreen).Render, + DimGreenFg: ctx.NewStyle().Foreground(dimGreen).Render, + + FuchsiaFg: ctx.NewStyle().Foreground(fuchsia).Render, + DimFuchsiaFg: ctx.NewStyle().Foreground(dimFuchsia).Render, + + DullFuchsiaFg: ctx.NewStyle().Foreground(dullFuchsia).Render, + DimDullFuchsiaFg: ctx.NewStyle().Foreground(dimDullFuchsia).Render, + + IndigoFg: ctx.NewStyle().Foreground(fuchsia).Render, + DimIndigoFg: ctx.NewStyle().Foreground(dimIndigo).Render, + + SubtleIndigoFg: ctx.NewStyle().Foreground(subtleIndigo).Render, + DimSubtleIndigoFg: ctx.NewStyle().Foreground(dimSubtleIndigo).Render, + + YellowFg: ctx.NewStyle().Foreground(yellowGreen).Render, // renders light green on light backgrounds + DullYellowFg: ctx.NewStyle().Foreground(dullYellowGreen).Render, // renders light green on light backgrounds + RedFg: ctx.NewStyle().Foreground(red).Render, + FaintRedFg: ctx.NewStyle().Foreground(faintRed).Render, + TabStyle: ctx.NewStyle(). + Foreground(AdaptiveColor{Light: "#909090", Dark: "#626262"}), + + SelectedTabStyle: ctx.NewStyle(). + Foreground(AdaptiveColor{Light: "#333333", Dark: "#979797"}), + + ErrorTitleStyle: ctx.NewStyle(). Foreground(cream). Background(red). - Padding(0, 1) + Padding(0, 1), - subtleStyle = NewStyle(). - Foreground(AdaptiveColor{Light: "#9B9B9B", Dark: "#5C5C5C"}) + SubtleStyle: subtleStyle, - paginationStyle = subtleStyle.Copy() -) + PaginationStyle: subtleStyle.Copy(), + + LogoStyle: ctx.NewStyle(). + Foreground(lipgloss.Color("#ECFD65")). + Background(fuchsia). + Bold(true), + + StashSpinnerStyle: ctx.NewStyle(). + Foreground(gray), + StashInputPromptStyle: ctx.NewStyle(). + Foreground(yellowGreen). + MarginRight(1), + StashInputCursorStyle: ctx.NewStyle(). + Foreground(fuchsia). + MarginRight(1), + + DividerDot: darkGrayFg(" • "), + DividerBar: darkGrayFg(" │ "), + OfflineHeaderNote: darkGrayFg("(Offline)"), + } +} diff --git a/ui/ui.go b/ui/ui.go index ff0e8bc..724ccb0 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -14,7 +14,6 @@ import ( "github.com/charmbracelet/charm/keygen" "github.com/charmbracelet/glow/utils" "github.com/muesli/gitcha" - te "github.com/muesli/termenv" "github.com/segmentio/ksuid" ) @@ -141,6 +140,7 @@ const ( // Common stuff we'll need to access in all models. type commonModel struct { + ctx tea.Context cfg Config cc *charm.Client cwd string @@ -158,6 +158,8 @@ type commonModel struct { // Files currently being stashed. We remove files from this set once // a stash operation has either succeeded or failed. filesStashing map[ksuid.KSUID]struct{} + + styles styles } func (c commonModel) isStashing() bool { @@ -199,16 +201,6 @@ func (m *model) unloadDocument() []tea.Cmd { } func newModel(cfg Config) tea.Model { - initSections() - - if cfg.GlamourStyle == "auto" { - if te.HasDarkBackground() { - cfg.GlamourStyle = "dark" - } else { - cfg.GlamourStyle = "light" - } - } - if len(cfg.DocumentTypes) == 0 { cfg.DocumentTypes.Add(LocalDoc, StashedDoc, ConvertedDoc, NewsDoc) } @@ -224,12 +216,28 @@ func newModel(cfg Config) tea.Model { common: &common, state: stateShowStash, keygenState: keygenUnstarted, - pager: newPagerModel(&common), - stash: newStashModel(&common), } } -func (m model) Init(tea.Context) (tea.Model, tea.Cmd) { +func (m model) Init(ctx tea.Context) (tea.Model, tea.Cmd) { + if m.common.cfg.GlamourStyle == "auto" { + if ctx.HasLightBackground() { + m.common.cfg.GlamourStyle = "light" + } else { + m.common.cfg.GlamourStyle = "dark" + } + } + + // Initialize styles & sub-models + m.common.ctx = ctx + m.common.styles = defaultStyles(ctx) + + // Initialize sections after we have the styles + initSections(m.common) + + m.pager = newPagerModel(m.common) + m.stash = newStashModel(m.common) + var cmds []tea.Cmd d := m.common.cfg.DocumentTypes @@ -273,7 +281,7 @@ func (m model) Update(ctx tea.Context, msg tea.Msg) (tea.Model, tea.Cmd) { case stateShowStash: // pass through all keys if we're editing the filter if m.stash.filterState == filtering || m.stash.selectionState == selectionSettingNote { - m.stash, cmd = m.stash.update(msg) + m.stash, cmd = m.stash.update(ctx, msg) return m, cmd } @@ -283,7 +291,7 @@ func (m model) Update(ctx tea.Context, msg tea.Msg) (tea.Model, tea.Cmd) { // If setting a note send all keys straight through case pagerStateSetNote: var batch []tea.Cmd - newPagerModel, cmd := m.pager.update(msg) + newPagerModel, cmd := m.pager.update(ctx, msg) m.pager = newPagerModel batch = append(batch, cmd) return m, tea.Batch(batch...) @@ -370,7 +378,7 @@ func (m model) Update(ctx tea.Context, msg tea.Msg) (tea.Model, tea.Cmd) { // A note was saved to a document. This will have been done in the // pager, so we'll need to find the corresponding note in the stash. // So, pass the message to the stash for processing. - stashModel, cmd := m.stash.update(msg) + stashModel, cmd := m.stash.update(ctx, msg) m.stash = stashModel return m, cmd @@ -378,7 +386,7 @@ func (m model) Update(ctx tea.Context, msg tea.Msg) (tea.Model, tea.Cmd) { // Always pass these messages to the stash so we can keep it updated // about network activity, even if the user isn't currently viewing // the stash. - stashModel, cmd := m.stash.update(msg) + stashModel, cmd := m.stash.update(ctx, msg) m.stash = stashModel return m, cmd @@ -424,7 +432,7 @@ func (m model) Update(ctx tea.Context, msg tea.Msg) (tea.Model, tea.Cmd) { case filteredMarkdownMsg: if m.state == stateShowDocument { - newStashModel, cmd := m.stash.update(msg) + newStashModel, cmd := m.stash.update(ctx, msg) m.stash = newStashModel cmds = append(cmds, cmd) } @@ -433,12 +441,12 @@ func (m model) Update(ctx tea.Context, msg tea.Msg) (tea.Model, tea.Cmd) { // Process children switch m.state { case stateShowStash: - newStashModel, cmd := m.stash.update(msg) + newStashModel, cmd := m.stash.update(ctx, msg) m.stash = newStashModel cmds = append(cmds, cmd) case stateShowDocument: - newPagerModel, cmd := m.pager.update(msg) + newPagerModel, cmd := m.pager.update(ctx, msg) m.pager = newPagerModel cmds = append(cmds, cmd) } @@ -448,18 +456,18 @@ func (m model) Update(ctx tea.Context, msg tea.Msg) (tea.Model, tea.Cmd) { func (m model) View(ctx tea.Context) string { if m.fatalErr != nil { - return errorView(m.fatalErr, true) + return errorView(&m.common.styles, m.fatalErr, true) } switch m.state { case stateShowDocument: - return m.pager.View() + return m.pager.View(ctx) default: - return m.stash.view() + return m.stash.view(ctx) } } -func errorView(err error, fatal bool) string { +func errorView(st *styles, err error, fatal bool) string { exitMsg := "press any key to " if fatal { exitMsg += "exit" @@ -467,9 +475,9 @@ func errorView(err error, fatal bool) string { exitMsg += "return" } s := fmt.Sprintf("%s\n\n%v\n\n%s", - errorTitleStyle.Render("ERROR"), + st.ErrorTitleStyle.Render("ERROR"), err, - subtleStyle.Render(exitMsg), + st.SubtleStyle.Render(exitMsg), ) return "\n" + indent(s, 3) }