Files
firestar5683 ab81464bd1 Maps
Add navigation and map matching providers with the vendored map service sources.

Co-authored-by: Daniel Koepping <elkoled@gmail.com>
Co-authored-by: Danny <dannyirwin31@yahoo.com>
Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu>
Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com>
2026-10-14 19:56:17 -05:00

150 lines
4.2 KiB
Go

package cli
import (
"fmt"
"os"
"time"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"pfeifer.dev/mapd/cereal"
"pfeifer.dev/mapd/cereal/custom"
ms "pfeifer.dev/mapd/settings"
)
type mainState int
const (
showMenu mainState = iota
showSettings
showDownload
showOutput
showDownloadProgress
)
var docStyle = lipgloss.NewStyle().Margin(1, 2)
type TickMsg time.Time
func tickEvery() tea.Cmd {
return tea.Every(50*time.Millisecond, func(t time.Time) tea.Msg {
return TickMsg(t)
})
}
type uiModel struct {
list list.Model
state mainState
settings settingsModel
output outputModel
download downloadModel
downloadProgress downloadProgressModel
pub *cereal.Publisher[custom.MapdIn]
sub *cereal.Subscriber[custom.MapdOut]
extendedSub *cereal.Subscriber[custom.MapdExtendedOut]
extendedData custom.MapdExtendedOut
extendedDataValid bool
}
type item struct {
title, desc string
state mainState
}
func (i item) Title() string { return i.title }
func (i item) Description() string { return i.desc }
func (i item) FilterValue() string { return i.title }
func initialModel() uiModel {
items := []list.Item{
item{title: "Settings", desc: "Modify settings of an active instance of mapd", state: showSettings},
item{title: "Download", desc: "Trigger a download of maps in an active instance of mapd", state: showDownload},
item{title: "Download Progress", desc: "Watch the live download progress from mapd", state: showDownloadProgress},
item{title: "Watch", desc: "Watch the live output from mapd", state: showOutput},
}
listDelegate := list.NewDefaultDelegate()
pub := cereal.NewPublisher("mapdCli", cereal.MapdInCreator)
sub := cereal.NewSubscriber("mapdOut", cereal.MapdOutReader, true, false)
extendedSub := cereal.NewSubscriber("mapdExtendedOut", cereal.MapdExtendedOutReader, true, false)
m := uiModel{list: list.New(items, listDelegate, 0, 0), settings: getSettingsModel(), pub: &pub, sub: &sub, extendedSub: &extendedSub, download: getDownloadModel()}
m.list.Title = "Mapd Actions"
return m
}
func (m uiModel) Init() tea.Cmd {
// Just return `nil`, which means "no I/O right now, please."
return tickEvery()
}
func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
if msg.String() == "ctrl+c" {
return m, tea.Quit
}
if msg.Type == tea.KeyEnter && m.state == showMenu && m.list.FilterState() != list.Filtering {
it := m.list.SelectedItem().(item)
m.state = it.state
return m, nil
}
case tea.WindowSizeMsg:
h, v := docStyle.GetFrameSize()
m.list.SetSize(msg.Width-h, msg.Height-v)
m.settings, _ = m.settings.Update(msg, &m)
m.download, _ = m.download.Update(msg, &m)
m.output, _ = m.output.Update(msg, &m)
m.downloadProgress, _ = m.downloadProgress.Update(msg, &m)
case TickMsg:
extendedData, success := m.extendedSub.Read()
if success {
m.extendedData = extendedData
settingsData, _ := extendedData.Settings()
ms.Settings.Unmarshal([]byte(settingsData))
m.extendedDataValid = true
}
m.output, _ = m.output.Update(msg, &m)
m.downloadProgress, _ = m.downloadProgress.Update(msg, &m)
m.settings, _ = m.settings.Update(msg, &m)
return m, tickEvery()
}
var cmd tea.Cmd
switch m.state {
case showSettings:
m.settings, cmd = m.settings.Update(msg, &m)
case showOutput:
m.output, cmd = m.output.Update(msg, &m)
case showDownload:
m.download, cmd = m.download.Update(msg, &m)
case showDownloadProgress:
m.downloadProgress, cmd = m.downloadProgress.Update(msg, &m)
default:
m.list, cmd = m.list.Update(msg)
}
return m, cmd
}
func (m uiModel) View() string {
switch m.state {
case showSettings:
return m.settings.View()
case showOutput:
return m.output.View()
case showDownload:
return m.download.View()
case showDownloadProgress:
return m.downloadProgress.View()
}
return docStyle.Render(m.list.View())
}
func interactive() {
p := tea.NewProgram(initialModel(), tea.WithAltScreen())
if _, err := p.Run(); err != nil {
fmt.Printf("Alas, there's been an error: %v", err)
os.Exit(1)
}
}