Skip to content

Commit 7017666

Browse files
feat: v1.7.3 - local config storage, editable OI field, keybinding fix
- Move config/storage from AppData to local .kairo/ directory - Add editable Open Issue ID field in task editor - Add OpenIssueID to TaskPatch for programmatic updates - Fix tag view keybinding display in footer (f -> ctrl+f) - Update README config path docs - Bump version to 1.7.3
1 parent 9f5da75 commit 7017666

8 files changed

Lines changed: 44 additions & 21 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.7.3] (2026-06-25)
9+
10+
### Added
11+
- **Open Issue ID Editor**: The task editor now includes an editable `Issue ID` field, allowing users to modify the open issue identifier (e.g., `OI00008` to `OI102872`).
12+
- **Local Config Storage**: All configuration, database, and plugin data is now stored in a `.kairo` directory in the current working directory instead of `%APPDATA%\kairo` on Windows (or `~/.config/kairo` on Unix). This keeps everything self-contained in one folder.
13+
14+
### Fixed
15+
- **Tag View Keybinding Display**: The footer now correctly shows `ctrl+f` instead of `f` for the tag view action, matching the actual keybinding.
16+
- **Focus Mode Keybinding**: The `f` key now exclusively triggers focus mode without conflicting with the tag view.
17+
818
## [1.7.2] (2026-06-10)
919

1020
### Added

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,8 @@ minimal = true
273273

274274
Auto-generated on first run at:
275275

276-
* **Linux:** `~/.config/kairo/config.toml`
277-
* **macOS:** `~/Library/Application Support/kairo/config.toml`
278-
* **Windows:** `%APPDATA%\kairo\config.toml`
276+
* **All platforms:** `./.kairo/config.toml` (relative to the working directory)
277+
* *Legacy fallbacks:* `~/.kairo/config.toml` and `~/.config/kairo/config.toml`
279278

280279
| Option | Description | Default |
281280
| ------------ | ----------------------- | ------------ |

VERSION.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.7.2
1+
1.7.3

