wip: showing pdf viewport

This commit is contained in:
decentral1se 2023-05-10 13:07:34 +02:00
parent e347b4924c
commit fd88becf55
No known key found for this signature in database
GPG Key ID: 03789458B3D0C410
5 changed files with 111 additions and 24 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/datasheets/ /datasheets/
debug.log
gshmm gshmm

View File

@ -19,5 +19,6 @@ manuals and datasheets for everything in [Varia](https://varia.zone).
``` ```
mkdir -p datasheets # fill with https://vvvvvvaria.org/~crunk/datasheets.zip mkdir -p datasheets # fill with https://vvvvvvaria.org/~crunk/datasheets.zip
tail -f debug.log # in another terminal
go run goshmm.go go run goshmm.go
``` ```

3
go.mod
View File

@ -5,13 +5,14 @@ go 1.18
require ( require (
github.com/charmbracelet/bubbles v0.15.0 github.com/charmbracelet/bubbles v0.15.0
github.com/charmbracelet/bubbletea v0.23.1 github.com/charmbracelet/bubbletea v0.23.1
github.com/charmbracelet/lipgloss v0.6.0
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80
github.com/sahilm/fuzzy v0.1.0 github.com/sahilm/fuzzy v0.1.0
) )
require ( require (
github.com/atotto/clipboard v0.1.4 // indirect github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52 v1.0.3 // indirect github.com/aymanbagabas/go-osc52 v1.0.3 // indirect
github.com/charmbracelet/lipgloss v0.6.0 // indirect
github.com/containerd/console v1.0.3 // indirect github.com/containerd/console v1.0.3 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect github.com/mattn/go-isatty v0.0.16 // indirect

2
go.sum
View File

@ -13,6 +13,8 @@ github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARu
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=

120
gshmm.go
View File

@ -1,14 +1,20 @@
package main package main
import ( import (
"bytes"
"errors"
"flag" "flag"
"fmt" "fmt"
"log"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/textinput"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/ledongthuc/pdf"
"github.com/sahilm/fuzzy" "github.com/sahilm/fuzzy"
) )
@ -35,18 +41,60 @@ func handleCliFlags() {
flag.Parse() flag.Parse()
} }
func readPDF(name string) (string, error) {
file, reader, err := pdf.Open(name)
if err != nil {
return "", errors.Unwrap(err)
}
defer func() {
if e := file.Close(); e != nil {
err = e
}
}()
buf := new(bytes.Buffer)
buffer, err := reader.GetPlainText()
if err != nil {
return "", errors.Unwrap(err)
}
_, err = buf.ReadFrom(buffer)
if err != nil {
return "", errors.Unwrap(err)
}
return buf.String(), nil
}
type model struct { type model struct {
filterInput textinput.Model // fuzzy search interface filterInput textinput.Model // fuzzy search interface
datasheets []string // all datasheets under cwd datasheets []datasheet // all datasheets under cwd
dataSheetsView []string // filtered view on all datasheets dataSheetsView []string // filtered view on all datasheets
dataSheetViewport viewport.Model
}
func (m model) dataSheetNames() []string {
var names []string
for _, datasheet := range m.datasheets {
names = append(names, datasheet.filename)
}
return names
}
type datasheet struct {
filename string
absPath string
contents string
} }
func initialModel() model { func initialModel() model {
input := textinput.New() input := textinput.New()
input.Focus() input.Focus()
var ds []string var ds []datasheet
// TODO(d1): handle error in interface? // TODO: handle error in interface?
_ = filepath.Walk(".", func(path string, info os.FileInfo, err error) error { _ = filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
@ -54,35 +102,50 @@ func initialModel() model {
name := info.Name() name := info.Name()
if strings.HasSuffix(name, "pdf") { if strings.HasSuffix(name, "pdf") {
ds = append(ds, name) // TODO: handle error in interface?
contents, _ := readPDF(path)
d := datasheet{
filename: name,
absPath: path,
contents: contents,
}
ds = append(ds, d)
log.Print(d)
} }
return nil return nil
}) })
return model{ viewp := viewport.New(60, 30)
viewp.SetContent(ds[len(ds)-1].contents)
m := model{
filterInput: input, filterInput: input,
datasheets: ds, datasheets: ds,
dataSheetsView: ds, dataSheetViewport: viewp,
} }
// TODO: which index is the datasheet closest to the filter input?
m.dataSheetsView = m.dataSheetNames()
return m
} }
func (m model) Init() tea.Cmd { func (m model) Init() tea.Cmd {
// TODO(d1): implement reading all PDFs in cwd
// https://stackoverflow.com/a/67214584
// requires error reporting also
return textinput.Blink return textinput.Blink
} }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd var (
cmd tea.Cmd
cmds []tea.Cmd
)
if m.filterInput.Focused() { if m.filterInput.Focused() {
var matched []string var matched []string
search := m.filterInput.Value() search := m.filterInput.Value()
if len(search) >= minCharsUntilFilter { if len(search) >= minCharsUntilFilter {
matches := fuzzy.Find(search, m.datasheets) matches := fuzzy.Find(search, m.dataSheetNames())
for _, match := range matches { for _, match := range matches {
matched = append(matched, match.Str) matched = append(matched, match.Str)
} }
@ -90,12 +153,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if len(matches) > 0 { if len(matches) > 0 {
m.dataSheetsView = matched m.dataSheetsView = matched
} else { } else {
m.dataSheetsView = m.datasheets m.dataSheetsView = m.dataSheetNames()
} }
} else { } else {
m.dataSheetsView = m.datasheets m.dataSheetsView = m.dataSheetNames()
} }
} }
switch msg := msg.(type) { switch msg := msg.(type) {
@ -107,17 +169,25 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} }
m.filterInput, cmd = m.filterInput.Update(msg) m.filterInput, cmd = m.filterInput.Update(msg)
cmds = append(cmds, cmd)
return m, cmd // TODO figure out how update viewport when filtering
// the last item in m.dataSheetsView should be shown
m.dataSheetViewport, cmd = m.dataSheetViewport.Update(msg)
cmds = append(cmds, cmd)
return m, tea.Batch(cmds...)
} }
func (m model) View() string { func (m model) View() string {
body := strings.Builder{} body := strings.Builder{}
// TODO(d1): paginate / trim view to last 10 or something? // TODO: paginate / trim view to last 10 or something?
body.WriteString(strings.Join(m.dataSheetsView, "\n") + "\n") sheets := strings.Join(m.dataSheetsView, "\n")
panes := lipgloss.JoinHorizontal(lipgloss.Left, sheets, m.dataSheetViewport.View())
body.WriteString(panes)
body.WriteString(m.filterInput.View() + "\n") body.WriteString("\n" + m.filterInput.View())
return body.String() return body.String()
} }
@ -130,7 +200,19 @@ func main() {
os.Exit(0) os.Exit(0)
} }
p := tea.NewProgram(initialModel(), tea.WithAltScreen()) f, err := tea.LogToFile("debug.log", "debug")
if err != nil {
fmt.Println("fatal:", err)
os.Exit(1)
}
defer f.Close()
p := tea.NewProgram(
initialModel(),
tea.WithAltScreen(),
tea.WithMouseCellMotion(),
)
if err := p.Start(); err != nil { if err := p.Start(); err != nil {
fmt.Printf("oops, something went wrong: %v", err) fmt.Printf("oops, something went wrong: %v", err)
os.Exit(1) os.Exit(1)