package main import ( "errors" "fmt" "os" "path/filepath" "strings" tea "github.com/charmbracelet/bubbletea" ) type model struct { project string todosDir string count int } func newModel(project string) (model, error) { absolute, err := filepath.Abs(project) if err != nil { return model{}, fmt.Errorf("resolve project directory %q: %w", project, err) } info, err := os.Stat(absolute) if err != nil { return model{}, fmt.Errorf("project directory %q: %w", absolute, err) } if !info.IsDir() { return model{}, fmt.Errorf("project path %q is not a directory", absolute) } dir := filepath.Join(absolute, ".pi", "todos") entries, err := os.ReadDir(dir) if errors.Is(err, os.ErrNotExist) { return model{project: absolute, todosDir: dir}, nil } if err != nil { return model{}, fmt.Errorf("read todo directory %q: %w", dir, err) } count := 0 for _, entry := range entries { if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".md") { count++ } } return model{project: absolute, todosDir: dir, count: count}, nil } func (m model) Init() tea.Cmd { return nil } func (m model) Update(message tea.Msg) (tea.Model, tea.Cmd) { if key, ok := message.(tea.KeyMsg); ok { switch key.String() { case "q", "ctrl+c": return m, tea.Quit } } return m, nil } func (m model) View() string { var state string if m.count == 0 { state = "No todos yet. Pi stores todo items in this directory." } else { state = fmt.Sprintf("Found %d todo file(s). Browsing is coming in the next ticket.", m.count) } return fmt.Sprintf("TodoApp\n\nProject: %s\nTodos: %s\n\n%s\n\nPress q or Ctrl+C to quit.\n", m.project, m.todosDir, state) }