feat: add datepicker dependency and init helper

Claude-Session: https://claude.ai/code/session_01BA1doywpQD49rP1vezTe9U
This commit is contained in:
Taybin Rutkin 2026-07-21 15:43:31 -04:00
parent 7bc00d6891
commit 04676f1bab
No known key found for this signature in database
GPG key ID: 605FA2C570B6AC9E
5 changed files with 68 additions and 1 deletions

3
go.mod
View file

@ -19,6 +19,7 @@ require (
github.com/charmbracelet/x/term v0.2.1 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/ethanefung/bubble-datepicker v0.1.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-localereader v0.0.1 // indirect
@ -29,5 +30,5 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sys v0.36.0 // indirect golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.3.8 // indirect golang.org/x/text v0.13.0 // indirect
) )

4
go.sum
View file

@ -34,6 +34,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/ethanefung/bubble-datepicker v0.1.1 h1:+12ZTE4ANZ2cAgYURXzDwG0z+rDMwo4Ljfxhs5B0okc=
github.com/ethanefung/bubble-datepicker v0.1.1/go.mod h1:8nxOYB9Oqays5U0JHKcIsbT7ZP/TwuJz8Uju9n5ueVU=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
@ -63,3 +65,5 @@ golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=

View file

@ -8,6 +8,7 @@ import (
"github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/bubbles/textarea"
"github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
datepicker "github.com/ethanefung/bubble-datepicker"
"github.com/rwejlgaard/org/internal/config" "github.com/rwejlgaard/org/internal/config"
"github.com/rwejlgaard/org/internal/model" "github.com/rwejlgaard/org/internal/model"
) )
@ -54,6 +55,8 @@ type uiModel struct {
settingsScroll int // Scroll position in settings view settingsScroll int // Scroll position in settings view
settingsSection settingsSection // Current settings section/tab settingsSection settingsSection // Current settings section/tab
captureCursor int // Store cursor position when entering capture mode captureCursor int // Store cursor position when entering capture mode
datepicker datepicker.Model
dateTextFocused bool // within set-date modes: text field focused vs. calendar
} }
func InitialModel(orgFile *model.OrgFile, cfg *config.Config, captureMode bool, captureText string) uiModel { func InitialModel(orgFile *model.OrgFile, cfg *config.Config, captureMode bool, captureText string) uiModel {

View file

@ -0,0 +1,43 @@
package ui
import (
"testing"
"time"
tea "github.com/charmbracelet/bubbletea"
datepicker "github.com/ethanefung/bubble-datepicker"
)
func sameDay(a, b time.Time) bool {
return a.Year() == b.Year() && a.Month() == b.Month() && a.Day() == b.Day()
}
func TestNewDatePickerDefaultsToToday(t *testing.T) {
dp := newDatePicker(nil)
if !sameDay(dp.Time, time.Now()) {
t.Fatalf("expected today, got %v", dp.Time)
}
if !dp.Selected {
t.Fatal("expected date to be selected")
}
if dp.Focused != datepicker.FocusCalendar {
t.Fatalf("expected FocusCalendar, got %v", dp.Focused)
}
// Quit binding must be disabled so 'q' does not quit the app from the calendar.
if dp.KeyMap.Quit.Enabled() && len(dp.KeyMap.Quit.Keys()) > 0 {
t.Fatalf("expected Quit binding disabled, got keys %v", dp.KeyMap.Quit.Keys())
}
// Forwarding a 'q' key must not produce tea.Quit.
_, cmd := dp.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'q'}})
if cmd != nil {
t.Fatal("expected no command from 'q' after disabling Quit")
}
}
func TestNewDatePickerUsesExisting(t *testing.T) {
existing := time.Date(2027, time.March, 9, 0, 0, 0, 0, time.Local)
dp := newDatePicker(&existing)
if !sameDay(dp.Time, existing) {
t.Fatalf("expected %v, got %v", existing, dp.Time)
}
}

View file

@ -9,6 +9,7 @@ import (
"github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/bubbles/textarea"
"github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
datepicker "github.com/ethanefung/bubble-datepicker"
"github.com/rwejlgaard/org/internal/model" "github.com/rwejlgaard/org/internal/model"
"github.com/rwejlgaard/org/internal/parser" "github.com/rwejlgaard/org/internal/parser"
) )
@ -573,6 +574,21 @@ func (m uiModel) updateAddSubTask(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, cmd return m, cmd
} }
// newDatePicker returns a calendar-focused datepicker seeded with existing (if
// non-nil) or today. The Quit binding is cleared so 'q'/ctrl+c do not quit the
// app while the calendar has focus.
func newDatePicker(existing *time.Time) datepicker.Model {
t := time.Now()
if existing != nil {
t = *existing
}
dp := datepicker.New(t)
dp.SelectDate()
dp.SetFocus(datepicker.FocusCalendar)
dp.KeyMap.Quit = key.Binding{}
return dp
}
func (m uiModel) updateSetDeadline(msg tea.Msg) (tea.Model, tea.Cmd) { func (m uiModel) updateSetDeadline(msg tea.Msg) (tea.Model, tea.Cmd) {
return m.updateSetDate(msg, "DEADLINE") return m.updateSetDate(msg, "DEADLINE")
} }