caret

[ACTIVE] a command line tool for browsing Lobsters in your terminal
git clone git://git.figbert.com/caret.git
Log | Files | Refs | README | LICENSE

article.go (1995B)


      1 package article
      2 
      3 import (
      4 	"fmt"
      5 	"os/exec"
      6 	"strings"
      7 	"time"
      8 
      9 	"git.figbert.com/caret/ui"
     10 	"git.figbert.com/caret/utils"
     11 
     12 	"github.com/charmbracelet/bubbletea"
     13 	"github.com/charmbracelet/glamour"
     14 	gloss "github.com/charmbracelet/lipgloss"
     15 
     16 	md "github.com/JohannesKaufmann/html-to-markdown"
     17 	"github.com/JohannesKaufmann/html-to-markdown/plugin"
     18 	"github.com/PuerkitoBio/goquery"
     19 	"github.com/go-shiori/go-readability"
     20 )
     21 
     22 func LoadLinkedArticle(url string) tea.Cmd {
     23 	return func() tea.Msg {
     24 		if url == "" {
     25 			return utils.NoExternalURL{}
     26 		}
     27 
     28 		title, article, err := getArticle(url)
     29 		if err != nil {
     30 			return utils.Error{}
     31 		}
     32 
     33 		header := getReaderModeMetaBlock(title, url)
     34 
     35 		renderer, err := glamour.NewTermRenderer(glamour.WithStyles(ui.GlamourConfig))
     36 		if err != nil {
     37 			return utils.Error{}
     38 		}
     39 
     40 		markdown, err := renderer.Render(article)
     41 		if err != nil {
     42 			return utils.Error{}
     43 		}
     44 		markdown = gloss.NewStyle().MarginLeft(2).Render(markdown)
     45 
     46 		renderedArticle := header + markdown
     47 
     48 		command := exec.Command("less",
     49 			"--RAW-CONTROL-CHARS",
     50 			"--ignore-case",
     51 			"--tilde",
     52 			"--use-color",
     53 			"-P?e"+ui.Prompt().Render("End"),
     54 			"-DSy",
     55 			"-DP-")
     56 		command.Stdin = strings.NewReader(renderedArticle)
     57 
     58 		return utils.RunCommand(command)
     59 	}
     60 }
     61 
     62 func getArticle(url string) (string, string, error) {
     63 	article, err := readability.FromURL(url, time.Second*5)
     64 	if err != nil {
     65 		return "", "", err
     66 	}
     67 
     68 	href := md.Rule{
     69 		Filter: []string{"a"},
     70 		Replacement: func(content string, selec *goquery.Selection, _ *md.Options) *string {
     71 			link, ok := selec.Attr("href")
     72 			if !ok {
     73 				return md.String(strings.TrimSpace(content))
     74 			}
     75 
     76 			return md.String(fmt.Sprintf("[%s](%s)", strings.TrimSpace(content), link))
     77 		},
     78 	}
     79 
     80 	converter := md.NewConverter("", true, &md.Options{})
     81 	converter.AddRules(href)
     82 	converter.Use(plugin.Table())
     83 
     84 	markdown, err := converter.ConvertString(article.Content)
     85 	if err != nil {
     86 		return "", "", err
     87 	}
     88 
     89 	return article.Title, markdown, nil
     90 }