Skip to content

Commit f746cf1

Browse files
committed
v1.2.3
- **Bugfix (TUI):** Fix spacebar handling in the TUI editor and filter. Some terminals emit `tea.KeySpace` instead of `tea.KeyRunes` for the spacebar; the TUI now treats `KeySpace` as a typed space so spaces are inserted into editor commands and list filters consistently. Added headless tests `TestEditorTypingSpaceKeyInCommands` and `TestFilterModeSpaceKeyAppendsSpace` to prevent regressions. - **Quality:** Ran `gocyclo` (no functions with complexity > 10 found) and `golangci-lint` (0 issues). - **Docs:** Update `docs/tui.md` and add release notes for the fix.
1 parent bdce27b commit f746cf1

File tree

11 files changed

+105
-10
lines changed

11 files changed

+105
-10
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33

44
All notable changes to this project will be documented in this file.
55

6+
## v1.2.3 - 2026-02-03
7+
8+
- **Bugfix (TUI):** Fix spacebar handling in the TUI editor and filter. Some terminals emit `tea.KeySpace` instead of `tea.KeyRunes` for the spacebar; the TUI now treats `KeySpace` as a typed space so spaces are inserted into editor commands and list filters consistently. Added headless tests `TestEditorTypingSpaceKeyInCommands` and `TestFilterModeSpaceKeyAppendsSpace` to prevent regressions.
9+
- **Quality:** Ran `gocyclo` (no functions with complexity > 10 found) and `golangci-lint` (0 issues).
10+
- **Docs:** Update `docs/tui.md` and add release notes for the fix.
11+
612
## v1.2.2 - 2026-01-17
713

814
- Bugfix: Preserve comma-containing commands passed via a single `-c` flag (e.g., PowerShell `Select-Object` usage such as `Get-ComputerInfo | Select-Object OsName, OsVersion, OsArchitecture`). The CLI no longer splits a single `-c` value on commas — instead we use repeatable `-c` flags (`StringArray`) and ensure a single quoted `-c` is preserved literally.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ _Record. Automate. Run._
2121

2222
---
2323

24-
### Why not just an alias or a shell script?
24+
### Why not just an alias or a shell script?
2525

2626
- **Aliases** - Fast to add, but per‑shell and per‑machine. They live in whichever RC file you edited and are invisible to others unless you share your dotfiles manually. Hard to discover across many short‑lived aliases.
2727
- **Shell scripts / dotfiles** - Flexible and shareable, but scattered. To share you need a repo, copy instructions, or a package. Scripts don’t include author/metadata, versioned history for the command itself, or a simple, interactive discovery UI.
2828
- **git** - Excellent for source control and team collaboration, but file‑centric: commits operate on files, not on discoverable, runnable command sets. Git workflows require manual commits and searches; they aren’t optimized for quick ad hoc rollbacks or interactive discovery of hundreds of tiny workflows.
2929

30-
### How krnr helps
30+
### How krnr helps
3131

3232
- **Global (per‑user, per‑machine)** - krnr stores a single, versioned SQLite registry in your user config path (see `KRNR_HOME`), so any shell on your machine (bash, zsh, PowerShell, TUI, CI sessions) can access the same saved workflows without per‑shell setup.
3333
- **Cross‑machine** — Use `krnr export` to produce a portable SQLite file (whole DB or selected sets) and `krnr import` on another machine. Exports are single files with metadata, timestamps, authorship, and configurable conflict policies (`--on-conflict` rename|skip|overwrite|merge) to avoid clobbering local setups.

RELEASE_NOTES/GITHUB_v1.2.3.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# v1.2.3 - 2026-02-03
2+
3+
- Bugfix: Fix spacebar behavior in the TUI. Some terminals produce a `KeySpace` event which previously was ignored; the TUI now treats it as a typed space in both the editor and list filter, restoring expected typing behavior.
4+
- Tests: Add headless UI tests to cover `KeySpace` in filter and editor flows.
5+
- Quality: Ran `gocyclo` and `golangci-lint` — no actionable issues found.
6+
7+
Upgrade note: This is a backward-compatible TUI fix; no DB or CLI-breaking changes.

cmd/tui/ui/editor.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ func (m *TuiModel) handleEditorKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
4141
if msg.Type == tea.KeyBackspace || msg.Type == tea.KeyDelete {
4242
return m.handleEditorBackspace()
4343
}
44+
// Treat KeySpace like typing a rune ' ' so real terminals that produce
45+
// a KeySpace are handled the same as test harnesses that use KeyRunes.
46+
if msg.Type == tea.KeySpace {
47+
// synthesize a KeyRunes message with a single space rune
48+
return m.handleEditorRunes(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{' '}})
49+
}
4450
if msg.Type == tea.KeyRunes {
4551
return m.handleEditorRunes(msg)
4652
}

