Add fuzzy matching for time tags

This commit is contained in:
Martin Pander
2026-02-02 15:41:53 +01:00
parent 81b9d87935
commit 938ed177f1
5 changed files with 577 additions and 32 deletions

View File

@ -4,11 +4,11 @@ import (
"log/slog"
"strings"
"tasksquire/common"
"tasksquire/components/autocomplete"
"tasksquire/components/timestampeditor"
"tasksquire/timewarrior"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
@ -20,7 +20,7 @@ type TimeEditorPage struct {
// Fields
startEditor *timestampeditor.TimestampEditor
endEditor *timestampeditor.TimestampEditor
tagsInput textinput.Model
tagsInput *autocomplete.Autocomplete
adjust bool
// State
@ -39,11 +39,12 @@ func NewTimeEditorPage(com *common.Common, interval *timewarrior.Interval) *Time
Title("End").
ValueFromString(interval.End)
// Create tags input
tagsInput := textinput.New()
tagsInput.Placeholder = "Space separated, use \"\" for tags with spaces"
// Create tags autocomplete with combinations from past intervals
tagCombinations := com.TimeW.GetTagCombinations()
tagsInput := autocomplete.New(tagCombinations, 3)
tagsInput.SetPlaceholder("Space separated, use \"\" for tags with spaces")
tagsInput.SetValue(formatTags(interval.Tags))
tagsInput.Width = 50
tagsInput.SetWidth(50)
p := &TimeEditorPage{
common: com,
@ -60,9 +61,10 @@ func NewTimeEditorPage(com *common.Common, interval *timewarrior.Interval) *Time
}
func (p *TimeEditorPage) Init() tea.Cmd {
// Focus the first field
// Focus the first field (tags)
p.currentField = 0
return p.startEditor.Focus()
p.tagsInput.Focus()
return p.tagsInput.Init()
}
func (p *TimeEditorPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
@ -110,19 +112,23 @@ func (p *TimeEditorPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
switch p.currentField {
case 0:
var model tea.Model
model, cmd = p.tagsInput.Update(msg)
if ac, ok := model.(*autocomplete.Autocomplete); ok {
p.tagsInput = ac
}
case 1:
var model tea.Model
model, cmd = p.startEditor.Update(msg)
if editor, ok := model.(*timestampeditor.TimestampEditor); ok {
p.startEditor = editor
}
case 1:
case 2:
var model tea.Model
model, cmd = p.endEditor.Update(msg)
if editor, ok := model.(*timestampeditor.TimestampEditor); ok {
p.endEditor = editor
}
case 2:
p.tagsInput, cmd = p.tagsInput.Update(msg)
case 3:
// Handle adjust toggle with space/enter
if msg, ok := msg.(tea.KeyMsg); ok {
@ -139,12 +145,12 @@ func (p *TimeEditorPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (p *TimeEditorPage) focusCurrentField() tea.Cmd {
switch p.currentField {
case 0:
return p.startEditor.Focus()
case 1:
return p.endEditor.Focus()
case 2:
p.tagsInput.Focus()
return nil
return p.tagsInput.Init()
case 1:
return p.startEditor.Focus()
case 2:
return p.endEditor.Focus()
case 3:
// Adjust checkbox doesn't need focus action
return nil
@ -155,11 +161,11 @@ func (p *TimeEditorPage) focusCurrentField() tea.Cmd {
func (p *TimeEditorPage) blurCurrentField() {
switch p.currentField {
case 0:
p.startEditor.Blur()
case 1:
p.endEditor.Blur()
case 2:
p.tagsInput.Blur()
case 1:
p.startEditor.Blur()
case 2:
p.endEditor.Blur()
case 3:
// Adjust checkbox doesn't need blur action
}
@ -173,18 +179,10 @@ func (p *TimeEditorPage) View() string {
sections = append(sections, titleStyle.Render("Edit Time Interval"))
sections = append(sections, "")
// Start editor
sections = append(sections, p.startEditor.View())
sections = append(sections, "")
// End editor
sections = append(sections, p.endEditor.View())
sections = append(sections, "")
// Tags input
// Tags input (now first)
tagsLabelStyle := p.common.Styles.Form.Focused.Title
tagsLabel := tagsLabelStyle.Render("Tags")
if p.currentField == 2 {
if p.currentField == 0 {
sections = append(sections, tagsLabel)
sections = append(sections, p.tagsInput.View())
descStyle := p.common.Styles.Form.Focused.Description
@ -192,12 +190,20 @@ func (p *TimeEditorPage) View() string {
} else {
blurredLabelStyle := p.common.Styles.Form.Blurred.Title
sections = append(sections, blurredLabelStyle.Render("Tags"))
sections = append(sections, lipgloss.NewStyle().Faint(true).Render(p.tagsInput.Value()))
sections = append(sections, lipgloss.NewStyle().Faint(true).Render(p.tagsInput.GetValue()))
}
sections = append(sections, "")
sections = append(sections, "")
// Start editor
sections = append(sections, p.startEditor.View())
sections = append(sections, "")
// End editor
sections = append(sections, p.endEditor.View())
sections = append(sections, "")
// Adjust checkbox
adjustLabelStyle := p.common.Styles.Form.Focused.Title
adjustLabel := adjustLabelStyle.Render("Adjust overlaps")
@ -249,7 +255,7 @@ func (p *TimeEditorPage) saveInterval() {
p.interval.End = p.endEditor.GetValueString()
// Parse tags
p.interval.Tags = parseTags(p.tagsInput.Value())
p.interval.Tags = parseTags(p.tagsInput.GetValue())
err := p.common.TimeW.ModifyInterval(p.interval, p.adjust)
if err != nil {