|
|
@ -8,6 +8,7 @@ import ( |
|
|
|
"path/filepath" |
|
|
|
"strings" |
|
|
|
|
|
|
|
"github.com/charmbracelet/bubbles/spinner" |
|
|
|
"github.com/charmbracelet/bubbles/textinput" |
|
|
|
"github.com/charmbracelet/bubbles/viewport" |
|
|
|
tea "github.com/charmbracelet/bubbletea" |
|
|
@ -68,9 +69,11 @@ func readPDF(name string) (string, error) { |
|
|
|
type model struct { |
|
|
|
input textinput.Model // Fuzzy search interface
|
|
|
|
|
|
|
|
datasheets []datasheet // All datasheets under cwd
|
|
|
|
datasheetNames []string // All datasheet names (caching)
|
|
|
|
filteredDatasheets []string // Filtered view on all datasheets
|
|
|
|
datasheets []datasheet // All datasheets under cwd
|
|
|
|
datasheetNames []string // All datasheet names (caching)
|
|
|
|
filteredDatasheets []string // Filtered view on all datasheets
|
|
|
|
loadDatasheetSpinner spinner.Model // Spinner to show while loading datasheets
|
|
|
|
datasheetsLoaded bool // Whether or not the datasheets are loaded or not
|
|
|
|
|
|
|
|
datasheetViewport viewport.Model // Viewport for the PDF content
|
|
|
|
|
|
|
@ -108,6 +111,35 @@ func initialModel() model { |
|
|
|
input := textinput.New() |
|
|
|
input.Focus() |
|
|
|
|
|
|
|
// TODO: set width/heigh to match terminal. this should also
|
|
|
|
// be set in relation to the list of filenames also. they
|
|
|
|
// should have some visually pleasing ratio set i imagine
|
|
|
|
viewp := viewport.New(60, 30) |
|
|
|
|
|
|
|
loadDatasheetSpinner := spinner.New() |
|
|
|
loadDatasheetSpinner.Spinner = spinner.Dot |
|
|
|
loadDatasheetSpinner.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) |
|
|
|
|
|
|
|
m := model{ |
|
|
|
input: input, |
|
|
|
datasheetsLoaded: false, |
|
|
|
loadDatasheetSpinner: loadDatasheetSpinner, |
|
|
|
datasheetViewport: viewp, |
|
|
|
filterMode: filenameFilterMode, |
|
|
|
} |
|
|
|
|
|
|
|
return m |
|
|
|
} |
|
|
|
|
|
|
|
// loadedDatasheetsMsg signals to the UI that the datasheets have been loaded.
|
|
|
|
type loadedDatasheetsMsg struct { |
|
|
|
datasheets []datasheet |
|
|
|
datasheetNames []string |
|
|
|
} |
|
|
|
|
|
|
|
// loadDatasheets loads the datasheets from disk. The UI shows a spinner while
|
|
|
|
// this is happening so that nobody gets bored.
|
|
|
|
func loadDatasheets(m model) loadedDatasheetsMsg { |
|
|
|
var datasheets []datasheet |
|
|
|
var datasheetNames []string |
|
|
|
|
|
|
@ -138,28 +170,19 @@ func initialModel() model { |
|
|
|
return nil |
|
|
|
}) |
|
|
|
|
|
|
|
// TODO: set width/heigh to match terminal. this should also
|
|
|
|
// be set in relation to the list of filenames also. they
|
|
|
|
// should have some visually pleasing ratio set i imagine
|
|
|
|
viewp := viewport.New(60, 30) |
|
|
|
selectedDatasheet := datasheets[len(datasheets)-1].contents |
|
|
|
viewp.SetContent(selectedDatasheet) |
|
|
|
|
|
|
|
m := model{ |
|
|
|
input: input, |
|
|
|
datasheets: datasheets, |
|
|
|
datasheetNames: datasheetNames, |
|
|
|
filteredDatasheets: datasheetNames, |
|
|
|
datasheetViewport: viewp, |
|
|
|
filterMode: filenameFilterMode, |
|
|
|
return loadedDatasheetsMsg{ |
|
|
|
datasheets: datasheets, |
|
|
|
datasheetNames: datasheetNames, |
|
|
|
} |
|
|
|
|
|
|
|
return m |
|
|
|
} |
|
|
|
|
|
|
|
// Init initialises the program.
|
|
|
|
func (m model) Init() tea.Cmd { |
|
|
|
return textinput.Blink |
|
|
|
return tea.Batch( |
|
|
|
func() tea.Msg { return loadDatasheets(m) }, |
|
|
|
textinput.Blink, |
|
|
|
m.loadDatasheetSpinner.Tick, |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
// filterDatasheetNames filters datasheet names based on user input.
|
|
|
@ -189,7 +212,16 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { |
|
|
|
cmds []tea.Cmd |
|
|
|
) |
|
|
|
|
|
|
|
if m.input.Focused() { |
|
|
|
m.input, cmd = m.input.Update(msg) |
|
|
|
cmds = append(cmds, cmd) |
|
|
|
|
|
|
|
m.datasheetViewport, cmd = m.datasheetViewport.Update(msg) |
|
|
|
cmds = append(cmds, cmd) |
|
|
|
|
|
|
|
m.loadDatasheetSpinner, cmd = m.loadDatasheetSpinner.Update(msg) |
|
|
|
cmds = append(cmds, cmd) |
|
|
|
|
|
|
|
if m.input.Focused() && m.datasheetsLoaded { |
|
|
|
m.filteredDatasheets = filterDatasheetNames(m) |
|
|
|
|
|
|
|
// TODO: implement cursor for scrolling up/down filtered
|
|
|
@ -202,6 +234,15 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { |
|
|
|
|
|
|
|
// TODO: handle terminal resizing
|
|
|
|
switch msg := msg.(type) { |
|
|
|
case loadedDatasheetsMsg: |
|
|
|
m.datasheets = msg.datasheets |
|
|
|
m.datasheetNames = msg.datasheetNames |
|
|
|
m.filteredDatasheets = msg.datasheetNames |
|
|
|
|
|
|
|
selectedDatasheet := msg.datasheets[len(msg.datasheets)-1].contents |
|
|
|
m.datasheetViewport.SetContent(selectedDatasheet) |
|
|
|
|
|
|
|
m.datasheetsLoaded = true |
|
|
|
case tea.KeyMsg: |
|
|
|
switch msg.String() { |
|
|
|
case "ctrl+c": |
|
|
@ -211,12 +252,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
m.input, cmd = m.input.Update(msg) |
|
|
|
cmds = append(cmds, cmd) |
|
|
|
|
|
|
|
m.datasheetViewport, cmd = m.datasheetViewport.Update(msg) |
|
|
|
cmds = append(cmds, cmd) |
|
|
|
|
|
|
|
return m, tea.Batch(cmds...) |
|
|
|
} |
|
|
|
|
|
|
@ -224,8 +259,13 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { |
|
|
|
func (m model) View() string { |
|
|
|
body := strings.Builder{} |
|
|
|
|
|
|
|
// TODO: paginate / trim view to last 10 or something?
|
|
|
|
sheets := strings.Join(m.filteredDatasheets, "\n") |
|
|
|
var sheets string |
|
|
|
if !m.datasheetsLoaded { |
|
|
|
sheets = fmt.Sprintf("%s gathering datasheets...", m.loadDatasheetSpinner.View()) |
|
|
|
} else { |
|
|
|
// TODO: paginate / trim view to last 10 or something?
|
|
|
|
sheets = strings.Join(m.filteredDatasheets, "\n") |
|
|
|
} |
|
|
|
|
|
|
|
// TODO: style further with lipgloss, e.g. borders, margins, etc.
|
|
|
|
panes := lipgloss.JoinHorizontal(lipgloss.Left, sheets, m.datasheetViewport.View()) |
|
|
|