Skip to content

Commit 7d879b2

Browse files
feat: implement robust configuration system and initial application structure for version 1.5.4
- closes #34
1 parent 8923c16 commit 7d879b2

9 files changed

Lines changed: 76 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.5.4]
9+
10+
### Added
11+
- Minimal Due Text Mode: A new toggleable field mode that abbreviates "overdue" to "OD" and uses a fixed-width column for perfect task list alignment.
12+
- Configuration for due field: Added `[list.fields.due] minimal = true` to `config.toml`.
13+
- Settings Toggle: Added "Minimal Due Text" toggle to the settings menu (ctrl+s) for live configuration.
14+
15+
### Improved
16+
- Task List Alignment: When minimal mode is enabled, the due column width is automatically calculated across all visible tasks to ensure consistent left-alignment.
17+
818
## [1.5.3]
919

1020
### Fixed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ right = ["tags", "due", "priority"]
218218
Valid values for `right` are: `tags`, `due`, `priority`.
219219

220220
### Task Fields
221+
* **Minimal Due Mode**: Abbreviate "overdue" to "OD" and use a fixed-width column for consistent task list alignment. Enabled by default.
222+
```toml
223+
[list.fields.due]
224+
minimal = true
225+
```
221226
* **wait_until**: Hide a task from the task list until the specified datetime. If the task is recurring, new instances are not generated/shown until `wait_until` has passed. Format: `yyyy-MM-dd HH:mm`.
222227
* **until**: Stop generating new recurring instances after the specified datetime. Existing instances may remain visible. Format: `yyyy-MM-dd HH:mm`.
223228

VERSION.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.5.3
1+
1.5.4

configs/kairo.example.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ onboarding_completed = false
3333
# Valid values: "tags", "due", "priority"
3434
right = ["tags", "due", "priority"]
3535

36+
[list.fields.due]
37+
# Enable minimal mode for the due date field (default: true).
38+
# Abbreviates "overdue" to "OD" and aligns the column.
39+
minimal = true
40+
3641
[theme]
3742
# Any field set here overrides the built-in theme colors.
3843
# bg = "#0B0F14"

