|
|
@ -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) |
|
|
|