mirror of
https://github.com/charmbracelet/glow.git
synced 2026-08-17 05:39:11 +02:00
Only show news in news section
This commit is contained in:
parent
c10a84adc1
commit
40a02259fc
6 changed files with 471 additions and 420 deletions
4
main.go
4
main.go
|
|
@ -294,9 +294,9 @@ func runTUI(stashedOnly bool) error {
|
|||
cfg.GlamourStyle = style
|
||||
|
||||
if stashedOnly {
|
||||
cfg.DocumentTypes = ui.StashedDocuments | ui.NewsDocuments
|
||||
cfg.DocumentTypes = ui.StashedDocument | ui.NewsDocument
|
||||
} else if localOnly {
|
||||
cfg.DocumentTypes = ui.LocalDocuments
|
||||
cfg.DocumentTypes = ui.LocalDocument
|
||||
}
|
||||
|
||||
// Run Bubble Tea program
|
||||
|
|
|
|||
|
|
@ -2,25 +2,29 @@ package ui
|
|||
|
||||
import (
|
||||
"log"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/charmbracelet/charm"
|
||||
"github.com/dustin/go-humanize"
|
||||
"golang.org/x/text/transform"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// markdownType allows us to differentiate between the types of markdown
|
||||
// documents we're dealing with.
|
||||
type markdownType int
|
||||
type DocumentType byte
|
||||
|
||||
const (
|
||||
stashedMarkdown markdownType = iota
|
||||
newsMarkdown
|
||||
localMarkdown
|
||||
convertedMarkdown // used to be local, now its stashed
|
||||
LocalDocument DocumentType = 1 << iota
|
||||
StashedDocument
|
||||
ConvertedDocument
|
||||
NewsDocument
|
||||
)
|
||||
|
||||
// markdown wraps charm.Markdown.
|
||||
type markdown struct {
|
||||
markdownType markdownType
|
||||
markdownType DocumentType
|
||||
|
||||
// Full path of a local markdown file. Only relevant to local documents and
|
||||
// those that have been stashed in this session.
|
||||
|
|
@ -43,7 +47,7 @@ func (m *markdown) buildFilterValue() {
|
|||
m.filterValue = m.Note
|
||||
}
|
||||
|
||||
if m.markdownType == newsMarkdown {
|
||||
if m.markdownType == NewsDocument {
|
||||
m.filterValue = "News: " + note
|
||||
return
|
||||
}
|
||||
|
|
@ -54,7 +58,7 @@ func (m *markdown) buildFilterValue() {
|
|||
// sortAsLocal returns whether or not this markdown should be sorted as though
|
||||
// it's a local markdown document.
|
||||
func (m markdown) sortAsLocal() bool {
|
||||
return m.markdownType == localMarkdown || m.markdownType == convertedMarkdown
|
||||
return m.markdownType == LocalDocument || m.markdownType == ConvertedDocument
|
||||
}
|
||||
|
||||
// Sort documents with local files first, then by date.
|
||||
|
|
@ -88,3 +92,64 @@ func (m markdownsByLocalFirst) Less(i, j int) bool {
|
|||
// If the timestamps also match, sort by ID.
|
||||
return m[i].ID > m[j].ID
|
||||
}
|
||||
|
||||
func (m markdown) relativeTime() string {
|
||||
return relativeTime(m.CreatedAt)
|
||||
}
|
||||
|
||||
// Normalize text to aid in the filtering process. In particular, we remove
|
||||
// diacritics, "ö" becomes "o".
|
||||
func normalize(in string) (string, error) {
|
||||
t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
|
||||
out, _, err := transform.String(t, in)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// Returns whether a given rune is a nonspacing mark (Mn is the key for
|
||||
// nonspacing marks)
|
||||
func isMn(r rune) bool {
|
||||
return unicode.Is(unicode.Mn, r)
|
||||
}
|
||||
|
||||
// wrapMarkdowns wraps a *charm.Markdown with a *markdown in order to add some
|
||||
// extra metadata.
|
||||
func wrapMarkdowns(t DocumentType, md []*charm.Markdown) (m []*markdown) {
|
||||
for _, v := range md {
|
||||
m = append(m, &markdown{
|
||||
markdownType: t,
|
||||
Markdown: *v,
|
||||
})
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func relativeTime(then time.Time) string {
|
||||
now := time.Now()
|
||||
ago := now.Sub(then)
|
||||
if ago < time.Minute {
|
||||
return "just now"
|
||||
} else if ago < humanize.Week {
|
||||
return humanize.CustomRelTime(then, now, "ago", "from now", magnitudes)
|
||||
}
|
||||
return then.Format("02 Jan 2006 15:04 MST")
|
||||
}
|
||||
|
||||
var magnitudes = []humanize.RelTimeMagnitude{
|
||||
{D: time.Second, Format: "now", DivBy: time.Second},
|
||||
{D: 2 * time.Second, Format: "1 second %s", DivBy: 1},
|
||||
{D: time.Minute, Format: "%d seconds %s", DivBy: time.Second},
|
||||
{D: 2 * time.Minute, Format: "1 minute %s", DivBy: 1},
|
||||
{D: time.Hour, Format: "%d minutes %s", DivBy: time.Minute},
|
||||
{D: 2 * time.Hour, Format: "1 hour %s", DivBy: 1},
|
||||
{D: humanize.Day, Format: "%d hours %s", DivBy: time.Hour},
|
||||
{D: 2 * humanize.Day, Format: "1 day %s", DivBy: 1},
|
||||
{D: humanize.Week, Format: "%d days %s", DivBy: humanize.Day},
|
||||
{D: 2 * humanize.Week, Format: "1 week %s", DivBy: 1},
|
||||
{D: humanize.Month, Format: "%d weeks %s", DivBy: humanize.Week},
|
||||
{D: 2 * humanize.Month, Format: "1 month %s", DivBy: 1},
|
||||
{D: humanize.Year, Format: "%d months %s", DivBy: humanize.Month},
|
||||
{D: 18 * humanize.Month, Format: "1 year %s", DivBy: 1},
|
||||
{D: 2 * humanize.Year, Format: "2 years %s", DivBy: 1},
|
||||
{D: humanize.LongTime, Format: "%d years %s", DivBy: humanize.Year},
|
||||
{D: math.MaxInt64, Format: "a long while %s", DivBy: 1},
|
||||
}
|
||||
|
|
|
|||
14
ui/pager.go
14
ui/pager.go
|
|
@ -173,7 +173,7 @@ func (m *pagerModel) unload() {
|
|||
m.textInput.Reset()
|
||||
}
|
||||
|
||||
func (m pagerModel) Update(msg tea.Msg) (pagerModel, tea.Cmd) {
|
||||
func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) {
|
||||
var (
|
||||
cmd tea.Cmd
|
||||
cmds []tea.Cmd
|
||||
|
|
@ -215,8 +215,8 @@ func (m pagerModel) Update(msg tea.Msg) (pagerModel, tea.Cmd) {
|
|||
cmds = append(cmds, viewport.Sync(m.viewport))
|
||||
}
|
||||
case "m":
|
||||
isStashed := m.currentDocument.markdownType == stashedMarkdown ||
|
||||
m.currentDocument.markdownType == convertedMarkdown
|
||||
isStashed := m.currentDocument.markdownType == StashedDocument ||
|
||||
m.currentDocument.markdownType == ConvertedDocument
|
||||
|
||||
// Users can only set the note on user-stashed markdown
|
||||
if !isStashed {
|
||||
|
|
@ -244,7 +244,7 @@ func (m pagerModel) Update(msg tea.Msg) (pagerModel, tea.Cmd) {
|
|||
}
|
||||
|
||||
// Stash a local document
|
||||
if m.state != pagerStateStashing && m.currentDocument.markdownType == localMarkdown {
|
||||
if m.state != pagerStateStashing && m.currentDocument.markdownType == LocalDocument {
|
||||
m.state = pagerStateStashing
|
||||
m.spinner.Start()
|
||||
cmds = append(
|
||||
|
|
@ -348,7 +348,7 @@ func (m pagerModel) statusBarView(b *strings.Builder) {
|
|||
percentToStringMagnitude float64 = 100.0
|
||||
)
|
||||
var (
|
||||
isStashed bool = m.currentDocument.markdownType == stashedMarkdown || m.currentDocument.markdownType == convertedMarkdown
|
||||
isStashed bool = m.currentDocument.markdownType == StashedDocument || m.currentDocument.markdownType == ConvertedDocument
|
||||
showStatusMessage bool = m.state == pagerStateStatusMessage
|
||||
)
|
||||
|
||||
|
|
@ -440,7 +440,7 @@ func (m pagerModel) setNoteView(b *strings.Builder) {
|
|||
|
||||
func (m pagerModel) helpView() (s string) {
|
||||
memoOrStash := "m set memo"
|
||||
if m.general.authStatus == authOK && m.currentDocument.markdownType == localMarkdown {
|
||||
if m.general.authStatus == authOK && m.currentDocument.markdownType == LocalDocument {
|
||||
memoOrStash = "s stash this document"
|
||||
}
|
||||
|
||||
|
|
@ -453,7 +453,7 @@ func (m pagerModel) helpView() (s string) {
|
|||
"q quit",
|
||||
}
|
||||
|
||||
if m.currentDocument.markdownType == newsMarkdown {
|
||||
if m.currentDocument.markdownType == NewsDocument {
|
||||
deleteFromStringSlice(col1, 3)
|
||||
}
|
||||
|
||||
|
|
|
|||
862
ui/stash.go
862
ui/stash.go
File diff suppressed because it is too large
Load diff
|
|
@ -23,18 +23,18 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) {
|
|||
truncateTo = m.general.width - stashViewHorizontalPadding*2
|
||||
gutter string
|
||||
title = md.Note
|
||||
date = relativeTime(md.CreatedAt)
|
||||
date = md.relativeTime()
|
||||
icon = ""
|
||||
)
|
||||
|
||||
switch md.markdownType {
|
||||
case newsMarkdown:
|
||||
case NewsDocument:
|
||||
if title == "" {
|
||||
title = "News"
|
||||
} else {
|
||||
title = newsPrefix + truncate(title, truncateTo-rw.StringWidth(newsPrefix))
|
||||
}
|
||||
case stashedMarkdown, convertedMarkdown:
|
||||
case StashedDocument, ConvertedDocument:
|
||||
icon = fileListingStashIcon
|
||||
if title == "" {
|
||||
title = noMemoTitle
|
||||
|
|
@ -81,7 +81,7 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) {
|
|||
} else {
|
||||
// Regular (non-selected) items
|
||||
|
||||
if md.markdownType == newsMarkdown {
|
||||
if md.markdownType == NewsDocument {
|
||||
gutter = " "
|
||||
|
||||
if isFilteringNotes && m.filterInput.Value() == "" {
|
||||
|
|
|
|||
34
ui/ui.go
34
ui/ui.go
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/charmbracelet/charm/keygen"
|
||||
"github.com/charmbracelet/charm/ui/common"
|
||||
"github.com/charmbracelet/glow/utils"
|
||||
runewidth "github.com/mattn/go-runewidth"
|
||||
"github.com/muesli/gitcha"
|
||||
te "github.com/muesli/termenv"
|
||||
)
|
||||
|
|
@ -194,7 +195,7 @@ func newModel(cfg Config) tea.Model {
|
|||
}
|
||||
|
||||
if cfg.DocumentTypes == 0 {
|
||||
cfg.DocumentTypes = LocalDocuments | StashedDocuments | NewsDocuments
|
||||
cfg.DocumentTypes = LocalDocument | StashedDocument | NewsDocument
|
||||
}
|
||||
|
||||
general := general{
|
||||
|
|
@ -213,15 +214,16 @@ func newModel(cfg Config) tea.Model {
|
|||
|
||||
func (m model) Init() tea.Cmd {
|
||||
var cmds []tea.Cmd
|
||||
d := &m.general.cfg.DocumentTypes
|
||||
|
||||
if m.general.cfg.DocumentTypes&StashedDocuments != 0 || m.general.cfg.DocumentTypes&NewsDocuments != 0 {
|
||||
if *d&StashedDocument != 0 || *d&NewsDocument != 0 {
|
||||
cmds = append(cmds,
|
||||
newCharmClient,
|
||||
spinner.Tick,
|
||||
)
|
||||
}
|
||||
|
||||
if m.general.cfg.DocumentTypes&LocalDocuments != 0 {
|
||||
if *d&LocalDocument != 0 {
|
||||
cmds = append(cmds, findLocalFiles(m))
|
||||
}
|
||||
|
||||
|
|
@ -265,7 +267,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
return m, tea.Quit
|
||||
}
|
||||
|
||||
m.stash, cmd = stashUpdate(msg, m.stash)
|
||||
m.stash, cmd = m.stash.update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
|
|
@ -275,7 +277,7 @@ func (m model) Update(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(msg)
|
||||
m.pager = newPagerModel
|
||||
batch = append(batch, cmd)
|
||||
return m, tea.Batch(batch...)
|
||||
|
|
@ -337,7 +339,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
}
|
||||
|
||||
// Even though it failed, news/stash loading is finished
|
||||
m.stash.loaded |= StashedDocuments | NewsDocuments
|
||||
m.stash.loaded |= StashedDocument | NewsDocument
|
||||
m.stash.loadingFromNetwork = false
|
||||
}
|
||||
|
||||
|
|
@ -352,7 +354,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
m.keygenState = keygenFinished
|
||||
|
||||
// Even though it failed, news/stash loading is finished
|
||||
m.stash.loaded |= StashedDocuments | NewsDocuments
|
||||
m.stash.loaded |= StashedDocument | NewsDocument
|
||||
m.stash.loadingFromNetwork = false
|
||||
|
||||
case keygenSuccessMsg:
|
||||
|
|
@ -381,7 +383,7 @@ func (m model) Update(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 := stashUpdate(msg, m.stash)
|
||||
stashModel, cmd := m.stash.update(msg)
|
||||
m.stash = stashModel
|
||||
return m, cmd
|
||||
|
||||
|
|
@ -389,7 +391,7 @@ func (m model) Update(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 := stashUpdate(msg, m.stash)
|
||||
stashModel, cmd := m.stash.update(msg)
|
||||
m.stash = stashModel
|
||||
return m, cmd
|
||||
|
||||
|
|
@ -417,12 +419,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
// Process children
|
||||
switch m.state {
|
||||
case stateShowStash:
|
||||
newStashModel, cmd := stashUpdate(msg, m.stash)
|
||||
newStashModel, cmd := m.stash.update(msg)
|
||||
m.stash = newStashModel
|
||||
cmds = append(cmds, cmd)
|
||||
|
||||
case stateShowDocument:
|
||||
newPagerModel, cmd := m.pager.Update(msg)
|
||||
newPagerModel, cmd := m.pager.update(msg)
|
||||
m.pager = newPagerModel
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
|
|
@ -439,7 +441,7 @@ func (m model) View() string {
|
|||
case stateShowDocument:
|
||||
return m.pager.View()
|
||||
default:
|
||||
return stashView(m.stash)
|
||||
return m.stash.view()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -637,7 +639,7 @@ func stashDocument(cc *charm.Client, md markdown) tea.Cmd {
|
|||
}
|
||||
|
||||
// Turn local markdown into a newly stashed (converted) markdown
|
||||
md.markdownType = convertedMarkdown
|
||||
md.markdownType = ConvertedDocument
|
||||
md.CreatedAt = time.Now()
|
||||
|
||||
// Set the note as the filename without the extension
|
||||
|
|
@ -673,7 +675,7 @@ func waitForStatusMessageTimeout(appCtx applicationContext, t *time.Timer) tea.C
|
|||
// already done that.
|
||||
func localFileToMarkdown(cwd string, res gitcha.SearchResult) *markdown {
|
||||
md := &markdown{
|
||||
markdownType: localMarkdown,
|
||||
markdownType: LocalDocument,
|
||||
localPath: res.Path,
|
||||
Markdown: charm.Markdown{
|
||||
Note: stripAbsolutePath(res.Path, cwd),
|
||||
|
|
@ -702,6 +704,10 @@ func indent(s string, n int) string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
func truncate(str string, num int) string {
|
||||
return runewidth.Truncate(str, num, "…")
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue