From 1474bbbe077d17967bff8410e047dd470b4f6c1d Mon Sep 17 00:00:00 2001 From: NucleoFusion Date: Mon, 21 Jul 2025 13:07:35 +0530 Subject: [PATCH 1/5] adding initial daily diary entries --- cmd/init.go | 8 ++ internal/enums/page.go | 2 +- internal/keymap/diaryMap.go | 44 ++++++++++ internal/models/diary/diary.go | 145 +++++++++++++++++++++++++++++++ internal/models/menu/menu.go | 8 +- internal/models/menu/menulist.go | 2 +- internal/models/rootModel.go | 17 +++- 7 files changed, 218 insertions(+), 8 deletions(-) create mode 100644 internal/keymap/diaryMap.go create mode 100644 internal/models/diary/diary.go diff --git a/cmd/init.go b/cmd/init.go index c6dde10..7771c35 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -33,6 +33,14 @@ var initCmd = &cobra.Command{ return } + diaryDir := filepath.Join(home, config.AppConfig.General.NotesDir, ".diary") + + err = os.MkdirAll(diaryDir, 0o755) + if err != nil { + fmt.Println("Could not create .diary directory", err.Error()) + return + } + err = os.MkdirAll(home+"/.config/toney", 0o755) if err != nil { fmt.Println("Could not create config directory", err.Error()) diff --git a/internal/enums/page.go b/internal/enums/page.go index 4b4d4cc..ed3a7ad 100644 --- a/internal/enums/page.go +++ b/internal/enums/page.go @@ -6,6 +6,6 @@ const ( MenuPage = iota HomePage DailyPage - JournalPage + DiaryPage Quit ) diff --git a/internal/keymap/diaryMap.go b/internal/keymap/diaryMap.go new file mode 100644 index 0000000..8a8a207 --- /dev/null +++ b/internal/keymap/diaryMap.go @@ -0,0 +1,44 @@ +package keymap + +import ( + "reflect" + + "github.com/charmbracelet/bubbles/key" +) + +type DiaryMap struct { + Edit key.Binding + ScrollUp key.Binding + ScrollDown key.Binding +} + +func NewDiaryMap() DiaryMap { + return DiaryMap{ + Edit: key.NewBinding( + key.WithKeys("e"), + key.WithHelp("e", "edit"), + ), + ScrollUp: key.NewBinding( + key.WithKeys("up"), + key.WithHelp("up", "scroll up"), + ), + ScrollDown: key.NewBinding( + key.WithKeys("down"), + key.WithHelp("down", "scroll down"), + ), + } +} + +func (m DiaryMap) Bindings() []key.Binding { + var bindings []key.Binding + + v := reflect.ValueOf(m) + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + if binding, ok := field.Interface().(key.Binding); ok { + bindings = append(bindings, binding) + } + } + + return bindings +} diff --git a/internal/models/diary/diary.go b/internal/models/diary/diary.go new file mode 100644 index 0000000..486340e --- /dev/null +++ b/internal/models/diary/diary.go @@ -0,0 +1,145 @@ +package diary + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "time" + + "github.com/SourcewareLab/Toney/internal/colors" + "github.com/SourcewareLab/Toney/internal/config" + "github.com/SourcewareLab/Toney/internal/keymap" + "github.com/SourcewareLab/Toney/internal/messages" + "github.com/SourcewareLab/Toney/internal/styles" + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" + "github.com/charmbracelet/bubbles/viewport" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/glamour" + "github.com/charmbracelet/lipgloss" +) + +type Diary struct { + Width int + Height int + Keymap keymap.DiaryMap + DirPath string + CurrFileName string + CurrDate time.Time + Vp viewport.Model + Help help.Model + Renderer *glamour.TermRenderer +} + +func NewDiary(w int, h int) *Diary { + today := time.Now().Format("2006-01-02") + ".md" + dirpath := config.AppConfig.General.NotesDir + + r, _ := glamour.NewTermRenderer(glamour.WithStyles(config.ToGlamourStyle(config.AppConfig.Styles.Renderer)), + glamour.WithWordWrap(w)) + content, _ := r.Render(ReadDiary(dirpath, today)) + + vp := viewport.New(w, h-1) + vp.Style = styles.BorderStyle(). + BorderForeground(colors.ColorPalette().FocusedBorder). + Foreground(colors.ColorPalette().Text) + vp.SetContent(content) + + return &Diary{ + Width: w, + Height: h, + Keymap: keymap.NewDiaryMap(), + Vp: vp, + Help: help.New(), + CurrFileName: today, + DirPath: dirpath, + Renderer: r, + } +} + +func (m *Diary) Init() tea.Cmd { + return nil +} + +func (m *Diary) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case messages.EditorClose: + m.Refresh() + return m, nil + case tea.KeyMsg: + switch { + case key.Matches(msg, m.Keymap.Edit): + home, _ := os.UserHomeDir() + c := exec.Command(config.AppConfig.General.Editor, filepath.Join(home, m.DirPath, ".diary", m.CurrFileName)) + cmd := tea.ExecProcess(c, func(err error) tea.Msg { + return messages.EditorClose{ + Err: err, + } + }) + + return m, cmd + default: + var cmd tea.Cmd + m.Vp, cmd = m.Vp.Update(msg) + return m, cmd + } + } + var cmd tea.Cmd + m.Vp, cmd = m.Vp.Update(msg) + return m, cmd +} + +func (m *Diary) View() string { + return lipgloss.JoinVertical(lipgloss.Left, m.Vp.View(), + lipgloss.NewStyle().PaddingLeft(2).Render(m.Help.View(keymap.NewDynamic(m.Keymap.Bindings())))) +} + +func (m *Diary) Refresh() { + data := ReadDiary(m.DirPath, m.CurrFileName) + content, _ := m.Renderer.Render(string(data)) + m.Vp.SetContent(content) +} + +func ReadDiary(dirpath string, today string) string { + home, _ := os.UserHomeDir() + path := filepath.Join(home, dirpath, ".diary", today) + + _, err := os.Stat(path) + if os.IsNotExist(err) { + f, _ := os.Create(path) + + now := time.Now() + day := now.Day() + suffix := getOrdinalSuffix(day) + formatted := fmt.Sprintf("%d%s %s %d", day, suffix, now.Format("January"), now.Year()) + + f.WriteString(fmt.Sprintf("# %s \n", formatted)) + f.Close() + } else if err != nil { + return fmt.Sprintf("Creating: %t", os.IsNotExist(err)) + } + + data, err := os.ReadFile(path) + if err != nil { + return err.Error() + } + + return string(data) +} + +func getOrdinalSuffix(day int) string { + if day >= 11 && day <= 13 { + return "th" + } + switch day % 10 { + case 1: + return "st" + case 2: + return "nd" + case 3: + return "rd" + default: + return "th" + } +} diff --git a/internal/models/menu/menu.go b/internal/models/menu/menu.go index af14caf..c9c48bf 100644 --- a/internal/models/menu/menu.go +++ b/internal/models/menu/menu.go @@ -16,10 +16,10 @@ type Menu struct { func NewMenu(w int, h int) *Menu { opts := map[enums.Page]string{ - enums.HomePage: "Home", - enums.DailyPage: "Daily Tasks", - enums.JournalPage: "Journal", - enums.Quit: "Quit", + enums.HomePage: "Home", + enums.DailyPage: "Daily Tasks", + enums.DiaryPage: "Diary", + enums.Quit: "Quit", } list := NewMenuList(w/3, h/2-1, opts) diff --git a/internal/models/menu/menulist.go b/internal/models/menu/menulist.go index 1efc00c..a3edb22 100644 --- a/internal/models/menu/menulist.go +++ b/internal/models/menu/menulist.go @@ -20,7 +20,7 @@ type MenuList struct { } func NewMenuList(w int, h int, opts map[enums.Page]string) *MenuList { - selections := []enums.Page{enums.HomePage, enums.DailyPage, enums.JournalPage, enums.Quit} + selections := []enums.Page{enums.HomePage, enums.DailyPage, enums.DiaryPage, enums.Quit} return &MenuList{ Width: w, diff --git a/internal/models/rootModel.go b/internal/models/rootModel.go index 0cb597c..ecccc7f 100644 --- a/internal/models/rootModel.go +++ b/internal/models/rootModel.go @@ -6,6 +6,7 @@ import ( "github.com/SourcewareLab/Toney/internal/enums" "github.com/SourcewareLab/Toney/internal/messages" "github.com/SourcewareLab/Toney/internal/models/daily" + "github.com/SourcewareLab/Toney/internal/models/diary" filepopup "github.com/SourcewareLab/Toney/internal/models/filePopup" homemodel "github.com/SourcewareLab/Toney/internal/models/homeModel" "github.com/SourcewareLab/Toney/internal/models/menu" @@ -22,6 +23,7 @@ type RootModel struct { Home *homemodel.HomeModel Menu *menu.Menu Daily *daily.Daily + Diary *diary.Diary CurrentPage enums.Page ShowPopup bool FilePopupType enums.PopupType @@ -60,8 +62,8 @@ func (m *RootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.CurrentPage = enums.HomePage case enums.DailyPage: m.CurrentPage = enums.DailyPage - case enums.JournalPage: - m.CurrentPage = enums.JournalPage + case enums.DiaryPage: + m.CurrentPage = enums.DiaryPage case enums.Quit: return m, tea.Quit } @@ -80,6 +82,10 @@ func (m *RootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.Home.FileExplorer.Update(msg) return m, nil case messages.EditorClose: + if m.CurrentPage == enums.DiaryPage { + m.Diary.Update(msg) + return m, nil + } if msg.Err != nil { fmt.Println(msg.Err.Error()) } @@ -101,10 +107,13 @@ func (m *RootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.Home != nil { // Checking whether this is an app resize or app open m.Menu.Update(msg) m.Home.Update(msg) + m.Diary.Update(msg) + m.Daily.Update(msg) } else { m.Home = homemodel.NewHome(msg.Width, msg.Height) m.Menu = menu.NewMenu(msg.Width, msg.Height) m.Daily = daily.NewDaily(msg.Width, msg.Height) + m.Diary = diary.NewDiary(msg.Width, msg.Height) } m.isLoading = false @@ -124,6 +133,8 @@ func (m *RootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { _, cmd = m.Home.Update(msg) case enums.DailyPage: _, cmd = m.Daily.Update(msg) + case enums.DiaryPage: + _, cmd = m.Diary.Update(msg) default: fmt.Printf("UNHANDLED MSG: %#v\n", msg) } @@ -148,6 +159,8 @@ func (m *RootModel) View() string { return m.Menu.View() case enums.DailyPage: return m.Daily.View() + case enums.DiaryPage: + return m.Diary.View() default: return lipgloss.NewStyle().Background(colors.ColorPalette().Background).Render(m.Home.View()) } From fe412b4f98c61213d8d3da923e5785ca98cf1395 Mon Sep 17 00:00:00 2001 From: NucleoFusion Date: Mon, 21 Jul 2025 16:04:19 +0530 Subject: [PATCH 2/5] adding initial fzf implementation --- internal/keymap/diaryMap.go | 5 ++ internal/models/diary/diary.go | 21 ++++++++- internal/models/fzf/fzf.go | 84 ++++++++++++++++++++++++++++++++++ internal/models/fzf/fzfti.go | 21 +++++++++ internal/models/fzf/fzfvp.go | 39 ++++++++++++++++ 5 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 internal/models/fzf/fzf.go create mode 100644 internal/models/fzf/fzfti.go create mode 100644 internal/models/fzf/fzfvp.go diff --git a/internal/keymap/diaryMap.go b/internal/keymap/diaryMap.go index 8a8a207..d2298a1 100644 --- a/internal/keymap/diaryMap.go +++ b/internal/keymap/diaryMap.go @@ -10,6 +10,7 @@ type DiaryMap struct { Edit key.Binding ScrollUp key.Binding ScrollDown key.Binding + OpenFinder key.Binding } func NewDiaryMap() DiaryMap { @@ -18,6 +19,10 @@ func NewDiaryMap() DiaryMap { key.WithKeys("e"), key.WithHelp("e", "edit"), ), + OpenFinder: key.NewBinding( + key.WithKeys("f"), + key.WithHelp("f", "find"), + ), ScrollUp: key.NewBinding( key.WithKeys("up"), key.WithHelp("up", "scroll up"), diff --git a/internal/models/diary/diary.go b/internal/models/diary/diary.go index 486340e..e0c2081 100644 --- a/internal/models/diary/diary.go +++ b/internal/models/diary/diary.go @@ -11,6 +11,7 @@ import ( "github.com/SourcewareLab/Toney/internal/config" "github.com/SourcewareLab/Toney/internal/keymap" "github.com/SourcewareLab/Toney/internal/messages" + "github.com/SourcewareLab/Toney/internal/models/fzf" "github.com/SourcewareLab/Toney/internal/styles" "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" @@ -26,9 +27,11 @@ type Diary struct { Keymap keymap.DiaryMap DirPath string CurrFileName string + ShowFinder bool CurrDate time.Time Vp viewport.Model Help help.Model + Finder fzf.FuzzyFinder Renderer *glamour.TermRenderer } @@ -52,6 +55,7 @@ func NewDiary(w int, h int) *Diary { Keymap: keymap.NewDiaryMap(), Vp: vp, Help: help.New(), + Finder: fzf.NewFzf([]string{"a", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c"}, w, h), CurrFileName: today, DirPath: dirpath, Renderer: r, @@ -68,6 +72,10 @@ func (m *Diary) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.Refresh() return m, nil case tea.KeyMsg: + if m.ShowFinder { + m.Finder.Update(msg) + return m, nil + } switch { case key.Matches(msg, m.Keymap.Edit): home, _ := os.UserHomeDir() @@ -79,6 +87,9 @@ func (m *Diary) Update(msg tea.Msg) (tea.Model, tea.Cmd) { }) return m, cmd + case key.Matches(msg, m.Keymap.OpenFinder): + m.ShowFinder = true + return m, nil default: var cmd tea.Cmd m.Vp, cmd = m.Vp.Update(msg) @@ -86,11 +97,19 @@ func (m *Diary) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } } var cmd tea.Cmd - m.Vp, cmd = m.Vp.Update(msg) + if m.ShowFinder { + m.Finder, cmd = m.Finder.Update(msg) + } else { + m.Vp, cmd = m.Vp.Update(msg) + } return m, cmd } func (m *Diary) View() string { + if m.ShowFinder { + return m.Finder.View() + } + return lipgloss.JoinVertical(lipgloss.Left, m.Vp.View(), lipgloss.NewStyle().PaddingLeft(2).Render(m.Help.View(keymap.NewDynamic(m.Keymap.Bindings())))) } diff --git a/internal/models/fzf/fzf.go b/internal/models/fzf/fzf.go new file mode 100644 index 0000000..d616a19 --- /dev/null +++ b/internal/models/fzf/fzf.go @@ -0,0 +1,84 @@ +package fzf + +import ( + "github.com/SourcewareLab/Toney/internal/colors" + "github.com/charmbracelet/bubbles/textinput" + "github.com/charmbracelet/bubbles/viewport" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +type FuzzyFinder struct { + Width int + Height int + Vp viewport.Model + Ti textinput.Model + Items []string + Filtered []string + SelectedIndex int +} + +func NewFzf(items []string, w int, h int) FuzzyFinder { + return FuzzyFinder{ + Width: w, + Height: h, + Items: items, + Filtered: items, + Vp: NewVP(w, h, items), + Ti: NewTI(w / 3), + SelectedIndex: 0, + } +} + +func (m *FuzzyFinder) Init() tea.Cmd { + return nil +} + +func (m *FuzzyFinder) Update(msg tea.Msg) (FuzzyFinder, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "down": + if len(m.Filtered)-1 > m.SelectedIndex { + m.SelectedIndex += 1 + } + if m.SelectedIndex > m.Vp.Height+m.Vp.YOffset-1 { + m.Vp.YOffset += 1 + } + m.UpdateVP() + return *m, nil + case "up": + if 0 < m.SelectedIndex { + m.SelectedIndex -= 1 + } + if m.SelectedIndex < m.Vp.YOffset { + m.Vp.YOffset -= 1 + } + m.UpdateVP() + return *m, nil + } + } + + return *m, nil +} + +func (m *FuzzyFinder) View() string { + return lipgloss.Place(m.Width, m.Height, lipgloss.Center, lipgloss.Center, + lipgloss.JoinVertical(lipgloss.Center, + lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(colors.ColorPalette().FocusedBorder).Render(m.Ti.View()), + m.Vp.View())) +} + +func (m *FuzzyFinder) UpdateVP() { + text := "" + for k, v := range m.Filtered { + if k == m.SelectedIndex { + text += SelectedItemStyle(m.Width/3).Render(v) + "\n" + continue + } + + text += ItemLineStyle(m.Width/3).Render(v) + "\n" + } + + m.Vp.SetContent(text) +} diff --git a/internal/models/fzf/fzfti.go b/internal/models/fzf/fzfti.go new file mode 100644 index 0000000..412f13c --- /dev/null +++ b/internal/models/fzf/fzfti.go @@ -0,0 +1,21 @@ +package fzf + +import ( + "github.com/SourcewareLab/Toney/internal/colors" + "github.com/charmbracelet/bubbles/textinput" + "github.com/charmbracelet/lipgloss" +) + +func TextStyle() lipgloss.Style { + return lipgloss.NewStyle().Foreground(colors.ColorPalette().Text) +} + +func NewTI(w int) textinput.Model { + ti := textinput.New() + ti.Placeholder = "Press '/' To Search..." + ti.Prompt = "Filter : " + ti.Width = w - 12 + ti.PromptStyle = TextStyle() + + return ti +} diff --git a/internal/models/fzf/fzfvp.go b/internal/models/fzf/fzfvp.go new file mode 100644 index 0000000..bf24e88 --- /dev/null +++ b/internal/models/fzf/fzfvp.go @@ -0,0 +1,39 @@ +package fzf + +import ( + "github.com/SourcewareLab/Toney/internal/colors" + "github.com/charmbracelet/bubbles/viewport" + "github.com/charmbracelet/lipgloss" +) + +func ItemLineStyle(w int) lipgloss.Style { + return lipgloss.NewStyle().Width(w).Foreground(colors.ColorPalette().Text) +} + +func SelectedItemStyle(w int) lipgloss.Style { + return ItemLineStyle(w).Background(colors.ColorPalette().MenuSelectedBg).Foreground(colors.ColorPalette().MenuSelectedText) +} + +func VPStyle() lipgloss.Style { + return lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(colors.ColorPalette().FocusedBorder). + Foreground(colors.ColorPalette().Text) +} + +func NewVP(w int, h int, items []string) viewport.Model { + vp := viewport.New(w/3, h/2) + vp.Style = VPStyle() + + text := "" + for k, v := range items { + if k == 0 { + text += SelectedItemStyle(w/3).Render(v) + "\n" + continue + } + + text += ItemLineStyle(w/3).Render(v) + "\n" + } + + vp.SetContent(text) + + return vp +} From 857568083a2e3efcbbe271590febffb506a09e64 Mon Sep 17 00:00:00 2001 From: NucleoFusion Date: Mon, 21 Jul 2025 17:58:48 +0530 Subject: [PATCH 3/5] adding complete fzf and diary, refactor for keymap and config left --- go.mod | 1 + go.sum | 33 +++++++++++++++++++++++++ internal/messages/messages.go | 4 +++ internal/models/diary/diary.go | 29 +++++++++++++++++++--- internal/models/filePopup/popup.go | 17 ++++++++----- internal/models/fzf/fzf.go | 39 ++++++++++++++++++++++++++++++ internal/models/fzf/fzfti.go | 1 + internal/models/rootModel.go | 27 +++++++++++++++++++++ 8 files changed, 142 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 042ab4f..4dbc918 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/lithammer/fuzzysearch v1.1.8 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect diff --git a/go.sum b/go.sum index e2c165a..b85321a 100644 --- a/go.sum +++ b/go.sum @@ -71,6 +71,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -125,6 +127,7 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= @@ -136,20 +139,50 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/messages/messages.go b/internal/messages/messages.go index 6e0c2eb..1efe4aa 100644 --- a/internal/messages/messages.go +++ b/internal/messages/messages.go @@ -6,6 +6,10 @@ import ( ) type ( + FzfSelection struct { + Selection string + } + TaskPopupMessage struct { Type enums.TaskPopup IsDeleted bool diff --git a/internal/models/diary/diary.go b/internal/models/diary/diary.go index e0c2081..271a0af 100644 --- a/internal/models/diary/diary.go +++ b/internal/models/diary/diary.go @@ -49,13 +49,15 @@ func NewDiary(w int, h int) *Diary { Foreground(colors.ColorPalette().Text) vp.SetContent(content) + files, _ := AllFiles(dirpath) + return &Diary{ Width: w, Height: h, Keymap: keymap.NewDiaryMap(), Vp: vp, Help: help.New(), - Finder: fzf.NewFzf([]string{"a", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c", "b", "c"}, w, h), + Finder: fzf.NewFzf(files, w, h), CurrFileName: today, DirPath: dirpath, Renderer: r, @@ -68,13 +70,19 @@ func (m *Diary) Init() tea.Cmd { func (m *Diary) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { + case messages.FzfSelection: + m.CurrFileName = msg.Selection + m.Refresh() + m.ShowFinder = false + return m, nil case messages.EditorClose: m.Refresh() return m, nil case tea.KeyMsg: if m.ShowFinder { - m.Finder.Update(msg) - return m, nil + var cmd tea.Cmd + m.Finder, cmd = m.Finder.Update(msg) + return m, cmd } switch { case key.Matches(msg, m.Keymap.Edit): @@ -147,6 +155,21 @@ func ReadDiary(dirpath string, today string) string { return string(data) } +func AllFiles(dir string) ([]string, error) { + home, _ := os.UserHomeDir() + entries, err := os.ReadDir(filepath.Join(home, dir, ".diary")) + if err != nil { + return nil, err + } + var names []string + for _, e := range entries { + if !e.IsDir() { + names = append(names, e.Name()) + } + } + return names, nil +} + func getOrdinalSuffix(day int) string { if day >= 11 && day <= 13 { return "th" diff --git a/internal/models/filePopup/popup.go b/internal/models/filePopup/popup.go index d7c897f..3b22d57 100644 --- a/internal/models/filePopup/popup.go +++ b/internal/models/filePopup/popup.go @@ -14,10 +14,13 @@ import ( "github.com/charmbracelet/lipgloss" ) -var ( - popupStyle = styles.BorderStyle().Align(lipgloss.Left, lipgloss.Top).BorderForeground(colors.ColorPalette().Border) - headerStyle = lipgloss.NewStyle().Background(colors.ColorPalette().Background).Foreground(colors.ColorPalette().Text) -) +func PopupStyle() lipgloss.Style { + return styles.BorderStyle().Align(lipgloss.Left, lipgloss.Top).BorderForeground(colors.ColorPalette().FocusedBorder) +} + +func HeaderStyle() lipgloss.Style { + return lipgloss.NewStyle().Background(colors.ColorPalette().MenuSelectedBg).Foreground(colors.ColorPalette().MenuSelectedText) +} type FilePopup struct { Height int @@ -29,6 +32,8 @@ type FilePopup struct { func NewPopup(typ enums.PopupType, node *filetree.Node) *FilePopup { ti := textinput.New() + ti.TextStyle = lipgloss.NewStyle().Foreground(colors.ColorPalette().Text) + ti.PromptStyle = lipgloss.NewStyle().Foreground(colors.ColorPalette().Text) ti.Focus() return &FilePopup{ @@ -72,7 +77,7 @@ func (m *FilePopup) View() string { w := m.Width / 3 h := m.Height / 3 - return popupStyle.Width(w).Height(h).Render(GetText(m.Type, m.TextInput)) + return PopupStyle().Width(w).Height(h).Render(GetText(m.Type, m.TextInput)) } func GetText(typ enums.PopupType, ti textinput.Model) string { @@ -90,5 +95,5 @@ func GetText(typ enums.PopupType, ti textinput.Model) string { header = "TBD" } - return fmt.Sprintf("%s\n\n%s", headerStyle.Render(header), ti.View()) + return fmt.Sprintf("%s\n\n%s", HeaderStyle().Render(header), ti.View()) } diff --git a/internal/models/fzf/fzf.go b/internal/models/fzf/fzf.go index d616a19..793eb5c 100644 --- a/internal/models/fzf/fzf.go +++ b/internal/models/fzf/fzf.go @@ -1,11 +1,16 @@ package fzf import ( + "fmt" + "sort" + "github.com/SourcewareLab/Toney/internal/colors" + "github.com/SourcewareLab/Toney/internal/messages" "github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/lithammer/fuzzysearch/fuzzy" ) type FuzzyFinder struct { @@ -38,6 +43,9 @@ func (m *FuzzyFinder) Update(msg tea.Msg) (FuzzyFinder, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { + case "/": + m.Ti.Placeholder = "Search for Diary Entries..." + m.Ti.Focus() case "down": if len(m.Filtered)-1 > m.SelectedIndex { m.SelectedIndex += 1 @@ -56,6 +64,22 @@ func (m *FuzzyFinder) Update(msg tea.Msg) (FuzzyFinder, tea.Cmd) { } m.UpdateVP() return *m, nil + case "enter": + fmt.Println("entered") + return *m, func() tea.Msg { + fmt.Println("CALLED IT") + return messages.FzfSelection{ + Selection: m.Filtered[m.SelectedIndex], + } + } + default: + if m.Ti.Focused() { + var cmd tea.Cmd + m.Ti, cmd = m.Ti.Update(msg) + m.Filter(m.Ti.Value()) + m.UpdateVP() + return *m, cmd + } } } @@ -82,3 +106,18 @@ func (m *FuzzyFinder) UpdateVP() { m.Vp.SetContent(text) } + +func (m *FuzzyFinder) Filter(val string) { + ranked := fuzzy.RankFindFold(val, m.Items) + sort.Sort(ranked) // Sort by ascending distance + result := make([]string, len(ranked)) + for i, r := range ranked { + result[i] = r.Target + } + + m.Filtered = result + + if len(m.Filtered) <= m.SelectedIndex { // So that index doesnt go out of bounds + m.SelectedIndex = len(m.Filtered) - 1 + } +} diff --git a/internal/models/fzf/fzfti.go b/internal/models/fzf/fzfti.go index 412f13c..9593d86 100644 --- a/internal/models/fzf/fzfti.go +++ b/internal/models/fzf/fzfti.go @@ -15,6 +15,7 @@ func NewTI(w int) textinput.Model { ti.Placeholder = "Press '/' To Search..." ti.Prompt = "Filter : " ti.Width = w - 12 + ti.TextStyle = TextStyle() ti.PromptStyle = TextStyle() return ti diff --git a/internal/models/rootModel.go b/internal/models/rootModel.go index ecccc7f..de92b49 100644 --- a/internal/models/rootModel.go +++ b/internal/models/rootModel.go @@ -45,6 +45,33 @@ func (m RootModel) Init() tea.Cmd { func (m *RootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { + case messages.FzfSelection: + switch m.CurrentPage { + case enums.MenuPage: + pg, cmd := m.Menu.Update(msg) + if d, ok := pg.(*menu.Menu); ok { // Type matching, cause I cant assign it straightaway + m.Menu = d + return m, cmd + } + case enums.HomePage: + pg, cmd := m.Home.Update(msg) + if d, ok := pg.(*homemodel.HomeModel); ok { // Type matching, cause I cant assign it straightaway + m.Home = d + return m, cmd + } + case enums.DailyPage: + pg, cmd := m.Daily.Update(msg) + if d, ok := pg.(*daily.Daily); ok { // Type matching, cause I cant assign it straightaway + m.Daily = d + return m, cmd + } + case enums.DiaryPage: + pg, cmd := m.Diary.Update(msg) + if d, ok := pg.(*diary.Diary); ok { // Type matching, cause I cant assign it straightaway + m.Diary = d + return m, cmd + } + } case messages.TaskPopupMessage: if m.CurrentPage == enums.DailyPage { dailypage, cmd := m.Daily.Update(msg) From 806585286a93e4e94b10727f0e075d3e2d1a4ba6 Mon Sep 17 00:00:00 2001 From: NucleoFusion Date: Tue, 22 Jul 2025 10:08:24 +0530 Subject: [PATCH 4/5] feat: diary --- docs/docs/.vitepress/config.ts | 4 ++- docs/docs/config/keybinds/diary.md | 40 ++++++++++++++++++++++++++++++ docs/docs/config/keybinds/fuzz.md | 40 ++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 docs/docs/config/keybinds/diary.md create mode 100644 docs/docs/config/keybinds/fuzz.md diff --git a/docs/docs/.vitepress/config.ts b/docs/docs/.vitepress/config.ts index 3101ce9..ca7db97 100644 --- a/docs/docs/.vitepress/config.ts +++ b/docs/docs/.vitepress/config.ts @@ -8,7 +8,7 @@ export default defineConfig({ ['link', { rel: 'icon', href: '/favicon.ico' }], ['meta', { name: 'theme-color', content: '#0f172a' }] ], - appearance: false, + appearance: 'force-dark', base: '/Toney/', themeConfig: { outline: 'deep', @@ -48,6 +48,8 @@ export default defineConfig({ { text: 'Global', link: '/config/keybinds/global' }, { text: 'Home', link: '/config/keybinds/home' }, { text: 'Daily', link: '/config/keybinds/daily' }, + { text: 'Diary', link: '/config/keybinds/diary' }, + { text: 'Fuzzy Finder', link: '/config/keybinds/fuzz' }, ] } ] diff --git a/docs/docs/config/keybinds/diary.md b/docs/docs/config/keybinds/diary.md new file mode 100644 index 0000000..7a6cee3 --- /dev/null +++ b/docs/docs/config/keybinds/diary.md @@ -0,0 +1,40 @@ +# Diary Keybinds + +Denoted by the `[keybinds.diary]` tag in TOML. + +## Edit File + +can change the keybind for editing the file using the `edit` toml key. + +default value is: +```toml +edit = "e" +``` + +## Finder + +can change the keybind for opening the fuzzy finder using the `finder` toml key. + +default value is: +```toml +finder = "f" +``` + +## Scroll Up + +can change the keybind for scrolling up using the `scroll_up` toml key. + +default value is: +```toml +scroll_up = "up" +``` + +## Scroll Down + +can change the keybind for scrolling down using the `scroll_down` toml key. + +default value is: +```toml +scroll_down = "down" +``` + diff --git a/docs/docs/config/keybinds/fuzz.md b/docs/docs/config/keybinds/fuzz.md new file mode 100644 index 0000000..d7efa2b --- /dev/null +++ b/docs/docs/config/keybinds/fuzz.md @@ -0,0 +1,40 @@ +# Fuzzy Finder Keybinds + +Denoted by the `[keybinds.fuzz]` tag in TOML. Usable while in the fuzzy finder menu. + +## Start Writing + +can change the keybind for writing/searching using the `start_writing` toml key. + +default value is: +```toml +start_writing = "/" +``` + +## Enter + +can change the keybind for selecting the item using the `enter` toml key. + +default value is: +```toml +enter = "enter" +``` + +## Up + +can change the keybind for moving up using the `up` toml key. + +default value is: +```toml +up = "up" +``` + +## Down + +can change the keybind for moving down using the `down` toml key. + +default value is: +```toml +down = "down" +``` + From 5c0d3ce8730adfa6aefbdb34608eb64533c10821 Mon Sep 17 00:00:00 2001 From: NucleoFusion Date: Tue, 22 Jul 2025 10:08:55 +0530 Subject: [PATCH 5/5] feat: diary (full) --- internal/config/default.go | 12 +++++++++ internal/config/keybinds.go | 16 +++++++++++ internal/keymap/diaryMap.go | 18 +++++++------ internal/keymap/fuzzyMap.go | 51 ++++++++++++++++++++++++++++++++++++ internal/models/fzf/fzf.go | 24 ++++++++++------- internal/models/fzf/fzfti.go | 5 +++- 6 files changed, 108 insertions(+), 18 deletions(-) create mode 100644 internal/keymap/fuzzyMap.go diff --git a/internal/config/default.go b/internal/config/default.go index 40f3f56..996a041 100644 --- a/internal/config/default.go +++ b/internal/config/default.go @@ -13,6 +13,18 @@ func DefaultConfig() Config { Up: "up", Down: "down", }, + Fuzz: FuzzyKeybinds{ + Up: "up", + Down: "down", + Enter: "enter", + StartWriting: "/", + }, + Diary: DiaryKeybinds{ + ScrollUp: "up", + ScrollDown: "down", + Edit: "e", + Finder: "f", + }, Home: HomeKeybinds{ FocusViewer: "V", FocusExplorer: "F", diff --git a/internal/config/keybinds.go b/internal/config/keybinds.go index ff7486c..a797e8e 100644 --- a/internal/config/keybinds.go +++ b/internal/config/keybinds.go @@ -4,6 +4,22 @@ type KeybindsConfig struct { Global GlobalKeybinds `mapstructure:"global"` Home HomeKeybinds `mapstructure:"home"` Daily DailyKeybinds `mapstructure:"daily"` + Fuzz FuzzyKeybinds `mapstructure:"fuzzy"` // TODO: Add to docs + Diary DiaryKeybinds `mapstructure:"diary"` // TODO: Add to docs +} + +type FuzzyKeybinds struct { + StartWriting string `mapstructure:"start_writing"` + Up string `mapstructure:"up"` + Down string `mapstructure:"down"` + Enter string `mapstructure:"enter"` +} + +type DiaryKeybinds struct { + Edit string `mapstructure:"edit"` + ScrollUp string `mapstructure:"scroll_up"` + ScrollDown string `mapstructure:"scroll_down"` + Finder string `mapstructure:"finder"` } type GlobalKeybinds struct { diff --git a/internal/keymap/diaryMap.go b/internal/keymap/diaryMap.go index d2298a1..df30e6f 100644 --- a/internal/keymap/diaryMap.go +++ b/internal/keymap/diaryMap.go @@ -3,6 +3,7 @@ package keymap import ( "reflect" + "github.com/SourcewareLab/Toney/internal/config" "github.com/charmbracelet/bubbles/key" ) @@ -14,22 +15,23 @@ type DiaryMap struct { } func NewDiaryMap() DiaryMap { + cfg := config.AppConfig.Keybinds.Diary return DiaryMap{ Edit: key.NewBinding( - key.WithKeys("e"), - key.WithHelp("e", "edit"), + key.WithKeys(cfg.Edit), + key.WithHelp(cfg.Edit, "edit"), ), OpenFinder: key.NewBinding( - key.WithKeys("f"), - key.WithHelp("f", "find"), + key.WithKeys(cfg.Finder), + key.WithHelp(cfg.Finder, "find"), ), ScrollUp: key.NewBinding( - key.WithKeys("up"), - key.WithHelp("up", "scroll up"), + key.WithKeys(cfg.ScrollUp), + key.WithHelp(cfg.ScrollUp, "scroll up"), ), ScrollDown: key.NewBinding( - key.WithKeys("down"), - key.WithHelp("down", "scroll down"), + key.WithKeys(cfg.ScrollDown), + key.WithHelp(cfg.ScrollDown, "scroll down"), ), } } diff --git a/internal/keymap/fuzzyMap.go b/internal/keymap/fuzzyMap.go new file mode 100644 index 0000000..50d9d14 --- /dev/null +++ b/internal/keymap/fuzzyMap.go @@ -0,0 +1,51 @@ +package keymap + +import ( + "reflect" + + "github.com/SourcewareLab/Toney/internal/config" + "github.com/charmbracelet/bubbles/key" +) + +type FuzzyMap struct { + StartWriting key.Binding + Up key.Binding + Down key.Binding + Enter key.Binding +} + +func NewFuzzyMap() FuzzyMap { + cfg := config.AppConfig.Keybinds.Fuzz + return FuzzyMap{ + StartWriting: key.NewBinding( + key.WithKeys(cfg.StartWriting), + key.WithHelp(cfg.StartWriting, "start writing"), + ), + Up: key.NewBinding( + key.WithKeys(cfg.Up), + key.WithHelp(cfg.Up, "up"), + ), + Down: key.NewBinding( + key.WithKeys(cfg.Down), + key.WithHelp(cfg.Down, "down"), + ), + Enter: key.NewBinding( + key.WithKeys(cfg.Enter), + key.WithHelp(cfg.Enter, "enter"), + ), + } +} + +func (m FuzzyMap) Bindings() []key.Binding { + var bindings []key.Binding + + v := reflect.ValueOf(m) + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + if binding, ok := field.Interface().(key.Binding); ok { + bindings = append(bindings, binding) + } + } + + return bindings +} diff --git a/internal/models/fzf/fzf.go b/internal/models/fzf/fzf.go index 793eb5c..aa5efbc 100644 --- a/internal/models/fzf/fzf.go +++ b/internal/models/fzf/fzf.go @@ -1,11 +1,13 @@ package fzf import ( - "fmt" "sort" "github.com/SourcewareLab/Toney/internal/colors" + "github.com/SourcewareLab/Toney/internal/keymap" "github.com/SourcewareLab/Toney/internal/messages" + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" @@ -16,6 +18,8 @@ import ( type FuzzyFinder struct { Width int Height int + Keymap keymap.FuzzyMap + Help help.Model Vp viewport.Model Ti textinput.Model Items []string @@ -27,6 +31,8 @@ func NewFzf(items []string, w int, h int) FuzzyFinder { return FuzzyFinder{ Width: w, Height: h, + Help: help.New(), + Keymap: keymap.NewFuzzyMap(), Items: items, Filtered: items, Vp: NewVP(w, h, items), @@ -42,11 +48,11 @@ func (m *FuzzyFinder) Init() tea.Cmd { func (m *FuzzyFinder) Update(msg tea.Msg) (FuzzyFinder, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: - switch msg.String() { - case "/": + switch { + case key.Matches(msg, m.Keymap.StartWriting): m.Ti.Placeholder = "Search for Diary Entries..." m.Ti.Focus() - case "down": + case key.Matches(msg, m.Keymap.Down): if len(m.Filtered)-1 > m.SelectedIndex { m.SelectedIndex += 1 } @@ -55,7 +61,7 @@ func (m *FuzzyFinder) Update(msg tea.Msg) (FuzzyFinder, tea.Cmd) { } m.UpdateVP() return *m, nil - case "up": + case key.Matches(msg, m.Keymap.Up): if 0 < m.SelectedIndex { m.SelectedIndex -= 1 } @@ -64,10 +70,8 @@ func (m *FuzzyFinder) Update(msg tea.Msg) (FuzzyFinder, tea.Cmd) { } m.UpdateVP() return *m, nil - case "enter": - fmt.Println("entered") + case key.Matches(msg, m.Keymap.Enter): return *m, func() tea.Msg { - fmt.Println("CALLED IT") return messages.FzfSelection{ Selection: m.Filtered[m.SelectedIndex], } @@ -87,10 +91,12 @@ func (m *FuzzyFinder) Update(msg tea.Msg) (FuzzyFinder, tea.Cmd) { } func (m *FuzzyFinder) View() string { - return lipgloss.Place(m.Width, m.Height, lipgloss.Center, lipgloss.Center, + pg := lipgloss.Place(m.Width, m.Height-1, lipgloss.Center, lipgloss.Center, lipgloss.JoinVertical(lipgloss.Center, lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(colors.ColorPalette().FocusedBorder).Render(m.Ti.View()), m.Vp.View())) + hlp := lipgloss.NewStyle().PaddingLeft(2).Render(m.Help.View(keymap.NewDynamic(m.Keymap.Bindings()))) + return lipgloss.JoinVertical(lipgloss.Left, pg, hlp) } func (m *FuzzyFinder) UpdateVP() { diff --git a/internal/models/fzf/fzfti.go b/internal/models/fzf/fzfti.go index 9593d86..164ef2b 100644 --- a/internal/models/fzf/fzfti.go +++ b/internal/models/fzf/fzfti.go @@ -1,7 +1,10 @@ package fzf import ( + "fmt" + "github.com/SourcewareLab/Toney/internal/colors" + "github.com/SourcewareLab/Toney/internal/config" "github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/lipgloss" ) @@ -12,7 +15,7 @@ func TextStyle() lipgloss.Style { func NewTI(w int) textinput.Model { ti := textinput.New() - ti.Placeholder = "Press '/' To Search..." + ti.Placeholder = fmt.Sprintf("Press '%s' To Search...", config.AppConfig.Keybinds.Fuzz.StartWriting) ti.Prompt = "Filter : " ti.Width = w - 12 ti.TextStyle = TextStyle()