cmd/tui/ui/input.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func applyListFilterKey(m *TuiModel, msg tea.KeyMsg) (tea.Model, tea.Cmd, bool)
5151
// update the live filter text and apply filtering only when the filter
5252
// text actually changed (typing/backspace/esc). Navigation keys should
5353
// not reapply the items and reset the selection.
54-
if msg.Type == tea.KeyRunes || msg.Type == tea.KeyBackspace || msg.Type == tea.KeyDelete || msg.Type == tea.KeyEsc {
54+
if msg.Type == tea.KeyRunes || msg.Type == tea.KeySpace || msg.Type == tea.KeyBackspace || msg.Type == tea.KeyDelete || msg.Type == tea.KeyEsc {
5555
updateListFilterText(m, msg)
5656
applyFilterItems(m)
5757
}
@@ -67,6 +67,8 @@ func updateListFilterText(m *TuiModel, msg tea.KeyMsg) {
6767
for _, ru := range msg.Runes {
6868
m.listFilter += string(ru)
6969
}
70+
case tea.KeySpace:
71+
m.listFilter += " "
7072
case tea.KeyBackspace, tea.KeyDelete:
7173
if len(m.listFilter) > 0 {
7274
r := []rune(m.listFilter)
@@ -139,7 +141,7 @@ func applyFilterItems(m *TuiModel) {
139141

140142
func handleDispatchFilterMode(m *TuiModel, msg tea.KeyMsg) (tea.Model, tea.Cmd, bool) {
141143
switch msg.Type {
142-
case tea.KeyRunes:
144+
case tea.KeyRunes, tea.KeySpace:
143145
return handleFilterModeRunes(m, msg)
144146
case tea.KeyBackspace, tea.KeyDelete:
145147
return handleFilterModeBackspace(m, msg)
@@ -162,8 +164,12 @@ func handleDispatchFilterMode(m *TuiModel, msg tea.KeyMsg) (tea.Model, tea.Cmd,
162164
}
163165

164166
func handleFilterModeRunes(m *TuiModel, msg tea.KeyMsg) (tea.Model, tea.Cmd, bool) {
165-
for _, ru := range msg.Runes {
166-
m.listFilter += string(ru)
167+
if msg.Type == tea.KeySpace {
168+
m.listFilter += " "
169+
} else {
170+
for _, ru := range msg.Runes {
171+
m.listFilter += string(ru)
172+
}
167173
}
168174
applyFilterItems(m)
169175
return m, nil, false

cmd/tui/ui/menu.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,6 @@ func (m *TuiModel) renderMenu() string {
351351
return b.String()
352352
}
353353

354-
355-
356354
func (m *TuiModel) renderMenuSingleCol() string {
357355
var b strings.Builder
358356
// Compute maximum item width so we can pad items to a consistent length

cmd/tui/ui/tui_filter_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,33 @@ func TestFilterTypingUpdatesList(t *testing.T) {
5252
}
5353
}
5454

55+
func TestFilterModeSpaceKeyAppendsSpace(t *testing.T) {
56+
reg := &fakeRegistry{items: []adapters.CommandSetSummary{{Name: "alpha", Description: "A"}, {Name: "beta", Description: "B"}}}
57+
ui := modelpkg.New(reg, &fakeExec{}, nil, nil)
58+
_ = ui.RefreshList(context.Background())
59+
m := NewModel(ui)
60+
m.Init()()
61+
// enter filter mode
62+
m1, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'/'}})
63+
m = m1.(*TuiModel)
64+
if !m.filterMode {
65+
t.Fatalf("expected filter mode to be active")
66+
}
67+
// type 'b'
68+
m2, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'b'}})
69+
m = m2.(*TuiModel)
70+
// press SPACE key (KeySpace)
71+
m3, _ := m.Update(tea.KeyMsg{Type: tea.KeySpace})
72+
m = m3.(*TuiModel)
73+
if m.listFilter != "b " {
74+
t.Fatalf("expected listFilter to be 'b ' after pressing KeySpace, got %q", m.listFilter)
75+
}
76+
// the list Title should include the space
77+
if m.list.Title != "Filter: b " {
78+
t.Fatalf("expected title to include space, got %q", m.list.Title)
79+
}
80+
}
81+
5582
func TestFilterMatchesTags(t *testing.T) {
5683
reg := &fakeRegistry{items: []adapters.CommandSetSummary{{Name: "s1", Description: "", Tags: []string{"db", "alpha"}}, {Name: "s2", Description: "", Tags: []string{"net"}}}}
5784
ui := modelpkg.New(reg, &fakeExec{}, nil, nil)

cmd/tui/ui/tui_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,48 @@ func TestEditorTypingKAndSpaceInCommands(t *testing.T) {
646646
}
647647
}
648648

649+
func TestEditorTypingSpaceKeyInCommands(t *testing.T) {
650+
full := adapters.CommandSetSummary{Name: "one", Description: "First", Commands: []string{""}}
651+
reg := &replaceFakeRegistry{items: []adapters.CommandSetSummary{{Name: "one", Description: "First"}}, full: full}
652+
ui := modelpkg.New(reg, &fakeExec{}, nil, nil)
653+
_ = ui.RefreshList(context.Background())
654+
m := NewModel(ui)
655+
m.Init()()
656+
m1, _ := m.Update(tea.WindowSizeMsg{Width: 80, Height: 20})
657+
m = m1.(*TuiModel)
658+
m2, _ := m.Update(tea.KeyMsg{Type: tea.KeyEnter})
659+
m = m2.(*TuiModel)
660+
// open in-TUI editor
661+
m3, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'e'}})
662+
m = m3.(*TuiModel)
663+
if !m.editingMeta {
664+
t.Fatalf("expected editor to be open")
665+
}
666+
// cycle to commands field (tab until commands)
667+
for i := 0; i < 10 && m.editor.field != 5; i++ {
668+
m4, _ := m.Update(tea.KeyMsg{Type: tea.KeyTab})
669+
m = m4.(*TuiModel)
670+
}
671+
// ensure we're editing the first command
672+
if m.editor.cmdIndex != 0 {
673+
t.Fatalf("expected cmdIndex 0, got %d", m.editor.cmdIndex)
674+
}
675+
// type 'k', then SPACE using KeySpace, then 'x'
676+
m5, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'k'}})
677+
m = m5.(*TuiModel)
678+
m6, _ := m.Update(tea.KeyMsg{Type: tea.KeySpace})
679+
m = m6.(*TuiModel)
680+
m7, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'x'}})
681+
m = m7.(*TuiModel)
682+
if m.editor.commands[m.editor.cmdIndex] != "k x" {
683+
t.Fatalf("expected command to contain typed runes 'k x', got %q", m.editor.commands[m.editor.cmdIndex])
684+
}
685+
// ensure cmdIndex didn't move due to 'k'
686+
if m.editor.cmdIndex != 0 {
687+
t.Fatalf("expected cmdIndex to remain 0 after typing 'k', got %d", m.editor.cmdIndex)
688+
}
689+
}
690+
649691
func TestDeleteFromDetailPromptsAndDeletesWhenConfirmed(t *testing.T) {
650692
full := adapters.CommandSetSummary{Name: "one", Description: "First", Commands: []string{"echo hi"}}
651693
reg := &replaceFakeRegistry{items: []adapters.CommandSetSummary{{Name: "one", Description: "First"}}, full: full}

docs/tui.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ Accessibility & theming
4646
- High-contrast mode: toggle with `T` inside the TUI. This switches to a high-contrast color scheme to improve readability in low-vision or busy terminal themes.
4747
- Menu modal: press `m` to open the **Menu** modal which contains actions like `Export database`, `Import database`, `Import set`, `Install`, `Uninstall`, and `Status`. Export/Import invoke the existing adapter-backed logic so the TUI delegates to the same exporter/importer paths as the CLI.
4848

49+
Key handling and spaces
50+
- Note: different terminals may report the spacebar as either a `KeyRunes` event with a single `' '` rune or as a `KeySpace` event. The TUI now handles both forms consistently for editor input and the list filter so pressing the spacebar reliably inserts a space character regardless of environment. Tests were added to prevent regressions.
51+
52+
4953
CI
5054
- We added a GitHub Actions workflow `.github/workflows/tui-ci.yml` to run `go test ./... -v` across Ubuntu, Windows and macOS. The workflow validates headless UI tests and the rest of the test suite on PRs and pushes to `main`.
5155
Accessibility

internal/version/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ package version
33

44
// Version is set at build time via -ldflags "-X github.com/VoxDroid/krnr/internal/version.Version=<value>"
55
// The default is a development placeholder.
6-
var Version = "v1.2.2"
6+
var Version = "v1.2.3"

0 commit comments

Comments
 (0)