add line number support and support for text files

This commit is contained in:
2025-10-15 21:42:07 +02:00
parent ff9035f85f
commit 9c200de774
5 changed files with 66 additions and 19 deletions

View File

@@ -17,7 +17,17 @@ import (
const ( const (
Version string = `v0.0.2` Version string = `v0.0.2`
Usage string = `epuppy [-vd] <epub file>` Usage string = `Usage epuppy [options] <epub file>
Options:
-D --dark enable dark mode
-s --store-progress store and use previous (experimental)
-n --line-numbers add line numbers
-c --config <file> use config <file>
-d --debug enable debugging
-h --help show help message
-v --version show program version
`
) )
type Config struct { type Config struct {
@@ -25,6 +35,7 @@ type Config struct {
Debug bool `koanf:"debug"` // -d Debug bool `koanf:"debug"` // -d
StoreProgress bool `koanf:"store-progress"` // -s StoreProgress bool `koanf:"store-progress"` // -s
Darkmode bool `koanf:"dark"` // -D Darkmode bool `koanf:"dark"` // -D
LineNumbers bool `koanf:"line-numbers"` // -n
Config string `koanf:"config"` // -c Config string `koanf:"config"` // -c
ColorDark ColorSetting `koanf:"colordark"` // comes from config file only ColorDark ColorSetting `koanf:"colordark"` // comes from config file only
ColorLight ColorSetting `koanf:"colorlight"` // comes from config file only ColorLight ColorSetting `koanf:"colorlight"` // comes from config file only
@@ -52,6 +63,7 @@ func InitConfig(output io.Writer) (*Config, error) {
flagset.BoolP("debug", "d", false, "enable debugging") flagset.BoolP("debug", "d", false, "enable debugging")
flagset.BoolP("dark", "D", false, "enable dark mode") flagset.BoolP("dark", "D", false, "enable dark mode")
flagset.BoolP("store-progress", "s", false, "store reading progress") flagset.BoolP("store-progress", "s", false, "store reading progress")
flagset.BoolP("line-numbers", "n", false, "add line numbers")
flagset.StringP("config", "c", "", "read config from file") flagset.StringP("config", "c", "", "read config from file")
if err := flagset.Parse(os.Args[1:]); err != nil { if err := flagset.Parse(os.Args[1:]); err != nil {

View File

@@ -189,7 +189,7 @@ func (m Doc) View() string {
// update current line for later saving // update current line for later saving
// FIXME: doesn't work correctly yet // FIXME: doesn't work correctly yet
m.meta.currentline = int(float64(m.meta.lines) * m.viewport.ScrollPercent()) m.meta.currentline = int(float64(m.viewport.TotalLineCount()) * m.viewport.ScrollPercent())
var helpView string var helpView string
if m.help.ShowAll { if m.help.ShowAll {
@@ -234,13 +234,28 @@ func Pager(conf *Config, title, message string) (int, error) {
scrollto = conf.InitialProgress scrollto = conf.InitialProgress
} }
if conf.LineNumbers {
catn := ""
for idx, line := range strings.Split(message, "\n") {
catn += fmt.Sprintf("%d: %s\n", idx, line)
}
message = catn
}
meta := Meta{ meta := Meta{
initialprogress: scrollto, initialprogress: scrollto,
lines: len(strings.Split(message, "\r\n")), lines: len(strings.Split(message, "\n")),
} }
p := tea.NewProgram( p := tea.NewProgram(
Doc{content: message, title: title, initialwidth: width, meta: &meta, config: conf, keys: keys}, Doc{
content: message,
title: title,
initialwidth: width,
meta: &meta,
config: conf,
keys: keys,
},
tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer" tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer"
tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel
) )

View File

@@ -2,12 +2,32 @@ package cmd
import ( import (
"fmt" "fmt"
"os"
"path/filepath"
"strings" "strings"
"github.com/tlinden/epuppy/pkg/epub" "github.com/tlinden/epuppy/pkg/epub"
) )
func View(conf *Config) (int, error) { func View(conf *Config) (int, error) {
switch filepath.Ext(conf.Document) {
case ".epub":
return ViewEpub(conf)
default:
return ViewText(conf)
}
}
func ViewText(conf *Config) (int, error) {
data, err := os.ReadFile(conf.Document)
if err != nil {
return 0, err
}
return Pager(conf, conf.Document, string(data))
}
func ViewEpub(conf *Config) (int, error) {
book, err := epub.Open(conf.Document) book, err := epub.Open(conf.Document)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -35,8 +55,11 @@ func View(conf *Config) (int, error) {
for _, content := range book.Content { for _, content := range book.Content {
if len(content.Body) > 0 { if len(content.Body) > 0 {
buf.WriteString(conf.Colors.Chapter. if content.Title != "" {
Render(fmt.Sprintf("Chapter %d: %s", chapter, content.Title))) buf.WriteString(conf.Colors.Chapter.
Render(fmt.Sprintf("──────┤ %s ├──────", content.Title)))
}
buf.WriteString("\r\n\r\n") buf.WriteString("\r\n\r\n")
buf.WriteString(conf.Colors.Body.Render(content.Body)) buf.WriteString(conf.Colors.Body.Render(content.Body))
buf.WriteString("\r\n\r\n\r\n\r\n") buf.WriteString("\r\n\r\n\r\n\r\n")

View File

@@ -3,9 +3,19 @@ package epub
import ( import (
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"regexp"
"strings" "strings"
) )
var (
cleantitle = regexp.MustCompile(`(?s)<head>.*</head>`)
cleanmarkup = regexp.MustCompile(`<[^<>]+>`)
cleanentities = regexp.MustCompile(`&.+;`)
cleancomments = regexp.MustCompile(`/*.*/`)
cleanspace = regexp.MustCompile(`^\s*`)
cleanh1 = regexp.MustCompile(`<h[1-6].*</h[1-6]>`)
)
// Content nav-point content // Content nav-point content
type Content struct { type Content struct {
Src string `xml:"src,attr" json:"src"` Src string `xml:"src,attr" json:"src"`

View File

@@ -1,18 +1,5 @@
package epub package epub
import (
"regexp"
)
var (
cleantitle = regexp.MustCompile(`(?s)<head>.*</head>`)
cleanmarkup = regexp.MustCompile(`<[^<>]+>`)
cleanentities = regexp.MustCompile(`&.+;`)
cleancomments = regexp.MustCompile(`/*.*/`)
cleanspace = regexp.MustCompile(`^\s*`)
cleanh1 = regexp.MustCompile(`<h[1-6].*</h[1-6]>`)
)
// Ncx OPS/toc.ncx // Ncx OPS/toc.ncx
type Ncx struct { type Ncx struct {
Points []*NavPoint `xml:"navMap>navPoint" json:"points"` Points []*NavPoint `xml:"navMap>navPoint" json:"points"`