commit 41a66392830d05a97d5309540648e82134203a45
parent 9e9c0d4bbf7f1b37918049c93c39b1871b1c5220
Author: FIGBERT <figbert@figbert.com>
Date: Thu, 14 Sep 2023 00:18:35 -0700
Add support for opening comment sections in Caret
Diffstat:
13 files changed, 427 insertions(+), 282 deletions(-)
diff --git a/api/api.go b/api/api.go
@@ -9,6 +9,7 @@ import (
)
type PageMsg []Post
+type PostMsg Post
type ErrorMsg struct{}
func FetchPage(index int) tea.Cmd {
@@ -18,11 +19,28 @@ func FetchPage(index int) tea.Cmd {
return ErrorMsg{}
}
- return PageMsg(*posts)
+ return PageMsg(posts)
}
}
-func page(i int) (*[]Post, error) {
+func FetchPost(id string) tea.Cmd {
+ return func() tea.Msg {
+ resp, err := http.Get(fmt.Sprintf("https://lobste.rs/s/%s.json", id))
+ if err != nil {
+ return ErrorMsg{}
+ }
+ defer resp.Body.Close()
+
+ var post Post
+ err = json.NewDecoder(resp.Body).Decode(&post)
+ if err != nil {
+ return ErrorMsg{}
+ }
+ return PostMsg(post)
+ }
+}
+
+func page(i int) ([]Post, error) {
resp, err := http.Get(fmt.Sprintf("https://lobste.rs/page/%d.json", i))
if err != nil {
return nil, err
@@ -30,6 +48,6 @@ func page(i int) (*[]Post, error) {
defer resp.Body.Close()
var posts []Post
- json.NewDecoder(resp.Body).Decode(&posts)
- return &posts, nil
+ err = json.NewDecoder(resp.Body).Decode(&posts)
+ return posts, err
}
diff --git a/api/schema.go b/api/schema.go
@@ -30,17 +30,18 @@ type Comment struct {
}
type Post struct {
- ShortID string `json:"short_id"`
- ShortIDURL string `json:"short_id_url"`
- CreatedAt string `json:"created_at"`
- Headline string `json:"title"`
- URL string `json:"url"`
- Score int `json:"score"`
- Flags int `json:"flags"`
- CommentCount int `json:"comment_count"`
- Paragraph string `json:"description"`
- DescriptionPlain string `json:"description_plain"`
- CommentsURL string `json:"comments_url"`
- User User `json:"submitter_user"`
- Tags []string `json:"tags"`
+ ShortID string `json:"short_id"`
+ ShortIDURL string `json:"short_id_url"`
+ CreatedAt string `json:"created_at"`
+ Headline string `json:"title"`
+ URL string `json:"url"`
+ Score int `json:"score"`
+ Flags int `json:"flags"`
+ CommentCount int `json:"comment_count"`
+ Paragraph string `json:"description"`
+ DescriptionPlain string `json:"description_plain"`
+ CommentsURL string `json:"comments_url"`
+ User User `json:"submitter_user"`
+ Tags []string `json:"tags"`
+ Comments []Comment `json:"comments"`
}
diff --git a/article/article.go b/article/article.go
@@ -7,10 +7,10 @@ import (
"time"
"git.figbert.com/caret/ui"
+ "git.figbert.com/caret/utils"
"github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/glamour"
- gloss "github.com/charmbracelet/lipgloss"
md "github.com/JohannesKaufmann/html-to-markdown"
"github.com/JohannesKaufmann/html-to-markdown/plugin"
@@ -18,50 +18,42 @@ import (
"github.com/go-shiori/go-readability"
)
-type Error struct{}
-type Start string
-type RunCommand *exec.Cmd
-type ReturnedFromReading struct{}
-
-func DisplayLinkedArticle(cmd *exec.Cmd) tea.Cmd {
- return tea.ExecProcess(cmd, func(_ error) tea.Msg {
- return ReturnedFromReading{}
- })
-}
-
func LoadLinkedArticle(url string) tea.Cmd {
return func() tea.Msg {
+ if url == "" {
+ return utils.NoExternalURL{}
+ }
+
title, article, err := getArticle(url)
if err != nil {
- return Error{}
+ return utils.Error{}
}
header := getReaderModeMetaBlock(title, url)
- renderer, err := glamour.NewTermRenderer(glamour.WithStyles(clxStyleConfig))
+ renderer, err := glamour.NewTermRenderer(glamour.WithStyles(ui.GlamourConfig))
if err != nil {
- return Error{}
+ return utils.Error{}
}
markdown, err := renderer.Render(article)
if err != nil {
- return Error{}
+ return utils.Error{}
}
renderedArticle := header + markdown
- promptStyle := gloss.NewStyle().Foreground(ui.LobstersRed()).Background(gloss.Color("7")).Padding(0, 1)
command := exec.Command("less",
"--RAW-CONTROL-CHARS",
"--ignore-case",
"--tilde",
"--use-color",
- "-P?e"+promptStyle.Render("End"),
+ "-P?e"+ui.Prompt().Render("End"),
"-DSy",
"-DP-")
command.Stdin = strings.NewReader(renderedArticle)
- return RunCommand(command)
+ return utils.RunCommand(command)
}
}
diff --git a/article/style.go b/article/style.go
@@ -1,214 +0,0 @@
-package article
-
-import (
- "github.com/charmbracelet/glamour/ansi"
-)
-
-var clxStyleConfig = ansi.StyleConfig{
- Document: ansi.StyleBlock{
- StylePrimitive: ansi.StylePrimitive{
- BlockPrefix: "\n",
- BlockSuffix: "\n",
- Color: stringPtr("252"),
- },
- Margin: uintPtr(2),
- },
- BlockQuote: ansi.StyleBlock{
- StylePrimitive: ansi.StylePrimitive{Color: stringPtr("244")},
- Indent: uintPtr(1),
- IndentToken: stringPtr("│ "),
- },
- List: ansi.StyleList{
- LevelIndent: 2,
- },
- Heading: ansi.StyleBlock{
- StylePrimitive: ansi.StylePrimitive{
- BlockSuffix: "\n",
- Color: stringPtr("39"),
- Bold: boolPtr(true),
- },
- },
- H1: ansi.StyleBlock{
- StylePrimitive: ansi.StylePrimitive{
- Prefix: " ",
- Suffix: " ",
- Color: stringPtr("228"),
- BackgroundColor: stringPtr("63"),
- Bold: boolPtr(true),
- },
- },
- H2: ansi.StyleBlock{
- StylePrimitive: ansi.StylePrimitive{
- Color: stringPtr("252"),
- },
- },
- H3: ansi.StyleBlock{
- StylePrimitive: ansi.StylePrimitive{
- Underline: boolPtr(true),
- },
- },
- H4: ansi.StyleBlock{
- StylePrimitive: ansi.StylePrimitive{
- Color: stringPtr("220"),
- Underline: boolPtr(true),
- },
- },
- H5: ansi.StyleBlock{
- StylePrimitive: ansi.StylePrimitive{
- Prefix: "##### ",
- },
- },
- H6: ansi.StyleBlock{
- StylePrimitive: ansi.StylePrimitive{
- Prefix: "###### ",
- Color: stringPtr("35"),
- Bold: boolPtr(false),
- },
- },
- Strikethrough: ansi.StylePrimitive{
- CrossedOut: boolPtr(true),
- },
- Emph: ansi.StylePrimitive{
- Italic: boolPtr(true),
- },
- Strong: ansi.StylePrimitive{
- Bold: boolPtr(true),
- },
- HorizontalRule: ansi.StylePrimitive{
- Color: stringPtr("240"),
- Format: "\n--------\n",
- },
- Item: ansi.StylePrimitive{
- BlockPrefix: "• ",
- },
- Enumeration: ansi.StylePrimitive{
- BlockPrefix: ". ",
- },
- Task: ansi.StyleTask{
- StylePrimitive: ansi.StylePrimitive{},
- Ticked: "[✓] ",
- Unticked: "[ ] ",
- },
- Link: ansi.StylePrimitive{Color: stringPtr("#7d0e09")},
- LinkText: ansi.StylePrimitive{Bold: boolPtr(true)},
- Image: ansi.StylePrimitive{
- Color: stringPtr("245"),
- Underline: boolPtr(true),
- },
- ImageText: ansi.StylePrimitive{
- Color: stringPtr("88"),
- Format: "Image: {{.text}} →",
- },
- Code: ansi.StyleBlock{
- StylePrimitive: ansi.StylePrimitive{
- Color: stringPtr("1"),
- Italic: boolPtr(true),
- },
- },
- CodeBlock: ansi.StyleCodeBlock{
- StyleBlock: ansi.StyleBlock{
- StylePrimitive: ansi.StylePrimitive{
- Color: stringPtr("244"),
- },
- Margin: uintPtr(2),
- },
- Chroma: &ansi.Chroma{
- Text: ansi.StylePrimitive{
- Color: stringPtr("#C4C4C4"),
- },
- Error: ansi.StylePrimitive{
- Color: stringPtr("#F1F1F1"),
- BackgroundColor: stringPtr("#F05B5B"),
- },
- Comment: ansi.StylePrimitive{
- Color: stringPtr("#676767"),
- },
- CommentPreproc: ansi.StylePrimitive{
- Color: stringPtr("#FF875F"),
- },
- Keyword: ansi.StylePrimitive{
- Color: stringPtr("#00AAFF"),
- },
- KeywordReserved: ansi.StylePrimitive{
- Color: stringPtr("#FF5FD2"),
- },
- KeywordNamespace: ansi.StylePrimitive{
- Color: stringPtr("#FF5F87"),
- },
- KeywordType: ansi.StylePrimitive{
- Color: stringPtr("#6E6ED8"),
- },
- Operator: ansi.StylePrimitive{
- Color: stringPtr("#EF8080"),
- },
- Punctuation: ansi.StylePrimitive{
- Color: stringPtr("#E8E8A8"),
- },
- Name: ansi.StylePrimitive{
- Color: stringPtr("#C4C4C4"),
- },
- NameBuiltin: ansi.StylePrimitive{
- Color: stringPtr("#FF8EC7"),
- },
- NameTag: ansi.StylePrimitive{
- Color: stringPtr("#B083EA"),
- },
- NameAttribute: ansi.StylePrimitive{
- Color: stringPtr("#7A7AE6"),
- },
- NameClass: ansi.StylePrimitive{
- Color: stringPtr("#F1F1F1"),
- Underline: boolPtr(true),
- Bold: boolPtr(true),
- },
- NameDecorator: ansi.StylePrimitive{
- Color: stringPtr("#FFFF87"),
- },
- NameFunction: ansi.StylePrimitive{
- Color: stringPtr("#00D787"),
- },
- LiteralNumber: ansi.StylePrimitive{
- Color: stringPtr("#6EEFC0"),
- },
- LiteralString: ansi.StylePrimitive{
- Color: stringPtr("#C69669"),
- },
- LiteralStringEscape: ansi.StylePrimitive{
- Color: stringPtr("#AFFFD7"),
- },
- GenericDeleted: ansi.StylePrimitive{
- Color: stringPtr("#FD5B5B"),
- },
- GenericEmph: ansi.StylePrimitive{
- Italic: boolPtr(true),
- },
- GenericInserted: ansi.StylePrimitive{
- Color: stringPtr("#00D787"),
- },
- GenericStrong: ansi.StylePrimitive{
- Bold: boolPtr(true),
- },
- GenericSubheading: ansi.StylePrimitive{
- Color: stringPtr("#777777"),
- },
- Background: ansi.StylePrimitive{
- BackgroundColor: stringPtr("#373737"),
- },
- },
- },
- Table: ansi.StyleTable{
- StyleBlock: ansi.StyleBlock{
- StylePrimitive: ansi.StylePrimitive{},
- },
- CenterSeparator: stringPtr("┼"),
- ColumnSeparator: stringPtr("│"),
- RowSeparator: stringPtr("─"),
- },
- DefinitionDescription: ansi.StylePrimitive{
- BlockPrefix: "\n🠶 ",
- },
-}
-
-func boolPtr(b bool) *bool { return &b }
-func stringPtr(s string) *string { return &s }
-func uintPtr(u uint) *uint { return &u }
diff --git a/cmds.go b/cmds.go
@@ -5,27 +5,25 @@ import (
"runtime"
"time"
+ "git.figbert.com/caret/api"
"git.figbert.com/caret/article"
+ "git.figbert.com/caret/utils"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbletea"
)
-type genericError struct{}
-type postHasNoLinkToOpen struct{}
+type returnedFromReading struct{}
type platformUnsupported struct{}
type msgExpired struct{}
type loading struct{}
func attemptToOpenPostURL(itm list.Item) tea.Cmd {
return func() tea.Msg {
- i, ok := itm.(item)
- if !ok {
- return genericError{}
- }
+ i := itm.(item)
if i.post.URL == "" {
- return postHasNoLinkToOpen{}
+ return utils.NoExternalURL{}
}
success := openURL(i.post.URL)
@@ -39,10 +37,7 @@ func attemptToOpenPostURL(itm list.Item) tea.Cmd {
func attemptToOpenPostComments(itm list.Item) tea.Cmd {
return func() tea.Msg {
- i, ok := itm.(item)
- if !ok {
- return genericError{}
- }
+ i := itm.(item)
success := openURL(i.post.CommentsURL)
if !success {
@@ -79,17 +74,16 @@ func startLoading() tea.Msg {
return loading{}
}
-func displayLinkedArticle(itm list.Item) tea.Cmd {
- return func() tea.Msg {
- i, ok := itm.(item)
- if !ok {
- return genericError{}
- }
+func displayLinkedArticle(i list.Item) tea.Cmd {
+ return article.LoadLinkedArticle(i.(item).post.URL)
+}
- if i.post.URL == "" {
- return postHasNoLinkToOpen{}
- }
+func fetchComments(i list.Item) tea.Cmd {
+ return api.FetchPost(i.(item).post.ShortID)
+}
- return article.Start(i.post.URL)
- }
+func run(cmd *exec.Cmd) tea.Cmd {
+ return tea.ExecProcess(cmd, func(_ error) tea.Msg {
+ return returnedFromReading{}
+ })
}
diff --git a/comments/comments.go b/comments/comments.go
@@ -0,0 +1,61 @@
+package comments
+
+import (
+ "fmt"
+ "os/exec"
+ "strings"
+
+ "git.figbert.com/caret/api"
+ "git.figbert.com/caret/ui"
+ "git.figbert.com/caret/utils"
+
+ "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/glamour"
+ gloss "github.com/charmbracelet/lipgloss"
+
+ "github.com/dustin/go-humanize"
+)
+
+func Load(post api.Post) tea.Cmd {
+ return func() tea.Msg {
+ renderer, err := glamour.NewTermRenderer(glamour.WithStyles(ui.GlamourConfig))
+ if err != nil {
+ return utils.Error{}
+ }
+
+ var page strings.Builder
+ page.WriteString(header(&post))
+
+ for _, comment := range post.Comments {
+ cmnt, err := renderer.Render(comment.CommentPlain)
+ if err != nil {
+ return utils.Error{}
+ }
+ cmnt = strings.TrimSpace(cmnt)
+
+ headline := fmt.Sprintf(
+ " %s %s",
+ ui.Bold().Render(comment.User.Username),
+ ui.SecondaryText().Render(humanize.Time(
+ utils.TimeFromAPI(comment.CreatedAt),
+ )),
+ )
+
+ composite := fmt.Sprintf("%s\n%s", headline, cmnt)
+ margin := gloss.NewStyle().MarginLeft(2 * comment.Depth)
+ page.WriteString("\n\n" + margin.Render(composite))
+ }
+
+ command := exec.Command("less",
+ "--RAW-CONTROL-CHARS",
+ "--ignore-case",
+ "--tilde",
+ "--use-color",
+ "-P?e"+ui.Prompt().Render("End"),
+ "-DSy",
+ "-DP-")
+ command.Stdin = strings.NewReader(page.String())
+
+ return utils.RunCommand(command)
+ }
+}
diff --git a/comments/header.go b/comments/header.go
@@ -0,0 +1,53 @@
+package comments
+
+import (
+ "fmt"
+ "strings"
+
+ "git.figbert.com/caret/api"
+ "git.figbert.com/caret/ui"
+ "git.figbert.com/caret/utils"
+
+ gloss "github.com/charmbracelet/lipgloss"
+
+ "github.com/dustin/go-humanize"
+)
+
+func header(post *api.Post) string {
+ blockWidth := 72
+ textWidth := blockWidth - 2
+
+ box := gloss.NewStyle().
+ BorderStyle(gloss.RoundedBorder()).
+ PaddingLeft(1).
+ PaddingRight(1).
+ MarginLeft(1).
+ MarginTop(1).
+ Width(blockWidth)
+
+ url := post.URL
+ urlRunes := []rune(url)
+ if len(urlRunes) > textWidth {
+ url = string(append(urlRunes[:textWidth-1], '…'))
+ }
+
+ posted := utils.TimeFromAPI(post.CreatedAt)
+
+ user := fmt.Sprintf("by %s %s", ui.Title().Render(post.User.Username), ui.SecondaryText().Render(humanize.Time(posted)))
+ id := fmt.Sprintf("ID %s", gloss.NewStyle().Foreground(gloss.Color("2")).Render(post.ShortID))
+ cmnts := fmt.Sprintf("%s comments", gloss.NewStyle().Foreground(gloss.Color("5")).Render(fmt.Sprintf("%d", post.CommentCount)))
+ karma := fmt.Sprintf("%s karma", ui.TagText().Render(fmt.Sprintf("%d", post.Score)))
+
+ topSpacer := strings.Repeat(" ", textWidth-gloss.Width(user)-gloss.Width(id))
+ bottomSpacer := strings.Repeat(" ", textWidth-gloss.Width(cmnts)-gloss.Width(karma))
+
+ return box.Render(
+ fmt.Sprintf(
+ "%s\n%s\n\n%s%s%s\n%s%s%s",
+ post.Headline,
+ gloss.NewStyle().Foreground(gloss.Color("4")).Render(url),
+ user, topSpacer, id,
+ cmnts, bottomSpacer, karma,
+ ),
+ )
+}
diff --git a/item.go b/item.go
@@ -4,10 +4,10 @@ import (
"fmt"
"net/url"
"strings"
- "time"
"git.figbert.com/caret/api"
"git.figbert.com/caret/ui"
+ "git.figbert.com/caret/utils"
"github.com/dustin/go-humanize"
)
@@ -27,7 +27,7 @@ func (i item) Title() string {
return fmt.Sprintf("%s %s", i.post.Headline, displayURL)
}
func (itm item) Description() string {
- created, _ := time.Parse("2006-01-02T15:04:05.999-07:00", itm.post.CreatedAt)
+ created := utils.TimeFromAPI(itm.post.CreatedAt)
intro := ui.SecondaryText().Render(
fmt.Sprintf(
diff --git a/main.go b/main.go
@@ -4,8 +4,9 @@ import (
"strings"
"git.figbert.com/caret/api"
- "git.figbert.com/caret/article"
+ "git.figbert.com/caret/comments"
"git.figbert.com/caret/ui"
+ "git.figbert.com/caret/utils"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbletea"
@@ -41,6 +42,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, attemptToOpenPostComments(m.posts.SelectedItem()))
case " ":
cmds = append(cmds, displayLinkedArticle(m.posts.SelectedItem()), startLoading)
+ case "enter":
+ cmds = append(cmds, fetchComments(m.posts.SelectedItem()), startLoading)
case "j":
if m.posts.Index() == len(m.posts.Items())-1 {
m.page++
@@ -57,25 +60,25 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
for _, pst := range []api.Post(msg) {
m.posts.SetItems(append(m.posts.Items(), item{post: pst}))
}
+ case api.PostMsg:
+ cmds = append(cmds, comments.Load(api.Post(msg)))
case api.ErrorMsg:
m.msg = "Failed to fetch new stories."
cmds = append(cmds, clearMsg)
case loading:
m.msg = "Loading…"
- case genericError, article.Error:
+ case utils.Error:
m.msg = "Error! How perplexing."
cmds = append(cmds, clearMsg)
- case postHasNoLinkToOpen:
+ case utils.NoExternalURL:
m.msg = "This post does not have an external URL to open."
cmds = append(cmds, clearMsg)
case platformUnsupported:
m.msg = "Exotic! Opening URLs on your OS is not yet supported. Reach out!"
cmds = append(cmds, clearMsg)
- case article.Start:
- cmds = append(cmds, article.LoadLinkedArticle(string(msg)))
- case article.RunCommand:
- cmds = append(cmds, article.DisplayLinkedArticle(msg))
- case article.ReturnedFromReading, msgExpired:
+ case utils.RunCommand:
+ cmds = append(cmds, run(msg))
+ case returnedFromReading, msgExpired:
m.msg = ""
}
diff --git a/ui/glamour.go b/ui/glamour.go
@@ -0,0 +1,214 @@
+package ui
+
+import (
+ "github.com/charmbracelet/glamour/ansi"
+)
+
+var GlamourConfig = ansi.StyleConfig{
+ Document: ansi.StyleBlock{
+ StylePrimitive: ansi.StylePrimitive{
+ BlockPrefix: "\n",
+ BlockSuffix: "\n",
+ Color: stringPtr("252"),
+ },
+ Margin: uintPtr(2),
+ },
+ BlockQuote: ansi.StyleBlock{
+ StylePrimitive: ansi.StylePrimitive{Color: stringPtr("244")},
+ Indent: uintPtr(1),
+ IndentToken: stringPtr("│ "),
+ },
+ List: ansi.StyleList{
+ LevelIndent: 2,
+ },
+ Heading: ansi.StyleBlock{
+ StylePrimitive: ansi.StylePrimitive{
+ BlockSuffix: "\n",
+ Color: stringPtr("39"),
+ Bold: boolPtr(true),
+ },
+ },
+ H1: ansi.StyleBlock{
+ StylePrimitive: ansi.StylePrimitive{
+ Prefix: " ",
+ Suffix: " ",
+ Color: stringPtr("228"),
+ BackgroundColor: stringPtr("63"),
+ Bold: boolPtr(true),
+ },
+ },
+ H2: ansi.StyleBlock{
+ StylePrimitive: ansi.StylePrimitive{
+ Color: stringPtr("252"),
+ },
+ },
+ H3: ansi.StyleBlock{
+ StylePrimitive: ansi.StylePrimitive{
+ Underline: boolPtr(true),
+ },
+ },
+ H4: ansi.StyleBlock{
+ StylePrimitive: ansi.StylePrimitive{
+ Color: stringPtr("220"),
+ Underline: boolPtr(true),
+ },
+ },
+ H5: ansi.StyleBlock{
+ StylePrimitive: ansi.StylePrimitive{
+ Prefix: "##### ",
+ },
+ },
+ H6: ansi.StyleBlock{
+ StylePrimitive: ansi.StylePrimitive{
+ Prefix: "###### ",
+ Color: stringPtr("35"),
+ Bold: boolPtr(false),
+ },
+ },
+ Strikethrough: ansi.StylePrimitive{
+ CrossedOut: boolPtr(true),
+ },
+ Emph: ansi.StylePrimitive{
+ Italic: boolPtr(true),
+ },
+ Strong: ansi.StylePrimitive{
+ Bold: boolPtr(true),
+ },
+ HorizontalRule: ansi.StylePrimitive{
+ Color: stringPtr("240"),
+ Format: "\n--------\n",
+ },
+ Item: ansi.StylePrimitive{
+ BlockPrefix: "• ",
+ },
+ Enumeration: ansi.StylePrimitive{
+ BlockPrefix: ". ",
+ },
+ Task: ansi.StyleTask{
+ StylePrimitive: ansi.StylePrimitive{},
+ Ticked: "[✓] ",
+ Unticked: "[ ] ",
+ },
+ Link: ansi.StylePrimitive{Color: stringPtr("#7d0e09")},
+ LinkText: ansi.StylePrimitive{Bold: boolPtr(true)},
+ Image: ansi.StylePrimitive{
+ Color: stringPtr("245"),
+ Underline: boolPtr(true),
+ },
+ ImageText: ansi.StylePrimitive{
+ Color: stringPtr("88"),
+ Format: "Image: {{.text}} →",
+ },
+ Code: ansi.StyleBlock{
+ StylePrimitive: ansi.StylePrimitive{
+ Color: stringPtr("1"),
+ Italic: boolPtr(true),
+ },
+ },
+ CodeBlock: ansi.StyleCodeBlock{
+ StyleBlock: ansi.StyleBlock{
+ StylePrimitive: ansi.StylePrimitive{
+ Color: stringPtr("244"),
+ },
+ Margin: uintPtr(2),
+ },
+ Chroma: &ansi.Chroma{
+ Text: ansi.StylePrimitive{
+ Color: stringPtr("#C4C4C4"),
+ },
+ Error: ansi.StylePrimitive{
+ Color: stringPtr("#F1F1F1"),
+ BackgroundColor: stringPtr("#F05B5B"),
+ },
+ Comment: ansi.StylePrimitive{
+ Color: stringPtr("#676767"),
+ },
+ CommentPreproc: ansi.StylePrimitive{
+ Color: stringPtr("#FF875F"),
+ },
+ Keyword: ansi.StylePrimitive{
+ Color: stringPtr("#00AAFF"),
+ },
+ KeywordReserved: ansi.StylePrimitive{
+ Color: stringPtr("#FF5FD2"),
+ },
+ KeywordNamespace: ansi.StylePrimitive{
+ Color: stringPtr("#FF5F87"),
+ },
+ KeywordType: ansi.StylePrimitive{
+ Color: stringPtr("#6E6ED8"),
+ },
+ Operator: ansi.StylePrimitive{
+ Color: stringPtr("#EF8080"),
+ },
+ Punctuation: ansi.StylePrimitive{
+ Color: stringPtr("#E8E8A8"),
+ },
+ Name: ansi.StylePrimitive{
+ Color: stringPtr("#C4C4C4"),
+ },
+ NameBuiltin: ansi.StylePrimitive{
+ Color: stringPtr("#FF8EC7"),
+ },
+ NameTag: ansi.StylePrimitive{
+ Color: stringPtr("#B083EA"),
+ },
+ NameAttribute: ansi.StylePrimitive{
+ Color: stringPtr("#7A7AE6"),
+ },
+ NameClass: ansi.StylePrimitive{
+ Color: stringPtr("#F1F1F1"),
+ Underline: boolPtr(true),
+ Bold: boolPtr(true),
+ },
+ NameDecorator: ansi.StylePrimitive{
+ Color: stringPtr("#FFFF87"),
+ },
+ NameFunction: ansi.StylePrimitive{
+ Color: stringPtr("#00D787"),
+ },
+ LiteralNumber: ansi.StylePrimitive{
+ Color: stringPtr("#6EEFC0"),
+ },
+ LiteralString: ansi.StylePrimitive{
+ Color: stringPtr("#C69669"),
+ },
+ LiteralStringEscape: ansi.StylePrimitive{
+ Color: stringPtr("#AFFFD7"),
+ },
+ GenericDeleted: ansi.StylePrimitive{
+ Color: stringPtr("#FD5B5B"),
+ },
+ GenericEmph: ansi.StylePrimitive{
+ Italic: boolPtr(true),
+ },
+ GenericInserted: ansi.StylePrimitive{
+ Color: stringPtr("#00D787"),
+ },
+ GenericStrong: ansi.StylePrimitive{
+ Bold: boolPtr(true),
+ },
+ GenericSubheading: ansi.StylePrimitive{
+ Color: stringPtr("#777777"),
+ },
+ Background: ansi.StylePrimitive{
+ BackgroundColor: stringPtr("#373737"),
+ },
+ },
+ },
+ Table: ansi.StyleTable{
+ StyleBlock: ansi.StyleBlock{
+ StylePrimitive: ansi.StylePrimitive{},
+ },
+ CenterSeparator: stringPtr("┼"),
+ ColumnSeparator: stringPtr("│"),
+ RowSeparator: stringPtr("─"),
+ },
+ DefinitionDescription: ansi.StylePrimitive{
+ BlockPrefix: "\n🠶 ",
+ },
+}
+
+func boolPtr(b bool) *bool { return &b }
+func stringPtr(s string) *string { return &s }
+func uintPtr(u uint) *uint { return &u }
diff --git a/ui/styles.go b/ui/styles.go
@@ -14,6 +14,10 @@ func TagText() gloss.Style {
return gloss.NewStyle().Foreground(Orange())
}
+func Bold() gloss.Style {
+ return gloss.NewStyle().Bold(true)
+}
+
func LobstersRed() gloss.Color {
return gloss.Color("#AC130D")
}
@@ -34,3 +38,7 @@ func HR(width int) string {
Foreground(Gray()).
Render()
}
+
+func Prompt() gloss.Style {
+ return gloss.NewStyle().Foreground(LobstersRed()).Background(gloss.Color("7")).Padding(0, 1)
+}
diff --git a/utils/api.go b/utils/api.go
@@ -0,0 +1,8 @@
+package utils
+
+import "time"
+
+func TimeFromAPI(str string) time.Time {
+ created, _ := time.Parse("2006-01-02T15:04:05.999-07:00", str)
+ return created
+}
diff --git a/utils/tea.go b/utils/tea.go
@@ -0,0 +1,7 @@
+package utils
+
+import "os/exec"
+
+type Error struct{}
+type RunCommand *exec.Cmd
+type NoExternalURL struct{}