adding setting for default state of new items

This commit is contained in:
Rasmus Wejlgaard 2025-11-08 18:36:21 +00:00
parent 43573a6e79
commit 3c7b64417b
3 changed files with 129 additions and 23 deletions

View file

@ -82,7 +82,8 @@ type StateConfig struct {
// StatesConfig holds TODO state configurations // StatesConfig holds TODO state configurations
type StatesConfig struct { type StatesConfig struct {
States []StateConfig `toml:"states"` States []StateConfig `toml:"states"`
DefaultNewTaskState string `toml:"default_new_task_state"`
} }
// UIConfig holds UI-related configurations // UIConfig holds UI-related configurations
@ -151,6 +152,7 @@ func DefaultConfig() *Config {
{Name: "BLOCK", Color: "196"}, {Name: "BLOCK", Color: "196"},
{Name: "DONE", Color: "34"}, {Name: "DONE", Color: "34"},
}, },
DefaultNewTaskState: "TODO",
}, },
UI: UIConfig{ UI: UIConfig{
HelpTextWidth: 22, HelpTextWidth: 22,
@ -355,6 +357,9 @@ func (c *Config) fillDefaults() {
if len(c.States.States) == 0 { if len(c.States.States) == 0 {
c.States.States = defaults.States.States c.States.States = defaults.States.States
} }
if c.States.DefaultNewTaskState == "" {
c.States.DefaultNewTaskState = defaults.States.DefaultNewTaskState
}
// Fill UI if zero values // Fill UI if zero values
if c.UI.HelpTextWidth == 0 { if c.UI.HelpTextWidth == 0 {
@ -542,3 +547,26 @@ func (c *Config) GetAllKeybindings() map[string][]string {
"tag_item": c.Keybindings.TagItem, "tag_item": c.Keybindings.TagItem,
} }
} }
// GetDefaultNewTaskState returns the default state for new tasks
// Returns empty string if configured as "none" or if the configured state doesn't exist
func (c *Config) GetDefaultNewTaskState() string {
// Empty string means no state
if c.States.DefaultNewTaskState == "" {
return ""
}
// Validate that the configured state exists
for _, state := range c.States.States {
if state.Name == c.States.DefaultNewTaskState {
return c.States.DefaultNewTaskState
}
}
// If configured state doesn't exist, fall back to first state or empty
if len(c.States.States) > 0 {
return c.States.States[0].Name
}
return ""
}

View file

@ -345,10 +345,13 @@ func (m uiModel) updateCapture(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyEnter: case tea.KeyEnter:
title := strings.TrimSpace(m.textinput.Value()) title := strings.TrimSpace(m.textinput.Value())
if title != "" { if title != "" {
// Get default state from config
defaultState := model.TodoState(m.config.GetDefaultNewTaskState())
// Create new TODO at top level // Create new TODO at top level
newItem := &model.Item{ newItem := &model.Item{
Level: 1, Level: 1,
State: model.StateTODO, State: defaultState,
Title: title, Title: title,
Notes: []string{}, Notes: []string{},
Children: []*model.Item{}, Children: []*model.Item{},
@ -387,10 +390,13 @@ func (m uiModel) updateAddSubTask(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyEnter: case tea.KeyEnter:
title := strings.TrimSpace(m.textinput.Value()) title := strings.TrimSpace(m.textinput.Value())
if title != "" && m.editingItem != nil { if title != "" && m.editingItem != nil {
// Get default state from config
defaultState := model.TodoState(m.config.GetDefaultNewTaskState())
// Create new sub-task // Create new sub-task
newItem := &model.Item{ newItem := &model.Item{
Level: m.editingItem.Level + 1, Level: m.editingItem.Level + 1,
State: model.StateTODO, State: defaultState,
Title: title, Title: title,
Notes: []string{}, Notes: []string{},
Children: []*model.Item{}, Children: []*model.Item{},

View file

@ -140,7 +140,7 @@ func (m *uiModel) getSettingsItemCount() int {
case settingsSectionTags: case settingsSectionTags:
return len(m.config.Tags.Tags) + 1 // +1 for "Add new tag" option return len(m.config.Tags.Tags) + 1 // +1 for "Add new tag" option
case settingsSectionStates: case settingsSectionStates:
return len(m.config.States.States) + 1 // +1 for "Add new state" option return len(m.config.States.States) + 2 // +1 for "Default new task state" setting, +1 for "Add new state" option
case settingsSectionKeybindings: case settingsSectionKeybindings:
return len(m.config.GetAllKeybindings()) return len(m.config.GetAllKeybindings())
default: default:
@ -161,10 +161,20 @@ func (m *uiModel) startSettingsEdit() {
m.textinput.Focus() m.textinput.Focus()
case settingsSectionStates: case settingsSectionStates:
if m.settingsCursor >= len(m.config.States.States) { // First item is the default new task state setting
if m.settingsCursor == 0 {
m.textinput.SetValue(m.config.States.DefaultNewTaskState)
m.textinput.Placeholder = "Enter state name or leave empty for none"
m.textinput.Focus()
return return
} }
state := m.config.States.States[m.settingsCursor]
// Adjust for the default state setting offset
stateIndex := m.settingsCursor - 1
if stateIndex >= len(m.config.States.States) {
return
}
state := m.config.States.States[stateIndex]
m.textinput.SetValue(state.Name + "," + state.Color) m.textinput.SetValue(state.Name + "," + state.Color)
m.textinput.Placeholder = "name,color (e.g., TODO,202)" m.textinput.Placeholder = "name,color (e.g., TODO,202)"
m.textinput.Focus() m.textinput.Focus()
@ -223,13 +233,33 @@ func (m *uiModel) saveSettingsEdit() {
} }
case settingsSectionStates: case settingsSectionStates:
if m.settingsCursor >= len(m.config.States.States) { // First item is the default new task state setting
if m.settingsCursor == 0 {
newDefault := strings.TrimSpace(m.textinput.Value())
// Convert to uppercase
newDefault = strings.ToUpper(newDefault)
m.config.States.DefaultNewTaskState = newDefault
if newDefault == "" {
m.setStatus("Default new task state set to 'none' (saved)")
} else {
m.setStatus(fmt.Sprintf("Default new task state set to '%s' (saved)", newDefault))
}
// Auto-save
if err := m.config.Save(); err != nil {
m.setStatus(fmt.Sprintf("Error auto-saving config: %v", err))
}
return
}
// Adjust for the default state setting offset
stateIndex := m.settingsCursor - 1
if stateIndex >= len(m.config.States.States) {
return return
} }
// Parse "name,color" format // Parse "name,color" format
parts := strings.Split(m.textinput.Value(), ",") parts := strings.Split(m.textinput.Value(), ",")
if len(parts) >= 2 { if len(parts) >= 2 {
state := &m.config.States.States[m.settingsCursor] state := &m.config.States.States[stateIndex]
state.Name = strings.TrimSpace(parts[0]) state.Name = strings.TrimSpace(parts[0])
state.Color = strings.TrimSpace(parts[1]) state.Color = strings.TrimSpace(parts[1])
m.setStatus(fmt.Sprintf("Updated state '%s' (saved)", state.Name)) m.setStatus(fmt.Sprintf("Updated state '%s' (saved)", state.Name))
@ -316,18 +346,27 @@ func (m *uiModel) deleteSettingsItem() {
} }
case settingsSectionStates: case settingsSectionStates:
if m.settingsCursor >= len(m.config.States.States) { // Cannot delete the default new task state setting (first item)
if m.settingsCursor == 0 {
m.setStatus("Cannot delete default state setting (use Enter to edit)")
return return
} }
state := m.config.States.States[m.settingsCursor]
// Adjust for the default state setting offset
stateIndex := m.settingsCursor - 1
if stateIndex >= len(m.config.States.States) {
return
}
state := m.config.States.States[stateIndex]
m.config.RemoveState(state.Name) m.config.RemoveState(state.Name)
m.setStatus(fmt.Sprintf("Deleted state '%s' (saved)", state.Name)) m.setStatus(fmt.Sprintf("Deleted state '%s' (saved)", state.Name))
// Adjust cursor if needed // Adjust cursor if needed
if m.settingsCursor >= len(m.config.States.States) { // +1 for the default state setting
m.settingsCursor = len(m.config.States.States) - 1 if m.settingsCursor >= len(m.config.States.States)+1 {
if m.settingsCursor < 0 { m.settingsCursor = len(m.config.States.States)
m.settingsCursor = 0 if m.settingsCursor < 1 {
m.settingsCursor = 1
} }
} }
@ -465,11 +504,30 @@ func (m *uiModel) viewSettingsTags() string {
func (m *uiModel) viewSettingsStates() string { func (m *uiModel) viewSettingsStates() string {
var content strings.Builder var content strings.Builder
// First show the default new task state setting
line := ""
if m.settingsCursor == 0 && !m.textinput.Focused() {
line += "▶ "
} else {
line += " "
}
line += "Default new task state: "
if m.config.States.DefaultNewTaskState == "" {
line += m.styles.statusStyle.Render("(none)")
} else {
// Try to get the color for this state
color := m.config.GetStateColor(m.config.States.DefaultNewTaskState)
stateStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(color))
line += stateStyle.Render(m.config.States.DefaultNewTaskState)
}
content.WriteString(line + "\n\n")
// Then show all configured states
for i, state := range m.config.States.States { for i, state := range m.config.States.States {
line := "" line := ""
// Cursor // Cursor (offset by 1 because of the default state setting)
if i == m.settingsCursor && !m.textinput.Focused() { if i+1 == m.settingsCursor && !m.textinput.Focused() {
line += "▶ " line += "▶ "
} else { } else {
line += " " line += " "
@ -484,7 +542,7 @@ func (m *uiModel) viewSettingsStates() string {
} }
// Add new state option // Add new state option
if m.settingsCursor == len(m.config.States.States) && !m.textinput.Focused() { if m.settingsCursor == len(m.config.States.States)+1 && !m.textinput.Focused() {
content.WriteString("▶ ") content.WriteString("▶ ")
} else { } else {
content.WriteString(" ") content.WriteString(" ")
@ -666,10 +724,17 @@ func (m *uiModel) moveSettingsItemUp() {
} }
case settingsSectionStates: case settingsSectionStates:
if m.settingsCursor > 0 && m.settingsCursor < len(m.config.States.States) { // Cannot reorder the default state setting (first item)
if m.settingsCursor <= 1 {
return
}
// Adjust for the default state setting offset
stateIndex := m.settingsCursor - 1
if stateIndex > 0 && stateIndex < len(m.config.States.States) {
// Swap with previous item // Swap with previous item
m.config.States.States[m.settingsCursor], m.config.States.States[m.settingsCursor-1] = m.config.States.States[stateIndex], m.config.States.States[stateIndex-1] =
m.config.States.States[m.settingsCursor-1], m.config.States.States[m.settingsCursor] m.config.States.States[stateIndex-1], m.config.States.States[stateIndex]
m.settingsCursor-- m.settingsCursor--
// Auto-save // Auto-save
if err := m.config.Save(); err != nil { if err := m.config.Save(); err != nil {
@ -703,10 +768,17 @@ func (m *uiModel) moveSettingsItemDown() {
} }
case settingsSectionStates: case settingsSectionStates:
if m.settingsCursor >= 0 && m.settingsCursor < len(m.config.States.States)-1 { // Cannot reorder the default state setting (first item)
if m.settingsCursor == 0 {
return
}
// Adjust for the default state setting offset
stateIndex := m.settingsCursor - 1
if stateIndex >= 0 && stateIndex < len(m.config.States.States)-1 {
// Swap with next item // Swap with next item
m.config.States.States[m.settingsCursor], m.config.States.States[m.settingsCursor+1] = m.config.States.States[stateIndex], m.config.States.States[stateIndex+1] =
m.config.States.States[m.settingsCursor+1], m.config.States.States[m.settingsCursor] m.config.States.States[stateIndex+1], m.config.States.States[stateIndex]
m.settingsCursor++ m.settingsCursor++
// Auto-save // Auto-save
if err := m.config.Save(); err != nil { if err := m.config.Save(); err != nil {