internal/app/model.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2335,7 +2335,7 @@ func (m *Model) renderFooter() string {
23352335
makePill(fk(m.km.Palette) + " " + styles.IconPalette + "palette"),
23362336
makePill(fk(m.km.ProjectSwitcher) + " project"),
23372337
makePill(fk(m.km.NewTask) + " " + styles.IconNew + "new"),
2338-
makePill("f " + styles.IconTag + "tag"),
2338+
makePill(fk(m.km.ViewTag) + " " + styles.IconTag + "tag"),
23392339
makePill(fk(m.km.ToggleStrike) + " " + styles.IconStrike + "done"),
23402340
makePill(fk(m.km.Stats) + " stats"),
23412341
makePill(fk(m.km.DeleteTask) + " " + styles.IconDelete + "delete"),

internal/core/task.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ type TaskPatch struct {
183183
Collapsed *bool
184184
CompletedAt **time.Time
185185
Result *string
186+
OpenIssueID *string
186187
Responsible *string
187188
}
188189

@@ -235,6 +236,9 @@ func (p TaskPatch) ApplyTo(t Task) Task {
235236
if p.Result != nil {
236237
t.Result = *p.Result
237238
}
239+
if p.OpenIssueID != nil {
240+
t.OpenIssueID = *p.OpenIssueID
241+
}
238242
if p.Responsible != nil {
239243
t.Responsible = *p.Responsible
240244
}

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.7.2"))
64+
L.SetField(kairo, "version", lua.LString("1.7.3"))
6565

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

internal/ui/editor/model.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ type Model struct {
5555
parentID textinput.Model
5656
responsible textinput.Model
5757
result textinput.Model
58+
openIssueID textinput.Model
5859
desc textarea.Model
5960

6061
focus int
@@ -178,6 +179,12 @@ func New(s styles.Styles, mode Mode, t core.Task, preview bool) Model {
178179
res.SetValue(t.Result)
179180
applyFieldStyles(&res)
180181

182+
oi := textinput.New()
183+
oi.Prompt = ""
184+
oi.CharLimit = 16
185+
oi.SetValue(t.OpenIssueID)
186+
applyFieldStyles(&oi)
187+
181188
d := textarea.New()
182189
d.Placeholder = "Description (Markdown)…"
183190
d.SetValue(t.Description)
@@ -201,6 +208,7 @@ func New(s styles.Styles, mode Mode, t core.Task, preview bool) Model {
201208
parentID: pid,
202209
responsible: resp,
203210
result: res,
211+
openIssueID: oi,
204212
desc: d,
205213
focus: 0,
206214
showPreview: preview,
@@ -239,6 +247,7 @@ func (m *Model) SetSize(w, h int) {
239247
m.parentID.Width = max(20, editorW-20)
240248
m.responsible.Width = max(20, editorW-20)
241249
m.result.Width = max(20, editorW-20)
250+
m.openIssueID.Width = max(20, editorW-20)
242251
style := "dark"
243252
if m.styles.Theme.IsLight {
244253
style = "light"
@@ -278,14 +287,14 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
278287
return m, func() tea.Msg { return CloseMsg{} }
279288
case "tab":
280289
m.blurAll()
281-
m.focus = (m.focus + 1) % 13
290+
m.focus = (m.focus + 1) % 14
282291
m.focusField()
283292
return m, nil
284293
case "shift+tab":
285294
m.blurAll()
286295
m.focus--
287296
if m.focus < 0 {
288-
m.focus = 12
297+
m.focus = 13
289298
}
290299
m.focusField()
291300
return m, nil
@@ -348,6 +357,8 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
348357
case 11:
349358
m.result, cmd = m.result.Update(msg)
350359
case 12:
360+
m.openIssueID, cmd = m.openIssueID.Update(msg)
361+
case 13:
351362
m.desc, cmd = m.desc.Update(msg)
352363
}
353364
return m, cmd
@@ -435,6 +446,7 @@ func (m Model) View() string {
435446
fields = append(fields, renderField("󱓡 ", "Project:", m.project.View(), m.focus == 8))
436447
fields = append(fields, renderField("󰓆 ", "Resp:", m.responsible.View(), m.focus == 10))
437448
fields = append(fields, renderField("󰄬 ", "Result:", m.result.View(), m.focus == 11))
449+
fields = append(fields, renderField("󰋼 ", "Issue:", m.openIssueID.View(), m.focus == 12))
438450

439451
parentIDDisplay := m.parentID.Value()
440452
if parentIDDisplay == "" {
@@ -498,6 +510,7 @@ func (m *Model) blurAll() {
498510
m.parentID.Blur()
499511
m.responsible.Blur()
500512
m.result.Blur()
513+
m.openIssueID.Blur()
501514
m.desc.Blur()
502515
}
503516

@@ -528,6 +541,8 @@ func (m *Model) focusField() {
528541
case 11:
529542
m.result.Focus()
530543
case 12:
544+
m.openIssueID.Focus()
545+
case 13:
531546
m.desc.Focus()
532547
}
533548
}
@@ -692,6 +707,7 @@ func (m Model) saveCmd() tea.Cmd {
692707
ParentID: strings.TrimSpace(m.parentID.Value()),
693708
Responsible: strings.TrimSpace(m.responsible.Value()),
694709
Result: res,
710+
OpenIssueID: strings.TrimSpace(m.openIssueID.Value()),
695711
}
696712
return func() tea.Msg { return SaveNewMsg{Task: task} }
697713
}
@@ -749,5 +765,9 @@ func (m Model) saveCmd() tea.Cmd {
749765
if res != m.orig.Result {
750766
patch.Result = &res
751767
}
768+
oi := strings.TrimSpace(m.openIssueID.Value())
769+
if oi != m.orig.OpenIssueID {
770+
patch.OpenIssueID = &oi
771+
}
752772
return func() tea.Msg { return SavePatchMsg{ID: m.orig.ID, Patch: patch} }
753773
}

internal/util/paths.go

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,13 @@ import (
66
)
77

88
func AppDataDir(appName string) (string, error) {
9-
if d, err := os.UserConfigDir(); err == nil && d != "" {
10-
return filepath.Join(d, appName), nil
11-
}
12-
home, err := os.UserHomeDir()
9+
d, err := os.Getwd()
1310
if err != nil {
1411
return "", err
1512
}
16-
return filepath.Join(home, ".config", appName), nil
13+
return filepath.Join(d, "."+appName), nil
1714
}
1815

1916
func AppStateDir(appName string) (string, error) {
20-
if d, err := os.UserCacheDir(); err == nil && d != "" {
21-
return filepath.Join(d, appName), nil
22-
}
23-
home, err := os.UserHomeDir()
24-
if err != nil {
25-
return "", err
26-
}
27-
return filepath.Join(home, ".local", "share", appName), nil
17+
return AppDataDir(appName)
2818
}

0 commit comments

Comments
 (0)