From 6f56e0b26af7b9c84c42030c3c8c3403028a3457 Mon Sep 17 00:00:00 2001 From: Hadi <112569860+anotherhadi@users.noreply.github.com> Date: Tue, 19 May 2026 14:34:48 +0200 Subject: [PATCH] ui/home is now in the same app Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com> --- cmd/spilltea/main.go | 23 +++++---------- cmd/spilltea/root.go | 60 ++++++++++++++++++++++++++++++++++++++ internal/ui/home/model.go | 9 +++--- internal/ui/home/update.go | 12 ++++---- 4 files changed, 79 insertions(+), 25 deletions(-) create mode 100644 cmd/spilltea/root.go diff --git a/cmd/spilltea/main.go b/cmd/spilltea/main.go index efad090..251b16c 100644 --- a/cmd/spilltea/main.go +++ b/cmd/spilltea/main.go @@ -127,32 +127,25 @@ func main() { projectDir := config.ExpandPath(config.Global.App.ProjectDir) - // Resolve project: either from --project flag or by running the home UI. - var project *homeUI.Project + // If --project flag is set, skip the home screen entirely. if *flagProject != "" { - p, err := homeUI.OpenProject(projectDir, *flagProject) + project, err := homeUI.OpenProject(projectDir, *flagProject) if err != nil { fmt.Fprintf(os.Stderr, "project: %v\n", err) os.Exit(1) } - project = p - } else { - finalModel, err := tea.NewProgram(homeUI.New(projectDir)).Run() - if err != nil { + broker := intercept.NewBroker() + m := appUI.New(broker, project.Name, project.Path) + if _, err := tea.NewProgram(m).Run(); err != nil { fmt.Fprintf(os.Stderr, "tui: %v\n", err) os.Exit(1) } - project = finalModel.(homeUI.Model).Selected() - } - - // User quit the home screen without selecting a project. - if project == nil { return } - broker := intercept.NewBroker() - m := appUI.New(broker, project.Name, project.Path) - if _, err := tea.NewProgram(m).Run(); err != nil { + // Run home + app in a single program to avoid a blank flash on transition. + root := rootModel{home: homeUI.New(projectDir)} + if _, err := tea.NewProgram(root).Run(); err != nil { fmt.Fprintf(os.Stderr, "tui: %v\n", err) os.Exit(1) } diff --git a/cmd/spilltea/root.go b/cmd/spilltea/root.go new file mode 100644 index 0000000..072c608 --- /dev/null +++ b/cmd/spilltea/root.go @@ -0,0 +1,60 @@ +package main + +import ( + tea "charm.land/bubbletea/v2" + "github.com/anotherhadi/spilltea/internal/intercept" + appUI "github.com/anotherhadi/spilltea/internal/ui/app" + homeUI "github.com/anotherhadi/spilltea/internal/ui/home" +) + +type rootState int + +const ( + rootStateHome rootState = iota + rootStateApp +) + +type rootModel struct { + state rootState + home homeUI.Model + app tea.Model + width int + height int +} + +func (m rootModel) Init() tea.Cmd { + return m.home.Init() +} + +func (m rootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + if ws, ok := msg.(tea.WindowSizeMsg); ok { + m.width = ws.Width + m.height = ws.Height + } + + if m.state == rootStateHome { + if sel, ok := msg.(homeUI.ProjectSelectedMsg); ok { + broker := intercept.NewBroker() + app := appUI.New(broker, sel.Project.Name, sel.Project.Path) + m.app = app + m.state = rootStateApp + return m, tea.Batch(app.Init(), func() tea.Msg { + return tea.WindowSizeMsg{Width: m.width, Height: m.height} + }) + } + updated, cmd := m.home.Update(msg) + m.home = updated.(homeUI.Model) + return m, cmd + } + + updated, cmd := m.app.Update(msg) + m.app = updated + return m, cmd +} + +func (m rootModel) View() tea.View { + if m.state == rootStateApp { + return m.app.(interface{ View() tea.View }).View() + } + return m.home.View() +} diff --git a/internal/ui/home/model.go b/internal/ui/home/model.go index c9e50ba..eb7d27c 100644 --- a/internal/ui/home/model.go +++ b/internal/ui/home/model.go @@ -142,6 +142,11 @@ type Project struct { ModTime time.Time } +// ProjectSelectedMsg is emitted when the user picks a project from the home screen. +type ProjectSelectedMsg struct { + Project *Project +} + type inputMode int const ( @@ -161,15 +166,11 @@ type Model struct { list list.Model projectDir string nameInput textinput.Model - selected *Project width int height int teapotFrame int } -// Selected returns the project chosen by the user, or nil if the program was -// quit without making a selection. -func (m Model) Selected() *Project { return m.selected } func New(projectDir string) Model { projects := loadProjects(projectDir) diff --git a/internal/ui/home/update.go b/internal/ui/home/update.go index 9498425..8500486 100644 --- a/internal/ui/home/update.go +++ b/internal/ui/home/update.go @@ -76,11 +76,11 @@ func (m Model) handleSelection() (tea.Model, tea.Cmd) { return m, nil } initProjectFiles(dir) - m.selected = &Project{Name: "temporary", Path: filepath.Join(dir, "data.db")} - return m, tea.Quit + p := &Project{Name: "temporary", Path: filepath.Join(dir, "data.db")} + return m, func() tea.Msg { return ProjectSelectedMsg{Project: p} } default: - m.selected = &Project{Name: item.name, Path: item.path} - return m, tea.Quit + p := &Project{Name: item.name, Path: item.path} + return m, func() tea.Msg { return ProjectSelectedMsg{Project: p} } } } @@ -117,8 +117,8 @@ func (m Model) updateNaming(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { return m, nil } initProjectFiles(dir) - m.selected = &Project{Name: name, Path: filepath.Join(dir, "data.db")} - return m, tea.Quit + p := &Project{Name: name, Path: filepath.Join(dir, "data.db")} + return m, func() tea.Msg { return ProjectSelectedMsg{Project: p} } default: var cmd tea.Cmd m.nameInput, cmd = m.nameInput.Update(msg)