forked from varia/go-sh-manymanuals
wip: showing pdf viewport
This commit is contained in:
parent
e347b4924c
commit
fd88becf55
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/datasheets/
|
/datasheets/
|
||||||
|
debug.log
|
||||||
gshmm
|
gshmm
|
||||||
|
@ -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
3
go.mod
@ -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
2
go.sum
@ -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
120
gshmm.go
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user