mirror of
https://github.com/charmbracelet/glow
synced 2024-11-10 06:04:18 +00:00
Now you can filter news too
Also note that the main stash state and the filter state now operate independently.
This commit is contained in:
parent
40a02259fc
commit
fc898831af
3 changed files with 50 additions and 71 deletions
75
ui/stash.go
75
ui/stash.go
|
@ -51,14 +51,21 @@ const (
|
||||||
stashStateLoadingDocument
|
stashStateLoadingDocument
|
||||||
stashStateSettingNote
|
stashStateSettingNote
|
||||||
stashStateShowingError
|
stashStateShowingError
|
||||||
stashStateFilterNotes
|
|
||||||
stashStateShowFiltered
|
|
||||||
stashStateShowNews
|
stashStateShowNews
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type filterState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
unfiltered filterState = iota
|
||||||
|
filtering // user is actively setting a filter
|
||||||
|
filterApplied // a filter is applied and user is not editing filter
|
||||||
|
)
|
||||||
|
|
||||||
type stashModel struct {
|
type stashModel struct {
|
||||||
general *general
|
general *general
|
||||||
state stashState
|
state stashState
|
||||||
|
filterState filterState
|
||||||
err error
|
err error
|
||||||
spinner spinner.Model
|
spinner spinner.Model
|
||||||
noteInput textinput.Model
|
noteInput textinput.Model
|
||||||
|
@ -124,6 +131,7 @@ func (m *stashModel) setSize(width, height int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *stashModel) resetFiltering() {
|
func (m *stashModel) resetFiltering() {
|
||||||
|
m.filterState = unfiltered
|
||||||
m.filterInput.Reset()
|
m.filterInput.Reset()
|
||||||
sort.Stable(markdownsByLocalFirst(m.markdowns))
|
sort.Stable(markdownsByLocalFirst(m.markdowns))
|
||||||
m.filteredMarkdowns = nil
|
m.filteredMarkdowns = nil
|
||||||
|
@ -132,14 +140,7 @@ func (m *stashModel) resetFiltering() {
|
||||||
|
|
||||||
// Is a filter currently being applied?
|
// Is a filter currently being applied?
|
||||||
func (m stashModel) isFiltering() bool {
|
func (m stashModel) isFiltering() bool {
|
||||||
switch m.state {
|
return m.filterState != unfiltered
|
||||||
case stashStateFilterNotes, stashStateShowFiltered:
|
|
||||||
return true
|
|
||||||
case stashStatePromptDelete, stashStateSettingNote:
|
|
||||||
return m.filterInput.Value() != ""
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should we be updating the filter?
|
// Should we be updating the filter?
|
||||||
|
@ -422,8 +423,7 @@ func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) {
|
||||||
// Stash or news results have come in from the server.
|
// Stash or news results have come in from the server.
|
||||||
//
|
//
|
||||||
// With the stash, this doesn't mean the whole stash listing is loaded,
|
// With the stash, this doesn't mean the whole stash listing is loaded,
|
||||||
// but some we've finished checking for the stash, at least, so mark
|
// but now know it can load, at least, so mark the stash as loaded here.
|
||||||
// the stash as loaded here.
|
|
||||||
var docs []*markdown
|
var docs []*markdown
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
|
@ -507,14 +507,17 @@ func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.filterState == filtering {
|
||||||
|
cmds = append(cmds, m.handleFiltering(msg))
|
||||||
|
return m, tea.Batch(cmds...)
|
||||||
|
}
|
||||||
|
|
||||||
// Updates per the current state
|
// Updates per the current state
|
||||||
switch m.state {
|
switch m.state {
|
||||||
case stashStateReady, stashStateShowFiltered, stashStateShowNews:
|
case stashStateReady, stashStateShowNews:
|
||||||
cmds = append(cmds, m.handleDocumentBrowsing(msg))
|
cmds = append(cmds, m.handleDocumentBrowsing(msg))
|
||||||
case stashStatePromptDelete:
|
case stashStatePromptDelete:
|
||||||
cmds = append(cmds, m.handleDeleteConfirmation(msg))
|
cmds = append(cmds, m.handleDeleteConfirmation(msg))
|
||||||
case stashStateFilterNotes:
|
|
||||||
cmds = append(cmds, m.handleFiltering(msg))
|
|
||||||
case stashStateSettingNote:
|
case stashStateSettingNote:
|
||||||
cmds = append(cmds, m.handleNoteInput(msg))
|
cmds = append(cmds, m.handleNoteInput(msg))
|
||||||
case stashStateShowingError:
|
case stashStateShowingError:
|
||||||
|
@ -553,10 +556,9 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd {
|
||||||
m.paginator.Page = m.paginator.TotalPages - 1
|
m.paginator.Page = m.paginator.TotalPages - 1
|
||||||
m.index = m.paginator.ItemsOnPage(pages) - 1
|
m.index = m.paginator.ItemsOnPage(pages) - 1
|
||||||
|
|
||||||
// Note: esc is only passed trough in stashStateFilterNotes and stashStateShowNews
|
|
||||||
case "esc":
|
case "esc":
|
||||||
m.state = stashStateReady
|
|
||||||
m.resetFiltering()
|
m.resetFiltering()
|
||||||
|
m.state = stashStateReady
|
||||||
|
|
||||||
// Open document
|
// Open document
|
||||||
case "enter":
|
case "enter":
|
||||||
|
@ -584,7 +586,7 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd {
|
||||||
|
|
||||||
m.paginator.Page = 0
|
m.paginator.Page = 0
|
||||||
m.index = 0
|
m.index = 0
|
||||||
m.state = stashStateFilterNotes
|
m.filterState = filtering
|
||||||
m.filterInput.CursorEnd()
|
m.filterInput.CursorEnd()
|
||||||
m.filterInput.Focus()
|
m.filterInput.Focus()
|
||||||
return textinput.Blink
|
return textinput.Blink
|
||||||
|
@ -750,13 +752,6 @@ func (m *stashModel) handleDeleteConfirmation(msg tea.Msg) tea.Cmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set state and delete
|
|
||||||
if m.isFiltering() {
|
|
||||||
m.state = stashStateShowFiltered
|
|
||||||
} else {
|
|
||||||
m.state = stashStateReady
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update pagination
|
// Update pagination
|
||||||
m.setTotalPages()
|
m.setTotalPages()
|
||||||
|
|
||||||
|
@ -765,10 +760,6 @@ func (m *stashModel) handleDeleteConfirmation(msg tea.Msg) tea.Cmd {
|
||||||
default:
|
default:
|
||||||
// Any other keys cancels deletion
|
// Any other keys cancels deletion
|
||||||
m.state = stashStateReady
|
m.state = stashStateReady
|
||||||
if m.filterInput.Value() != "" {
|
|
||||||
m.state = stashStateShowFiltered
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -785,7 +776,6 @@ func (m *stashModel) handleFiltering(msg tea.Msg) tea.Cmd {
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "esc":
|
case "esc":
|
||||||
// Cancel filtering
|
// Cancel filtering
|
||||||
m.state = stashStateReady
|
|
||||||
m.resetFiltering()
|
m.resetFiltering()
|
||||||
case "enter", "tab", "shift+tab", "ctrl+k", "up", "ctrl+j", "down":
|
case "enter", "tab", "shift+tab", "ctrl+k", "up", "ctrl+j", "down":
|
||||||
m.hideStatusMessage()
|
m.hideStatusMessage()
|
||||||
|
@ -814,9 +804,8 @@ func (m *stashModel) handleFiltering(msg tea.Msg) tea.Cmd {
|
||||||
|
|
||||||
m.filterInput.Blur()
|
m.filterInput.Blur()
|
||||||
|
|
||||||
m.state = stashStateShowFiltered
|
m.filterState = filterApplied
|
||||||
if m.filterInput.Value() == "" {
|
if m.filterInput.Value() == "" {
|
||||||
m.state = stashStateReady
|
|
||||||
m.resetFiltering()
|
m.resetFiltering()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -847,12 +836,8 @@ func (m *stashModel) handleNoteInput(msg tea.Msg) tea.Cmd {
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "esc":
|
case "esc":
|
||||||
// Cancel note
|
// Cancel note
|
||||||
if m.filterInput.Value() != "" {
|
|
||||||
m.state = stashStateShowFiltered
|
|
||||||
} else {
|
|
||||||
m.state = stashStateReady
|
|
||||||
}
|
|
||||||
m.noteInput.Reset()
|
m.noteInput.Reset()
|
||||||
|
m.state = stashStateReady
|
||||||
case "enter":
|
case "enter":
|
||||||
// Set new note
|
// Set new note
|
||||||
md := m.selectedMarkdown()
|
md := m.selectedMarkdown()
|
||||||
|
@ -860,11 +845,7 @@ func (m *stashModel) handleNoteInput(msg tea.Msg) tea.Cmd {
|
||||||
cmd := saveDocumentNote(m.general.cc, md.ID, newNote)
|
cmd := saveDocumentNote(m.general.cc, md.ID, newNote)
|
||||||
md.Note = newNote
|
md.Note = newNote
|
||||||
m.noteInput.Reset()
|
m.noteInput.Reset()
|
||||||
if m.filterInput.Value() != "" {
|
|
||||||
m.state = stashStateShowFiltered
|
|
||||||
} else {
|
|
||||||
m.state = stashStateReady
|
m.state = stashStateReady
|
||||||
}
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -890,7 +871,7 @@ func (m stashModel) view() string {
|
||||||
return errorView(m.err, false)
|
return errorView(m.err, false)
|
||||||
case stashStateLoadingDocument:
|
case stashStateLoadingDocument:
|
||||||
s += " " + m.spinner.View() + " Loading document..."
|
s += " " + m.spinner.View() + " Loading document..."
|
||||||
case stashStateReady, stashStateSettingNote, stashStatePromptDelete, stashStateFilterNotes, stashStateShowFiltered, stashStateShowNews:
|
case stashStateReady, stashStateSettingNote, stashStatePromptDelete, stashStateShowNews:
|
||||||
|
|
||||||
loadingIndicator := " "
|
loadingIndicator := " "
|
||||||
if !m.localOnly() && (!m.loadingDone() || m.loadingFromNetwork || m.spinner.Visible()) {
|
if !m.localOnly() && (!m.loadingDone() || m.loadingFromNetwork || m.spinner.Visible()) {
|
||||||
|
@ -926,7 +907,7 @@ func (m stashModel) view() string {
|
||||||
logoOrFilter := glowLogoView(" Glow ")
|
logoOrFilter := glowLogoView(" Glow ")
|
||||||
|
|
||||||
// If we're filtering we replace the logo with the filter field
|
// If we're filtering we replace the logo with the filter field
|
||||||
if m.state == stashStateFilterNotes || m.state == stashStateShowFiltered {
|
if m.isFiltering() {
|
||||||
logoOrFilter = m.filterInput.View()
|
logoOrFilter = m.filterInput.View()
|
||||||
} else if m.state == stashStateShowNews {
|
} else if m.state == stashStateShowNews {
|
||||||
logoOrFilter += newsTitleStyle(" News ")
|
logoOrFilter += newsTitleStyle(" News ")
|
||||||
|
@ -1081,11 +1062,11 @@ func stashHelpView(m stashModel) string {
|
||||||
h = append(h, "enter: confirm", "esc: cancel")
|
h = append(h, "enter: confirm", "esc: cancel")
|
||||||
} else if m.state == stashStatePromptDelete {
|
} else if m.state == stashStatePromptDelete {
|
||||||
h = append(h, "y: delete", "n: cancel")
|
h = append(h, "y: delete", "n: cancel")
|
||||||
} else if m.state == stashStateFilterNotes && numDocs == 1 {
|
} else if m.filterState == filtering && numDocs == 1 {
|
||||||
h = append(h, "enter: open", "esc: cancel")
|
h = append(h, "enter: open", "esc: cancel")
|
||||||
} else if m.state == stashStateFilterNotes && numDocs == 0 {
|
} else if m.filterState == filtering && numDocs == 0 {
|
||||||
h = append(h, "enter/esc: cancel")
|
h = append(h, "enter/esc: cancel")
|
||||||
} else if m.state == stashStateFilterNotes {
|
} else if m.filterState == filtering {
|
||||||
h = append(h, "enter: confirm", "esc: cancel", "ctrl+j/ctrl+k, ↑/↓: choose")
|
h = append(h, "enter: confirm", "esc: cancel", "ctrl+j/ctrl+k, ↑/↓: choose")
|
||||||
} else if m.state == stashStateShowNews {
|
} else if m.state == stashStateShowNews {
|
||||||
h = append(h, "enter: open", "esc: return", "j/k, ↑/↓: choose", "q: quit")
|
h = append(h, "enter: open", "esc: return", "j/k, ↑/↓: choose", "q: quit")
|
||||||
|
@ -1093,7 +1074,7 @@ func stashHelpView(m stashModel) string {
|
||||||
if len(m.markdowns) > 0 {
|
if len(m.markdowns) > 0 {
|
||||||
h = append(h, "enter: open")
|
h = append(h, "enter: open")
|
||||||
}
|
}
|
||||||
if m.state == stashStateShowFiltered {
|
if m.filterState == filterApplied {
|
||||||
h = append(h, "esc: clear filter")
|
h = append(h, "esc: clear filter")
|
||||||
}
|
}
|
||||||
if len(m.markdowns) > 1 {
|
if len(m.markdowns) > 1 {
|
||||||
|
|
|
@ -45,15 +45,15 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) {
|
||||||
}
|
}
|
||||||
|
|
||||||
isSelected := index == m.index
|
isSelected := index == m.index
|
||||||
isFilteringNotes := m.state == stashStateFilterNotes
|
isFiltering := m.filterState == filtering
|
||||||
|
|
||||||
// If there are multiple items being filtered we don't highlight a selected
|
// If there are multiple items being filtered we don't highlight a selected
|
||||||
// item in the results. If we've filtered down to one item, however,
|
// item in the results. If we've filtered down to one item, however,
|
||||||
// highlight that first item since pressing return will open it.
|
// highlight that first item since pressing return will open it.
|
||||||
singleFilteredItem :=
|
singleFilteredItem :=
|
||||||
m.state == stashStateFilterNotes && len(m.getVisibleMarkdowns()) == 1
|
isFiltering && len(m.getVisibleMarkdowns()) == 1
|
||||||
|
|
||||||
if isSelected && !isFilteringNotes || singleFilteredItem {
|
if isSelected && !isFiltering || singleFilteredItem {
|
||||||
// Selected item
|
// Selected item
|
||||||
|
|
||||||
switch m.state {
|
switch m.state {
|
||||||
|
@ -70,7 +70,7 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) {
|
||||||
default:
|
default:
|
||||||
gutter = dullFuchsiaFg(verticalLine)
|
gutter = dullFuchsiaFg(verticalLine)
|
||||||
icon = dullFuchsiaFg(icon)
|
icon = dullFuchsiaFg(icon)
|
||||||
if m.state == stashStateShowFiltered || singleFilteredItem {
|
if m.filterState == filterApplied || singleFilteredItem {
|
||||||
s := termenv.Style{}.Foreground(common.Fuschia.Color())
|
s := termenv.Style{}.Foreground(common.Fuschia.Color())
|
||||||
title = styleFilteredText(title, m.filterInput.Value(), s, s.Underline())
|
title = styleFilteredText(title, m.filterInput.Value(), s, s.Underline())
|
||||||
} else {
|
} else {
|
||||||
|
@ -84,7 +84,7 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) {
|
||||||
if md.markdownType == NewsDocument {
|
if md.markdownType == NewsDocument {
|
||||||
gutter = " "
|
gutter = " "
|
||||||
|
|
||||||
if isFilteringNotes && m.filterInput.Value() == "" {
|
if isFiltering && m.filterInput.Value() == "" {
|
||||||
title = dimIndigoFg(title)
|
title = dimIndigoFg(title)
|
||||||
date = dimSubtleIndigoFg(date)
|
date = dimSubtleIndigoFg(date)
|
||||||
} else {
|
} else {
|
||||||
|
@ -92,7 +92,7 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) {
|
||||||
title = styleFilteredText(title, m.filterInput.Value(), s, s.Underline())
|
title = styleFilteredText(title, m.filterInput.Value(), s, s.Underline())
|
||||||
date = subtleIndigoFg(date)
|
date = subtleIndigoFg(date)
|
||||||
}
|
}
|
||||||
} else if isFilteringNotes && m.filterInput.Value() == "" {
|
} else if isFiltering && m.filterInput.Value() == "" {
|
||||||
icon = dimGreenFg(icon)
|
icon = dimGreenFg(icon)
|
||||||
if title == noMemoTitle {
|
if title == noMemoTitle {
|
||||||
title = dimWarmGrayFg(title)
|
title = dimWarmGrayFg(title)
|
||||||
|
|
32
ui/ui.go
32
ui/ui.go
|
@ -164,15 +164,10 @@ type model struct {
|
||||||
// method alters the model we also need to send along any commands returned.
|
// method alters the model we also need to send along any commands returned.
|
||||||
func (m *model) unloadDocument() []tea.Cmd {
|
func (m *model) unloadDocument() []tea.Cmd {
|
||||||
m.state = stateShowStash
|
m.state = stateShowStash
|
||||||
|
m.stash.state = stashStateReady
|
||||||
m.pager.unload()
|
m.pager.unload()
|
||||||
m.pager.showHelp = false
|
m.pager.showHelp = false
|
||||||
|
|
||||||
if m.stash.filterInput.Value() == "" {
|
|
||||||
m.stash.state = stashStateReady
|
|
||||||
} else {
|
|
||||||
m.stash.state = stashStateShowFiltered
|
|
||||||
}
|
|
||||||
|
|
||||||
var batch []tea.Cmd
|
var batch []tea.Cmd
|
||||||
if m.pager.viewport.HighPerformanceRendering {
|
if m.pager.viewport.HighPerformanceRendering {
|
||||||
batch = append(batch, tea.ClearScrollArea)
|
batch = append(batch, tea.ClearScrollArea)
|
||||||
|
@ -250,19 +245,22 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
switch m.state {
|
switch m.state {
|
||||||
case stateShowStash:
|
case stateShowStash:
|
||||||
|
|
||||||
switch m.stash.state {
|
// Q quits if we're filtering, but we still send esc though.
|
||||||
|
if m.stash.isFiltering() {
|
||||||
// Send q/esc through in these cases
|
if msg.String() == "q" {
|
||||||
case stashStateSettingNote, stashStatePromptDelete,
|
|
||||||
stashStateShowingError, stashStateFilterNotes,
|
|
||||||
stashStateShowFiltered, stashStateShowNews:
|
|
||||||
|
|
||||||
// If we're fitering, only send esc through so we can clear
|
|
||||||
// the filter results. Q quits as normal.
|
|
||||||
if m.stash.state == stashStateShowFiltered && msg.String() == "q" {
|
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
// Q also quits glow when displaying only newsitems
|
m.stash, cmd = m.stash.update(msg)
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send q/esc through in these cases
|
||||||
|
switch m.stash.state {
|
||||||
|
case stashStateSettingNote, stashStatePromptDelete,
|
||||||
|
stashStateShowingError, stashStateShowNews:
|
||||||
|
|
||||||
|
// Q also quits glow when displaying only newsitems. Esc
|
||||||
|
// still passes through.
|
||||||
if m.stash.state == stashStateShowNews && msg.String() == "q" {
|
if m.stash.state == stashStateShowNews && msg.String() == "q" {
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue