Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
be33ad9
docs(superpowers): spec and plan for configurable keybindings (#82)
marcoarnulfo Aug 1, 2026
0ce993d
docs(superpowers): fold the opus review into the keybindings design (…
marcoarnulfo Aug 1, 2026
7b8ee57
feat(config): add the keys map for binding overrides (#82)
marcoarnulfo Aug 1, 2026
a19efd7
test(config): strengthen key spec coverage (#82)
marcoarnulfo Aug 1, 2026
e2992c1
feat(tui): derive the config name of every binding (#82)
marcoarnulfo Aug 1, 2026
e97f8e7
fix(tui): handle titlecase binding names (#82)
marcoarnulfo Aug 1, 2026
6f012a6
feat(tui): resolve binding overrides from the config (#82)
marcoarnulfo Aug 1, 2026
3185614
fix(tui): detach resolved keys from config input (#82)
marcoarnulfo Aug 1, 2026
d0df8d6
feat(tui): refuse an override that adds a claimant to a key (#82)
marcoarnulfo Aug 1, 2026
757d307
feat(tui): route with the configured binding table (#82)
marcoarnulfo Aug 1, 2026
0b63a52
fix(tui): keep remapped actions in the palette (#82)
marcoarnulfo Aug 1, 2026
ca16b34
fix(tui): reject noncanonical control aliases (#82)
marcoarnulfo Aug 1, 2026
0108ca6
fix(tui): show remapped keys in the footer and the help (#82)
marcoarnulfo Aug 1, 2026
568b2d2
fix(tui): derive inline key instructions (#82)
marcoarnulfo Aug 1, 2026
83a14bc
fix(tui): keep inline key guidance actionable (#82)
marcoarnulfo Aug 1, 2026
99962bb
docs: document the keys map (#82)
marcoarnulfo Aug 1, 2026
2aed4be
docs: correct configurable key examples (#82)
marcoarnulfo Aug 1, 2026
3b2f572
docs: align the keybinding plan with the final rule (#82)
marcoarnulfo Aug 1, 2026
282b24e
fix(tui): reject unreachable key bindings (#82)
marcoarnulfo Aug 1, 2026
09ede6e
fix(tui): render space in key hints (#82)
marcoarnulfo Aug 1, 2026
711b6e9
docs: add canonical key validation to the plan (#82)
marcoarnulfo Aug 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Keybindings are configurable: `keys:` in the config remaps any snake_case
binding name with a single key or a list, and the footer and `?` help show the
result. `force_quit` (`ctrl+c`) stays fixed so there is always a way out.
Invalid definitions stop startup with an explanatory error; the conservative
collision rule rejects a new claimant when the key remains shared, while still
allowing clean moves and swaps (#82).
- Themes: `theme: dracula` (or `nord`) picks a built-in palette, and `themes:` in
the config defines your own, naming only the tokens you want to change. An
invalid color stops startup with a message naming the theme, the token and
Expand Down
27 changes: 27 additions & 0 deletions README.it.md
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,33 @@ billing:
accent: {light: "127", dark: "205"}
muted: "240"
```
- `keys` (opzionale): rimappa per nome qualsiasi binding della TUI. Il valore può
essere un tasto o una lista di tasti; ogni binding che non nomini mantiene il
proprio default. I nomi usano lo snake_case, come `quit`, `log_hours`,
`prev_month` e `palette_up`. Se sbagli un nome, l'errore all'avvio elenca quelli
validi.

```yaml
keys:
log_hours: "L"
up: ["up", "ctrl+u"]
```

- **`force_quit` non può essere rimappato.** `ctrl+c` resta una via d'uscita
affidabile se sposti altri binding su tasti irraggiungibili.
- I binding predefiniti condividono deliberatamente alcuni tasti quando sono
attivi su schermate diverse. Il controllo conservativo delle collisioni
rifiuta un override solo quando aggiunge un nuovo binding a un tasto che, dopo
tutti gli override, ha ancora almeno due binding associati. Puoi sempre
spostare un binding da un tasto condiviso, spostarlo su un tasto il cui
precedente proprietario viene spostato altrove, oppure scambiare in modo pulito
due tasti. Poiché il controllo non modella quali binding condividono una
schermata, scegli un altro tasto se rifiuta una combinazione che sai non sarebbe
mai attiva insieme.
- Nomi sconosciuti, tasti vuoti o duplicati, liste vuote e collisioni bloccano
l'avvio con un errore esplicativo, senza ricadere silenziosamente sui default.
- I tasti rimappati compaiono nel footer, nell'aiuto esteso di `?` e nelle
indicazioni contestuali sui tasti.

### Come vengono calcolati gli importi fatturati

Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,30 @@ billing:
accent: {light: "127", dark: "205"}
muted: "240"
```
- `keys` (optional): remap any TUI binding by name. A value is one key or a list
of keys; every binding you do not name keeps its default. Names use snake_case,
such as `quit`, `log_hours`, `prev_month` and `palette_up`. If a name is wrong,
the startup error lists the valid ones.

```yaml
keys:
log_hours: "L"
up: ["up", "ctrl+u"]
```

- **`force_quit` cannot be remapped.** `ctrl+c` remains a reliable way out if
other bindings are moved somewhere unreachable.
- The built-in bindings deliberately share keys when they are active on
different screens. The conservative collision check rejects an override only
when it adds a new claimant to a key that still has at least two claimants
after all overrides. Moving a binding off a shared key, moving to a key whose
previous owner also moves away, and clean swaps are allowed. Because the check
does not model which bindings share a screen, choose another key if it rejects
a combination you know would not be active together.
- Unknown names, empty or duplicate keys, empty lists and collisions stop
startup with an explanatory error instead of silently falling back.
- Remapped keys appear in the footer, the expanded `?` help and inline key
guidance.

### How billed amounts are computed

Expand Down
13 changes: 6 additions & 7 deletions docs/superpowers/plans/2026-08-01-accessibility-base.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,9 @@ git commit -m "feat(config): add the optional mouse key (#74)"

**Contesto che il brief non può sapere.**

1. `keyMsgFor` (`internal/tui/actions.go:149`) esiste già e costruisce `KeyMsg`
sintetici, ma **non va usata né allargata**: il suo commento dichiara
l'insieme chiuso di proposito, per la command palette. `wheelKey` è una
funzione separata con la propria motivazione.
1. `parseKeyName` (`internal/tui/actions.go`) esiste già e ricostruisce `KeyMsg`
da nomi configurabili, ma qui non serve: la rotella ha due output interni
fissi e `wheelKey` li esprime direttamente in una funzione separata.
2. Le fixture dei test esistono già nel package e vanno riusate, non riscritte:
`filtersScreenFixture()` (`filters_test.go:18`), `newTestModel()`
(`log_test.go:31`), `newTestModelOnReport()` (`log_test.go:36`),
Expand Down Expand Up @@ -362,9 +361,9 @@ import tea "github.com/charmbracelet/bubbletea"
// and the tests that cover it — apply unchanged. actions.go does the same for
// the command palette.
//
// It deliberately does not go through keyMsgFor: that function's key set is
// closed on purpose for the palette's benefit (see its doc comment), and
// widening it for a different caller would betray that.
// It deliberately does not go through parseKeyName: wheel translation has two
// fixed internal outputs rather than a user-written name to validate, so typed
// KeyMsgs state that invariant directly.
//
// There is no check on msg.Action, and it would be dead code: measured against
// bubbletea v1.3.10, a wheel notch always arrives as a single press. The SGR
Expand Down
Loading
Loading