From fd88becf55883fdf48509df3522b96e62b5b96d1 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Wed, 10 May 2023 13:07:34 +0200 Subject: [PATCH] wip: showing pdf viewport --- .gitignore | 1 + README.md | 1 + go.mod | 3 +- go.sum | 2 + gshmm.go | 128 +++++++++++++++++++++++++++++++++++++++++++---------- 5 files changed, 111 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 1b42357..89f0e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /datasheets/ +debug.log gshmm diff --git a/README.md b/README.md index 81fe8e5..3d7e708 100644 --- a/README.md +++ b/README.md @@ -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 +tail -f debug.log # in another terminal go run goshmm.go ``` diff --git a/go.mod b/go.mod index aaa8dbe..2b1f10c 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,14 @@ go 1.18 require ( github.com/charmbracelet/bubbles v0.15.0 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 ) require ( github.com/atotto/clipboard v0.1.4 // 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/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.16 // indirect diff --git a/go.sum b/go.sum index 81d752c..b443eaf 100644 --- a/go.sum +++ b/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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 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/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= diff --git a/gshmm.go b/gshmm.go index 1827f4e..ee2218a 100644 --- a/gshmm.go +++ b/gshmm.go @@ -1,14 +1,20 @@ package main import ( + "bytes" + "errors" "flag" "fmt" + "log" "os" "path/filepath" "strings" "github.com/charmbracelet/bubbles/textinput" + "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/ledongthuc/pdf" "github.com/sahilm/fuzzy" ) @@ -35,18 +41,60 @@ func handleCliFlags() { 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 { - filterInput textinput.Model // fuzzy search interface - datasheets []string // all datasheets under cwd - dataSheetsView []string // filtered view on all datasheets + filterInput textinput.Model // fuzzy search interface + datasheets []datasheet // all datasheets under cwd + 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 { input := textinput.New() input.Focus() - var ds []string - // TODO(d1): handle error in interface? + var ds []datasheet + // TODO: handle error in interface? _ = filepath.Walk(".", func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -54,35 +102,50 @@ func initialModel() model { name := info.Name() 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 model{ - filterInput: input, - datasheets: ds, - dataSheetsView: ds, + viewp := viewport.New(60, 30) + viewp.SetContent(ds[len(ds)-1].contents) + + m := model{ + filterInput: input, + datasheets: 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 { - // TODO(d1): implement reading all PDFs in cwd - // https://stackoverflow.com/a/67214584 - // requires error reporting also return textinput.Blink } 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() { var matched []string search := m.filterInput.Value() if len(search) >= minCharsUntilFilter { - matches := fuzzy.Find(search, m.datasheets) + matches := fuzzy.Find(search, m.dataSheetNames()) for _, match := range matches { matched = append(matched, match.Str) } @@ -90,12 +153,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if len(matches) > 0 { m.dataSheetsView = matched } else { - m.dataSheetsView = m.datasheets + m.dataSheetsView = m.dataSheetNames() } } else { - m.dataSheetsView = m.datasheets + m.dataSheetsView = m.dataSheetNames() } - } 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) + cmds = append(cmds, 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, cmd + return m, tea.Batch(cmds...) } func (m model) View() string { body := strings.Builder{} - // TODO(d1): paginate / trim view to last 10 or something? - body.WriteString(strings.Join(m.dataSheetsView, "\n") + "\n") + // TODO: paginate / trim view to last 10 or something? + 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() } @@ -130,7 +200,19 @@ func main() { 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 { fmt.Printf("oops, something went wrong: %v", err) os.Exit(1)