internal/app/model.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ func New(ctx context.Context, cfg config.Config, svc service.TaskService) (tea.M
240240
tagFilterInput: tagInput,
241241
RainbowAnimationOffset: 0,
242242
}
243-
m.list = tasklist.New(m.s, cfg.App.VimMode, cfg.App.Animations, m.km)
243+
m.list = tasklist.New(m.s, cfg.App.VimMode, cfg.App.Animations, m.km, cfg.List.Fields.Due.Minimal)
244244
m.list.SetRightOrder(cfg.List.Order.Right)
245245
m.pal = palette.New(m.s)
246246
m.det = detail.New(m.s)
@@ -2280,7 +2280,7 @@ func (m *Model) fetchOpenEditCmd(id string) tea.Cmd {
22802280

22812281
func (m *Model) refreshStyles() {
22822282
m.s = styles.New(m.theme)
2283-
m.list = tasklist.New(m.s, m.cfg.App.VimMode, m.cfg.App.Animations, m.km)
2283+
m.list = tasklist.New(m.s, m.cfg.App.VimMode, m.cfg.App.Animations, m.km, m.cfg.List.Fields.Due.Minimal)
22842284
m.list.SetRightOrder(m.cfg.List.Order.Right)
22852285
m.list.SetTasks(m.tasks)
22862286
m.list.SetAllTasks(m.all)

internal/config/config.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,16 @@ type Config struct {
2323
}
2424

2525
type ListConfig struct {
26-
Order ListOrderConfig `toml:"order"`
26+
Order ListOrderConfig `toml:"order"`
27+
Fields ListFieldsConfig `toml:"fields"`
28+
}
29+
30+
type ListFieldsConfig struct {
31+
Due DueFieldConfig `toml:"due"`
32+
}
33+
34+
type DueFieldConfig struct {
35+
Minimal bool `toml:"minimal"`
2736
}
2837

2938
type ListOrderConfig struct {
@@ -152,6 +161,11 @@ func Default() Config {
152161
Order: ListOrderConfig{
153162
Right: []string{"tags", "due", "priority"},
154163
},
164+
Fields: ListFieldsConfig{
165+
Due: DueFieldConfig{
166+
Minimal: true,
167+
},
168+
},
155169
},
156170
Theme: ThemeConfig{
157171
Bg: "", // Use theme default

internal/lua/engine.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func (e *Engine) SetupKairoAPI(L *lua.LState) {
6161
L.SetField(kairo, "notify", L.NewFunction(e.luaNotify))
6262

6363
// Meta
64-
L.SetField(kairo, "version", lua.LString("1.5.3"))
64+
L.SetField(kairo, "version", lua.LString("1.5.4"))
6565

6666
// Set as global
6767
L.SetGlobal("kairo", kairo)

internal/ui/settings/model.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func (m *Model) rebuildItems() {
5757
{"Auto Push (Git)", "auto_push", "bool", m.cfg.Sync.AutoPush},
5858
{"MCP Server Enabled", "mcp_enabled", "bool", m.cfg.App.MCPEnabled},
5959
{"Animations", "animations", "bool", m.cfg.App.Animations},
60+
{"Minimal Due Text", "minimal_due", "bool", m.cfg.List.Fields.Due.Minimal},
6061
{"AI Model (←/→)", "ai_model", "enum", m.cfg.App.AIModel},
6162
{"Gemini API Key", "gemini_api_key", "string", m.cfg.App.GeminiAPIKey},
6263
{"AI Assistant Shortcut", "ai_toggle", "string", m.cfg.Keymap.AIPanelToggle},
@@ -186,6 +187,8 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
186187
m.cfg.App.MCPEnabled = val
187188
case "animations":
188189
m.cfg.App.Animations = val
190+
case "minimal_due":
191+
m.cfg.List.Fields.Due.Minimal = val
189192
}
190193
m.rebuildItems()
191194
// Save config immediately and notify app

internal/ui/tasklist/model.go

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,19 @@ type Model struct {
5050
DeletingTaskID string
5151
DeleteProgress float64
5252

53+
DueMinimal bool
54+
5355
lastKey string // For tracking key sequences like 'gg'
5456
}
5557

56-
func New(s styles.Styles, vimMode bool, animations bool, km keymap.Keymap) Model {
58+
func New(s styles.Styles, vimMode bool, animations bool, km keymap.Keymap, dueMinimal bool) Model {
5759
return Model{
5860
styles: s,
5961
vimMode: vimMode,
6062
Animations: animations,
6163
km: km,
6264
rightOrder: []string{"tags", "due", "priority"},
65+
DueMinimal: dueMinimal,
6366
}
6467
}
6568

@@ -279,6 +282,21 @@ func (m Model) View() string {
279282
start := clamp(m.sel-visible/2, 0, max(0, len(m.items)-visible))
280283
end := min(len(m.items), start+visible)
281284

285+
maxDueWidth := 0
286+
if m.DueMinimal {
287+
for i := start; i < end; i++ {
288+
item := m.items[i]
289+
if item.Deadline != nil {
290+
d := humanDeadline(*item.Deadline, time.Now())
291+
d = formatDue(d, true)
292+
w := lipgloss.Width(styles.IconDeadline + d)
293+
if w > maxDueWidth {
294+
maxDueWidth = w
295+
}
296+
}
297+
}
298+
}
299+
282300
lines := make([]string, 0, visible)
283301
for i := start; i < end; i++ {
284302
item := m.items[i]
@@ -298,7 +316,7 @@ func (m Model) View() string {
298316
}
299317
}
300318

301-
line := m.renderRow(item, i == m.sel)
319+
line := m.renderRow(item, i == m.sel, maxDueWidth)
302320
lines = append(lines, line)
303321
}
304322

@@ -392,7 +410,7 @@ func (m Model) renderEmpty() string {
392410
Align(lipgloss.Center, lipgloss.Center).
393411
Render(m.styles.Overlay.Width(boxWidth).Padding(2).Render(dashboard))
394412
}
395-
func (m Model) renderRow(item TaskItem, selected bool) string {
413+
func (m Model) renderRow(item TaskItem, selected bool, maxDueWidth int) string {
396414
t := item.Task
397415
// Compute animation progress for strike (completion toggle).
398416
// Progress is always clamped to [0, 1] — no overshoot.
@@ -544,7 +562,7 @@ func (m Model) renderRow(item TaskItem, selected bool) string {
544562
deadStyleColor = m.styles.Theme.Bad
545563
}
546564

547-
dueContent := styles.IconDeadline + deadText
565+
dueContent := styles.IconDeadline + formatDue(deadText, m.DueMinimal)
548566

549567
// Create pill badge for due date
550568
badge := m.styles.BadgeMuted.
@@ -557,6 +575,11 @@ func (m Model) renderRow(item TaskItem, selected bool) string {
557575
badge.Background(deadStyleColor).Render(dueContent),
558576
m.styles.TagRight.Foreground(deadStyleColor).Render(),
559577
)
578+
579+
if m.DueMinimal && maxDueWidth > 0 {
580+
// container width = max content width + padding (2) + caps (2)
581+
pill = lipgloss.NewStyle().Width(maxDueWidth + 4).Align(lipgloss.Left).Render(pill)
582+
}
560583
rightParts = append(rightParts, pill)
561584
}
562585
case "tags":
@@ -673,6 +696,13 @@ func humanDeadline(t time.Time, now time.Time) string {
673696
return fmt.Sprintf("%dd", int(d.Hours()/24))
674697
}
675698

699+
func formatDue(due string, minimal bool) string {
700+
if !minimal {
701+
return due
702+
}
703+
return strings.ReplaceAll(due, "overdue", "OD")
704+
}
705+
676706
func clamp(x, lo, hi int) int {
677707
if x < lo {
678708
return lo

0 commit comments

Comments
 (0)