diff --git a/go.mod b/go.mod index b899a79..8036ef7 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/charmbracelet/glow go 1.16 require ( - github.com/charmbracelet/bubbles v0.9.0 + github.com/charmbracelet/bubbles v0.10.2 github.com/charmbracelet/bubbletea v0.19.3 github.com/charmbracelet/charm v0.9.1 github.com/charmbracelet/glamour v0.2.1-0.20210402234443-abe9cda419ba diff --git a/go.sum b/go.sum index a03947c..31d9c3a 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,9 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY= github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/auth0/go-jwt-middleware v1.0.0 h1:76t55qLQu3xjMFbkirbSCA8ZPcO1ny+20Uq1wkSTRDE= github.com/auth0/go-jwt-middleware v1.0.0/go.mod h1:nX2S0GmCyl087kdNSSItfOvMYokq5PSTG1yGIP5Le4U= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= @@ -57,10 +58,9 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/charmbracelet/bubbles v0.8.0/go.mod h1:5WX1sSSjNCgCrzvRMN/z23HxvWaa+AI16Ch0KPZPeDs= -github.com/charmbracelet/bubbles v0.9.0 h1:lqJ8FXwoLceQF2J0A+dWo1Cuu1dNyjbW4Opgdi2vkhw= -github.com/charmbracelet/bubbles v0.9.0/go.mod h1:NWT/c+0rYEnYChz5qCyX4Lj6fDw9gGToh9EFJPajghU= +github.com/charmbracelet/bubbles v0.10.2 h1:VK1Q7nnBMDFTlrMmvBgE9nidtU5udsIcZvFXvjE2Cfk= +github.com/charmbracelet/bubbles v0.10.2/go.mod h1:jOA+DUF1rjZm7gZHcNyIVW+YrBPALKfpGVdJu8UiJsA= github.com/charmbracelet/bubbletea v0.13.1/go.mod h1:tp9tr9Dadh0PLhgiwchE5zZJXm5543JYjHG9oY+5qSg= -github.com/charmbracelet/bubbletea v0.14.1/go.mod h1:b5lOf5mLjMg1tRn1HVla54guZB+jvsyV0yYAQja95zE= github.com/charmbracelet/bubbletea v0.19.0/go.mod h1:VuXF2pToRxDUHcBUcPmCRUHRvFATM4Ckb/ql1rBl3KA= github.com/charmbracelet/bubbletea v0.19.3 h1:OKeO/Y13rQQqt4snX+lePB0QrnW80UdrMNolnCcmoAw= github.com/charmbracelet/bubbletea v0.19.3/go.mod h1:VuXF2pToRxDUHcBUcPmCRUHRvFATM4Ckb/ql1rBl3KA= @@ -73,7 +73,6 @@ github.com/charmbracelet/keygen v0.1.2 h1:Gr/gdIOjDIxCTRVXpwa9tsXPoJPS2eGNehPoMn github.com/charmbracelet/keygen v0.1.2/go.mod h1:kFQ3Cvop12fXWX1K29vxDxV9x8ujG4wBSXq//GySSSk= github.com/charmbracelet/lipgloss v0.1.1/go.mod h1:5D8zradw52m7QmxRF6QgwbwJi9je84g8MkWiGN07uKg= github.com/charmbracelet/lipgloss v0.1.2/go.mod h1:5D8zradw52m7QmxRF6QgwbwJi9je84g8MkWiGN07uKg= -github.com/charmbracelet/lipgloss v0.3.0/go.mod h1:VkhdBS2eNAmRkTwRKLJCFhCOVkjntMusBDxv7TXahuk= github.com/charmbracelet/lipgloss v0.4.0 h1:768h64EFkGUr8V5yAKV7/Ta0NiVceiPaV+PphaW1K9g= github.com/charmbracelet/lipgloss v0.4.0/go.mod h1:vmdkHvce7UzX6xkyf4cca8WlwdQ5RQr8fzta+xl7BOM= github.com/charmbracelet/wish v0.1.1 h1:BLsUBlHzIxw5ebzmBzxUUMfakdteew6gQOhudhsLKpM= diff --git a/ui/pager.go b/ui/pager.go index bbab688..8f2de3b 100644 --- a/ui/pager.go +++ b/ui/pager.go @@ -133,12 +133,12 @@ type pagerModel struct { func newPagerModel(common *commonModel) pagerModel { // Init viewport - vp := viewport.Model{} + vp := viewport.New(0, 0) vp.YPosition = 0 vp.HighPerformanceRendering = config.HighPerformancePager // Text input for notes/memos - ti := textinput.NewModel() + ti := textinput.New() ti.Prompt = " > " ti.PromptStyle = pagerNoteInputPromptStyle ti.TextStyle = pagerNoteInputStyle @@ -147,10 +147,8 @@ func newPagerModel(common *commonModel) pagerModel { ti.Focus() // Text input for search - sp := spinner.NewModel() + sp := spinner.New() sp.Style = spinnerStyle - sp.HideFor = time.Millisecond * 50 - sp.MinimumLifetime = time.Millisecond * 180 return pagerModel{ common: common, @@ -297,11 +295,10 @@ func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) { // Stash a local document if m.state != pagerStateStashing && stashableDocTypes.Contains(md.docType) { m.state = pagerStateStashing - m.spinner.Start() cmds = append( cmds, stashDocument(m.common.cc, md), - spinner.Tick, + m.spinner.Tick, ) } case "?": @@ -316,8 +313,8 @@ func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) { if m.state == pagerStateStashing || m.spinner.Visible() { // If we're still stashing, or if the spinner still needs to // finish, spin it along. - newSpinnerModel, cmd := m.spinner.Update(msg) - m.spinner = newSpinnerModel + var cmd tea.Cmd + m.spinner, cmd = m.spinner.Update(msg) cmds = append(cmds, cmd) } else if m.state == pagerStateStashSuccess && !m.spinner.Visible() { // If the spinner's finished and we haven't told the user the @@ -431,9 +428,7 @@ func (m pagerModel) statusBarView(b *strings.Builder) { // Status indicator; spinner or stash dot var statusIndicator string if m.state == pagerStateStashing || m.state == pagerStateStashSuccess { - if m.spinner.Visible() { - statusIndicator = statusBarNoteStyle(" ") + m.spinner.View() - } + statusIndicator = statusBarNoteStyle(" ") + m.spinner.View() } else if isStashed && showStatusMessage { statusIndicator = statusBarMessageStashIconStyle(" " + pagerStashIcon) } else if isStashed { diff --git a/ui/stash.go b/ui/stash.go index 9f8b47a..3470596 100644 --- a/ui/stash.go +++ b/ui/stash.go @@ -29,7 +29,7 @@ const ( ) var ( - stashedStatusMessage = statusMessage{normalStatusMessage, "Stashed!"} + stashingStatusMessage = statusMessage{normalStatusMessage, "Stashing..."} alreadyStashedStatusMessage = statusMessage{subtleStatusMessage, "Already stashed"} ) @@ -253,6 +253,14 @@ func (m stashModel) online() bool { return !m.localOnly() && m.common.authStatus == authOK } +// Whether or not the spinner should be spinning. +func (m stashModel) shouldSpin() bool { + loading := !m.loadingDone() + stashing := m.common.isStashing() + openingDocument := m.viewState == stashStateLoadingDocument + return loading || stashing || openingDocument +} + func (m *stashModel) setSize(width, height int) { m.common.width = width m.common.height = height @@ -444,7 +452,7 @@ func (m *stashModel) openMarkdown(md *markdown) tea.Cmd { cmd = loadRemoteMarkdown(m.common.cc, md) } - return tea.Batch(cmd, spinner.Tick) + return tea.Batch(cmd, m.spinner.Tick) } func (m *stashModel) newStatusMessage(sm statusMessage) tea.Cmd { @@ -509,21 +517,18 @@ func (m *stashModel) moveCursorDown() { // INIT func newStashModel(common *commonModel) stashModel { - sp := spinner.NewModel() + sp := spinner.New() sp.Spinner = spinner.Line sp.Style = stashSpinnerStyle - sp.HideFor = time.Millisecond * 100 - sp.MinimumLifetime = time.Millisecond * 180 - sp.Start() - ni := textinput.NewModel() + ni := textinput.New() ni.Prompt = "Memo:" ni.PromptStyle = stashInputPromptStyle ni.CursorStyle = stashInputCursorStyle ni.CharLimit = noteCharacterLimit ni.Focus() - si := textinput.NewModel() + si := textinput.New() si.Prompt = "Find:" si.PromptStyle = stashInputPromptStyle si.CursorStyle = stashInputCursorStyle @@ -647,14 +652,9 @@ func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) { return m, nil case spinner.TickMsg: - loading := !m.loadingDone() - stashing := m.common.isStashing() - openingDocument := m.viewState == stashStateLoadingDocument - spinnerVisible := m.spinner.Visible() - - if loading || stashing || openingDocument || spinnerVisible { - newSpinnerModel, cmd := m.spinner.Update(msg) - m.spinner = newSpinnerModel + if m.shouldSpin() { + var cmd tea.Cmd + m.spinner, cmd = m.spinner.Update(msg) cmds = append(cmds, cmd) } @@ -667,10 +667,9 @@ func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) { } } - // Note: mechanical stuff related to stash success is handled in the parent - // update function. case stashSuccessMsg: - m.spinner.Finish() + // No-op: mechanical stuff related to stash success is handled in the + // parent update function. // Note: mechanical stuff related to stash failure is handled in the parent // update function. @@ -848,14 +847,13 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd { break } - // Checks passed; perform the stash. Note that we optimistically - // show the status message. + // Checks passed; perform the stash. m.common.filesStashed[md.stashID] = struct{}{} m.common.filesStashing[md.stashID] = struct{}{} m.common.latestFileStashed = md.stashID cmds = append(cmds, stashDocument(m.common.cc, *md), - m.newStatusMessage(stashedStatusMessage), + m.newStatusMessage(stashingStatusMessage), ) // If we're stashing a filtered item, optimistically convert the @@ -870,12 +868,9 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd { // The spinner subtly shows the stash state in a non-optimistic // fashion, namely because it was originally implemented this way. - // If this stash succeeds quickly enough, the spinner won't run - // at all. - if m.loadingDone() && !m.spinner.Visible() { - m.spinner.Start() - cmds = append(cmds, spinner.Tick) - } + // Ideally, if this stash succeeds quickly enough, the spinner + // wouldn't run at all. + cmds = append(cmds, m.spinner.Tick) // Prompt for deletion case "x": @@ -1146,7 +1141,7 @@ func (m stashModel) view() string { case stashStateReady: loadingIndicator := " " - if !m.loadingDone() || m.spinner.Visible() { + if m.shouldSpin() { loadingIndicator = m.spinner.View() } diff --git a/ui/stashitem.go b/ui/stashitem.go index 4389754..0206364 100644 --- a/ui/stashitem.go +++ b/ui/stashitem.go @@ -66,7 +66,7 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { date = dullYellowFg(date) default: if m.common.latestFileStashed == md.stashID && - m.statusMessage == stashedStatusMessage { + m.statusMessage == stashingStatusMessage { gutter = greenFg(verticalLine) icon = dimGreenFg(icon) title = greenFg(title) @@ -90,7 +90,7 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { gutter = " " if m.common.latestFileStashed == md.stashID && - m.statusMessage == stashedStatusMessage { + m.statusMessage == stashingStatusMessage { icon = dimGreenFg(icon) title = greenFg(title) date = semiDimGreenFg(date) diff --git a/ui/ui.go b/ui/ui.go index c7867a3..b5d2688 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -10,7 +10,6 @@ import ( "strings" "time" - "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" charm "github.com/charmbracelet/charm/proto" "github.com/charmbracelet/charm/ui/keygen" @@ -184,8 +183,8 @@ func (m *model) unloadDocument() []tea.Cmd { batch = append(batch, tea.ClearScrollArea) } - if !m.stash.loadingDone() { - batch = append(batch, spinner.Tick) + if !m.stash.shouldSpin() { + batch = append(batch, m.stash.spinner.Tick) } return batch } @@ -226,7 +225,7 @@ func (m model) Init() tea.Cmd { if d.Contains(StashedDoc) || d.Contains(NewsDoc) { cmds = append(cmds, newCharmClient, - spinner.Tick, + m.stash.spinner.Tick, ) }