diff --git a/internal/ui/actionsList.go b/actionsList.go similarity index 91% rename from internal/ui/actionsList.go rename to actionsList.go index 98666c2..a6d068f 100644 --- a/internal/ui/actionsList.go +++ b/actionsList.go @@ -1,7 +1,8 @@ -package ui +package main import ( "fmt" + "gemini_site/gemlog" tea "github.com/charmbracelet/bubbletea" ) @@ -50,7 +51,7 @@ func (m ActionListPageModel) Update(msg tea.Msg, active bool, ctx *context) (Act action := actions[m.cursor] switch action { case Write: - return m, WritePostCMD(ctx.config) + return m, gemlog.WritePostCMD(ctx.config) case Read, Delete, Edit: switchPageCmd := func() tea.Msg { return SwitchPages{Page: EntryList} @@ -58,7 +59,7 @@ func (m ActionListPageModel) Update(msg tea.Msg, active bool, ctx *context) (Act actionToTakeCmd := func() tea.Msg { return SelectActionToTake{ActionToTake: action} } - loadGemLogsCmd := LoadGemlogsCMD(ctx.config) + loadGemLogsCmd := gemlog.LoadGemlogsCMD(ctx.config) return m, tea.Batch(switchPageCmd, loadGemLogsCmd, actionToTakeCmd) } } diff --git a/internal/ui/entry.go b/entry.go similarity index 88% rename from internal/ui/entry.go rename to entry.go index 6ef36b1..d9f25ad 100644 --- a/internal/ui/entry.go +++ b/entry.go @@ -1,7 +1,8 @@ -package ui +package main import ( - gemlog "git.travisshears.com/travisshears/gemlog-cli/gemlog" + "gemini_site/gemlog" + tea "github.com/charmbracelet/bubbletea" ) @@ -15,7 +16,7 @@ func initialEntryPageModel() EntryPageModel { func (m EntryPageModel) Update(msg tea.Msg, active bool, ctx *context) (EntryPageModel, tea.Cmd) { switch msg := msg.(type) { - case GemLogLoaded: + case gemlog.GemLogLoaded: m.entry = msg.Log case tea.KeyMsg: if !active { diff --git a/internal/ui/entryList.go b/entryList.go similarity index 88% rename from internal/ui/entryList.go rename to entryList.go index ce6ba33..b70bc5f 100644 --- a/internal/ui/entryList.go +++ b/entryList.go @@ -1,9 +1,9 @@ -package ui +package main import ( "fmt" + "gemini_site/gemlog" - gemlog "git.travisshears.com/travisshears/gemlog-cli/gemlog" tea "github.com/charmbracelet/bubbletea" ) @@ -26,7 +26,7 @@ func (m EntryListPageModel) Update(msg tea.Msg, active bool, ctx *context) (Entr switch msg := msg.(type) { case SelectActionToTake: m.actionToTake = msg.ActionToTake - case GemLogsLoaded: + case gemlog.GemLogsLoaded: m.entries = msg.Logs return m, nil case tea.KeyMsg: @@ -54,14 +54,14 @@ func (m EntryListPageModel) Update(msg tea.Msg, active bool, ctx *context) (Entr switch m.actionToTake { // TODO: handle edit case Read: - loadCmd := LoadGemlogCMD(ctx.config, id) + loadCmd := gemlog.LoadGemlogCMD(ctx.config, id) navCmd := func() tea.Msg { return SwitchPages{Page: Entry} } return m, tea.Sequence(loadCmd, navCmd) case Delete: - delCmd := DeleteGemlogCMD(ctx.config, id, rev) - loadCmd := LoadGemlogsCMD(ctx.config) + delCmd := gemlog.DeleteGemlogCMD(ctx.config, id, rev) + loadCmd := gemlog.LoadGemlogsCMD(ctx.config) navCmd := func() tea.Msg { return SwitchPages{Page: ActionList} } diff --git a/gemlog/common.go b/gemlog/common.go new file mode 100644 index 0000000..363215d --- /dev/null +++ b/gemlog/common.go @@ -0,0 +1,6 @@ +package gemlog + +type Notification string +type ErrorMsg struct{ err error } +type GemLogsLoaded struct{ Logs []GemlogListEntry } +type GemLogLoaded struct{ Log GemlogEntry } diff --git a/gemlog/db.go b/gemlog/db.go index 3d21ce4..356a466 100644 --- a/gemlog/db.go +++ b/gemlog/db.go @@ -16,7 +16,7 @@ func genBasicAuthHeader(user, password string) string { return fmt.Sprintf("Basic %s", auth) } -func ListGemLogs(config *Config) ([]GemlogListEntry, error) { +func listGemLogs(config *Config) ([]GemlogListEntry, error) { url := fmt.Sprintf("%s:%d/gemlog/_design/gemlog-cli/_view/list", config.CouchDB.Host, config.CouchDB.Port) req, err := http.NewRequest("GET", url, nil) if err != nil { @@ -104,6 +104,8 @@ func ReadGemlogEntry(config *Config, id string) (GemlogEntry, error) { } var rawData struct { + // ID int `json:"_id"` + // Rev int `json:"_rev"` Title string `json:"title"` GemText string `json:"gemtxt"` Slug string `json:"slug"` @@ -183,6 +185,9 @@ func SaveGemlogEntry(config *Config, entry *GemlogEntry) error { return fmt.Errorf("unexpected status code %d: %s", res.StatusCode, string(body)) } + // fmt.Println(res) + // fmt.Println(string(body)) + return nil } diff --git a/internal/ui/commands.go b/gemlog/read.go similarity index 51% rename from internal/ui/commands.go rename to gemlog/read.go index b92c91c..173fc7b 100644 --- a/internal/ui/commands.go +++ b/gemlog/read.go @@ -1,15 +1,14 @@ -package ui +package gemlog import ( "fmt" - gemlog "git.travisshears.com/travisshears/gemlog-cli/gemlog" tea "github.com/charmbracelet/bubbletea" ) -func DeleteGemlogCMD(config *gemlog.Config, id string, rev string) tea.Cmd { +func DeleteGemlogCMD(config *Config, id string, rev string) tea.Cmd { return func() tea.Msg { - err := gemlog.DeleteGemlogEntry(config, id, rev) + err := DeleteGemlogEntry(config, id, rev) if err != nil { return ErrorMsg{err} } @@ -18,9 +17,9 @@ func DeleteGemlogCMD(config *gemlog.Config, id string, rev string) tea.Cmd { } } -func LoadGemlogCMD(config *gemlog.Config, id string) tea.Cmd { +func LoadGemlogCMD(config *Config, id string) tea.Cmd { return func() tea.Msg { - log, err := gemlog.ReadGemlogEntry(config, id) + log, err := ReadGemlogEntry(config, id) if err != nil { return ErrorMsg{err} } @@ -28,9 +27,9 @@ func LoadGemlogCMD(config *gemlog.Config, id string) tea.Cmd { } } -func LoadGemlogsCMD(config *gemlog.Config) tea.Cmd { +func LoadGemlogsCMD(config *Config) tea.Cmd { return func() tea.Msg { - logs, err := gemlog.ListGemLogs(config) + logs, err := listGemLogs(config) if err != nil { return ErrorMsg{err} } diff --git a/internal/ui/write.go b/gemlog/write.go similarity index 84% rename from internal/ui/write.go rename to gemlog/write.go index 1f31516..3c534f8 100644 --- a/internal/ui/write.go +++ b/gemlog/write.go @@ -1,4 +1,4 @@ -package ui +package gemlog import ( "fmt" @@ -7,15 +7,11 @@ import ( "strings" "time" - gemlog "git.travisshears.com/travisshears/gemlog-cli/gemlog" tea "github.com/charmbracelet/bubbletea" "gopkg.in/yaml.v3" ) -// TODO: add edit command -// func EditPostCMD(config *gemlog.Config) tea.Cmd {} - -func WritePostCMD(config *gemlog.Config) tea.Cmd { +func WritePostCMD(config *Config) tea.Cmd { return func() tea.Msg { // Create a temporary file tmpFile, err := os.CreateTemp("/tmp", "gemlog-*.md") @@ -65,7 +61,7 @@ func WritePostCMD(config *gemlog.Config) tea.Cmd { if err != nil { return ErrorMsg{fmt.Errorf("failed to parse post: %w", err)} } - if err := gemlog.SaveGemlogEntry(config, &gemlogEntry); err != nil { + if err := SaveGemlogEntry(config, &gemlogEntry); err != nil { return ErrorMsg{fmt.Errorf("failed to save gemlog entry: %w", err)} } @@ -75,7 +71,7 @@ func WritePostCMD(config *gemlog.Config) tea.Cmd { } } -func parsePost(post string) (gemlog.GemlogEntry, error) { +func parsePost(post string) (GemlogEntry, error) { // split post on new lines lines := strings.Split(post, "\n") @@ -89,7 +85,7 @@ func parsePost(post string) (gemlog.GemlogEntry, error) { } if separatorIndex == -1 { - return gemlog.GemlogEntry{}, fmt.Errorf("no frontmatter separator '---' found") + return GemlogEntry{}, fmt.Errorf("no frontmatter separator '---' found") } // Extract frontmatter and body @@ -107,13 +103,13 @@ func parsePost(post string) (gemlog.GemlogEntry, error) { } if err := yaml.Unmarshal([]byte(frontmatterYAML), &frontmatter); err != nil { - return gemlog.GemlogEntry{}, fmt.Errorf("failed to parse frontmatter: %w", err) + return GemlogEntry{}, fmt.Errorf("failed to parse frontmatter: %w", err) } // Parse date date, err := time.Parse("2006-01-02", strings.TrimSpace(frontmatter.Date)) if err != nil { - return gemlog.GemlogEntry{}, fmt.Errorf("failed to parse date: %w", err) + return GemlogEntry{}, fmt.Errorf("failed to parse date: %w", err) } // Parse tags (comma-separated) @@ -131,7 +127,7 @@ func parsePost(post string) (gemlog.GemlogEntry, error) { // Join body lines body := strings.Join(bodyLines, "\n") - return gemlog.GemlogEntry{ + return GemlogEntry{ Title: frontmatter.Title, Date: date, Slug: frontmatter.Slug, diff --git a/go.mod b/go.mod index b9b315d..dc509a0 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module git.travisshears.com/travisshears/gemlog-cli +module gemini_site go 1.25.0 diff --git a/internal/ui/app.go b/internal/ui/app.go deleted file mode 100644 index e775d72..0000000 --- a/internal/ui/app.go +++ /dev/null @@ -1,151 +0,0 @@ -package ui - -import ( - "fmt" - "io" - "log/slog" - "os" - - gemlog "git.travisshears.com/travisshears/gemlog-cli/gemlog" - tea "github.com/charmbracelet/bubbletea" -) - -type Page string - -const ( - ActionList Page = "actionList" - EntryList Page = "entryList" - Entry Page = "entry" -) - -type SwitchPages struct{ Page Page } -type Notification string -type ErrorMsg struct{ err error } -type GemLogsLoaded struct{ Logs []gemlog.GemlogListEntry } -type GemLogLoaded struct{ Log gemlog.GemlogEntry } - -type uiState struct { - notification string - errorTxt string - - page Page - entryListPage EntryListPageModel - entryPage EntryPageModel - actionListPage ActionListPageModel -} - -type context struct { - config *gemlog.Config -} - -type model struct { - ui uiState - context *context -} - -func initialModel(config *gemlog.Config) model { - return model{ - ui: uiState{ - page: ActionList, - actionListPage: initialActionListPageModel(), - entryListPage: initialEntryListPageModel(), - entryPage: initialEntryPageModel(), - }, - context: &context{ - config: config, - }, - } -} - -func (m model) Init() tea.Cmd { - cmds := make([]tea.Cmd, 0) - cmds = append(cmds, tea.SetWindowTitle("Gemlog CLI")) - // TODO: add init commands from other pages - return tea.Batch(cmds...) -} - -func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - cmds := make([]tea.Cmd, 3) - switch msg := msg.(type) { - case SwitchPages: - m.ui.page = msg.Page - case ErrorMsg: - m.ui.errorTxt = fmt.Sprintf("%s\n\n", fmt.Errorf("%s", msg)) - case Notification: - m.ui.notification = fmt.Sprintf("%s\n\n", string(msg)) - case tea.KeyMsg: - switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit - } - } - - actionListM, cmd := m.ui.actionListPage.Update(msg, m.ui.page == ActionList, m.context) - m.ui.actionListPage = actionListM - cmds = append(cmds, cmd) - - entryListM, cmd := m.ui.entryListPage.Update(msg, m.ui.page == EntryList, m.context) - m.ui.entryListPage = entryListM - cmds = append(cmds, cmd) - - entrytM, cmd := m.ui.entryPage.Update(msg, m.ui.page == Entry, m.context) - m.ui.entryPage = entrytM - cmds = append(cmds, cmd) - - return m, tea.Batch(cmds...) -} - -func (m model) View() string { - if len(m.ui.errorTxt) > 0 { - return m.ui.errorTxt - } - s := "" - if m.ui.notification != "" { - s += m.ui.notification - } - - switch m.ui.page { - case ActionList: - s += m.ui.actionListPage.View() - case EntryList: - s += m.ui.entryListPage.View() - case Entry: - s += m.ui.entryPage.View() - } - - s += "\nPress q to quit.\n" - - return s -} - -var enableLogs bool = false - -func Run() { - if enableLogs { - f, err := tea.LogToFile("debug.log", "debug") - if err != nil { - fmt.Println("fatal:", err) - os.Exit(1) - } - defer f.Close() - } else { - logger := slog.New(slog.NewJSONHandler(io.Discard, nil)) - slog.SetDefault(logger) - } - slog.Info("Starting gemlog cli") - config, err := gemlog.LoadConfig() - if err != nil { - fmt.Printf("Error loading config: %v", err) - os.Exit(1) - } - err = gemlog.CheckDBConnection(config) - if err != nil { - fmt.Printf("Error checking db connection: %v", err) - os.Exit(1) - } - p := tea.NewProgram(initialModel(config)) - if _, err := p.Run(); err != nil { - fmt.Printf("Alas, there's been an error: %v", err) - os.Exit(1) - } -} diff --git a/internal/ui/templates.go b/internal/ui/templates.go deleted file mode 100644 index 80039da..0000000 --- a/internal/ui/templates.go +++ /dev/null @@ -1,15 +0,0 @@ -package ui - -const defaultTemplate = `title: todo -date: 2025-12-31 -slug: todo -tags: cat, dog ---- - -Example text - -=> https://travisshears.com example link - -* Example list item 1 -* Example list item 2 -` diff --git a/main.go b/main.go index 9572dda..cc2d5b3 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,139 @@ package main import ( - ui "git.travisshears.com/travisshears/gemlog-cli/internal/ui" + "fmt" + "gemini_site/gemlog" + "log/slog" + "os" + + tea "github.com/charmbracelet/bubbletea" ) -func main() { - ui.Run() +type Page string + +const ( + ActionList Page = "actionList" + EntryList Page = "entryList" + Entry Page = "entry" +) + +type SwitchPages struct{ Page Page } + +type uiState struct { + notification string + errorTxt string + + page Page + entryListPage EntryListPageModel + entryPage EntryPageModel + actionListPage ActionListPageModel +} + +type context struct { + config *gemlog.Config +} + +type model struct { + ui uiState + context *context +} + +func initialModel(config *gemlog.Config) model { + return model{ + ui: uiState{ + page: ActionList, + actionListPage: initialActionListPageModel(), + entryListPage: initialEntryListPageModel(), + entryPage: initialEntryPageModel(), + }, + context: &context{ + config: config, + }, + } +} + +func (m model) Init() tea.Cmd { + cmds := make([]tea.Cmd, 0) + cmds = append(cmds, tea.SetWindowTitle("Gemlog CLI")) + // TODO: add init commands from other pages + return tea.Batch(cmds...) +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + cmds := make([]tea.Cmd, 3) + switch msg := msg.(type) { + case SwitchPages: + m.ui.page = msg.Page + case gemlog.ErrorMsg: + m.ui.errorTxt = fmt.Sprintf("%s\n\n", fmt.Errorf("%s", msg)) + case gemlog.Notification: + m.ui.notification = fmt.Sprintf("%s\n\n", string(msg)) + case tea.KeyMsg: + switch msg.String() { + case "ctrl+c", "q": + return m, tea.Quit + } + } + + actionListM, cmd := m.ui.actionListPage.Update(msg, m.ui.page == ActionList, m.context) + m.ui.actionListPage = actionListM + cmds = append(cmds, cmd) + + entryListM, cmd := m.ui.entryListPage.Update(msg, m.ui.page == EntryList, m.context) + m.ui.entryListPage = entryListM + cmds = append(cmds, cmd) + + entrytM, cmd := m.ui.entryPage.Update(msg, m.ui.page == Entry, m.context) + m.ui.entryPage = entrytM + cmds = append(cmds, cmd) + + return m, tea.Batch(cmds...) +} + +func (m model) View() string { + if len(m.ui.errorTxt) > 0 { + return m.ui.errorTxt + } + s := "" + if m.ui.notification != "" { + s += m.ui.notification + } + + switch m.ui.page { + case ActionList: + s += m.ui.actionListPage.View() + case EntryList: + s += m.ui.entryListPage.View() + case Entry: + s += m.ui.entryPage.View() + } + + s += "\nPress q to quit.\n" + + return s +} + +func main() { + f, err := tea.LogToFile("debug.log", "debug") + if err != nil { + fmt.Println("fatal:", err) + os.Exit(1) + } + defer f.Close() + slog.Info("Starting gemlog cli") + config, err := gemlog.LoadConfig() + if err != nil { + fmt.Printf("Error loading config: %v", err) + os.Exit(1) + } + err = gemlog.CheckDBConnection(config) + if err != nil { + fmt.Printf("Error checking db connection: %v", err) + os.Exit(1) + } + p := tea.NewProgram(initialModel(config)) + if _, err := p.Run(); err != nil { + fmt.Printf("Alas, there's been an error: %v", err) + os.Exit(1) + } }