Repository navigation
feat(tui): add /unread and a /doubletab setting; /settings only - #34
Merged
Merged
Conversation
/set and /options, the short forms of /settings, are gone from the command table and from /help. Typing either is now an unknown command, handled as any other: "unknown command /set, try /help", no menu, no focus change. Tab completes /set to /settings, since that is the one command it begins. specs/ui.md (Commands, its short forms, Settings menu) and docs/usage.md say so, with the reason as a Decision: one name for the menu, so the command list has one way to it. The parser and settings menu tests cover the removed spellings, their outcome next to an unknown command's, and the completion.
/unread focuses the first chat window after the current one, in window order and wrapping round, whose unread count is above 0: the windows [Act: ...] lists, in turn. Window 0 keeps no count of its own and is never one, even after [unread] on its /win row; the current window comes last, so it is the target only when it alone is marked unread. With none it is the error "no unread window" and nothing changes. It is a focus like /win N (switchTo): the read receipt goes at once, the last focused window follows, and a window an empty-prompt Tab showed that it leaves within the Tab's time gets its unread count back and is not reported. nextUnread in internal/tui/lastwin.go finds the target, for the double Tab to use as well. /help lists it and its notes mention it; Tab completes the name; there are no aliases. specs/ui.md (Windows, Commands, Read receipts), specs/overview.md and docs/usage.md describe it. Tests cover the order, the wrap, window 0, the current window, the error, the receipt and counts against /win N, and the window a Tab passed through.
Two fast empty-prompt Tabs used to jump back to the last window, so fast Tabs no longer walked the windows. What the second Tab does is now a kept setting, /doubletab [off|last|unread|<ms>], off by default: - off: no double Tab; every Tab goes to the next window, however fast. - last: the second Tab goes where /win last goes from the window the first Tab left, as before. - unread: the second Tab goes where /unread goes from there. The window the first Tab passed through gets its unread count back first, so it is the target when it is the next one with unread messages; with no unread window the second Tab is a plain one. The time window, 400 ms before, is kept too (150 to 1000 ms, "300" or "300ms") and replaces the tabGap constant everywhere: the double Tab, the held read receipt, the restored counts and the after timer. A held receipt keeps the time it was held with. The pass-through protection (a window a Tab showed and left within that time keeps its unread count and sends no receipt) stays in every mode. No argument shows the setting; on and any other word are an error that keeps nothing. The mode is stored under "doubletab" as its word, the time under "doubletab-time"; anything else reads as off and 400 ms. The settings menu gets two rows after vim, doubletab (off, last, unread) and doubletab-time (200ms to 800ms); a time set by the command that the row does not list shows as it is and steps to its listed neighbours. The setting code is in internal/tui/doubletab.go. Tab completes off, last and unread; /help, /keys, the start hints, specs/ui.md and docs/usage.md describe it, with the decisions: off by default because it changes what fast Tabs do, last or unread because both are a jump one may want, the protection in every mode because a window flashed for a moment was not read. Tests set /doubletab last where they use the gesture and cover the default walk, both jumps, the time window and the held receipt's timer, the command forms and their persistence, the menu rows, completion, help and hints.
parseTabGap and loadDoubleTab multiplied the number of milliseconds by time.Millisecond before checking the 150..1000 ms range. A large number wraps int64 there: 2^58 + 300 (288230376151712044) becomes exactly 300 ms, so /doubletab 288230376151712044 was accepted and kept as 300, and the same value in the store read as 300 ms instead of the 400 ms default for an out-of-range time. Both paths now go through tabGapOf, which checks the range on the int64 number itself and only then converts it. The command tests reject 2^58 + 300 (with and without ms), the int64 maximum and a number past int64; the store tests read 2^58 + 300, -2^58 + 300 and a too-large JSON number as 400 ms. specs/ui.md says that any larger number is the error, that a kept time out of range means 400, and why the range is checked before the conversion.
specs/ui.md said that with /doubletab last or unread fast Tabs do not walk two windows. The second Tab is a plain Tab when it has no target (no last window, or no window with unread messages), and for unread having no unread window is the usual state, so in that mode fast Tabs mostly do walk. The Windows section now limits the claim to a second Tab with a target, says that without one (and always with off) fast Tabs walk, and that with unread they usually walk. The off-by-default Decision, the Read receipts paragraph and the /unread entry say the same. The unread status texts of /doubletab now end their jump with "if any" (doubleTab.jump), and /help, docs/usage.md and the commands and Keys tables of the spec say that with no unread window the double Tab goes to the next window. The command and help tests assert the new texts.
/help's /doubletab line must give the range and default the code uses (minTabGap, maxTabGap, defaultTabGap), and no /help line may offer /set or /options for the settings menu; neither was checked before.
Mark the item done in todo/todo.md and add session/2026-11-04-doubletab.md: what /unread and /doubletab do, the review results, the decisions and the open note on window 0's unread mark.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
/unread: focuses the next window with unread messages after the current one, in window order and wrapping round. Window 0 is never a target. It focuses as/win Ndoes (read receipts, the last window, restored counts). With none it givesno unread window./doubletab [off|last|unread|<ms>]: off by default and kept between starts.off: every empty-prompt Tab goes to the next window, so fast Tabs walk the windows.last: a fast second Tab goes to the last window, as/win lastdoes.unread: a fast second Tab goes where/unreadwould from the window before the first Tab. The window the first Tab passed through gets its count back first, so it can be the target. With nothing unread the second Tab is a plain Tab.<ms>: a time from 150 to 1000 ms (default 400) replaces the fixed 400 ms everywhere. That includes the protection that keeps a window a Tab only passed through unread, which stays on in every mode./settings: two new rows,doubletab(off, last, unread) anddoubletab-time(200 to 800 ms). A time set by the command that isn't in the list shows as it is./settings:/setand/optionsare removed and are now unknown commands./help,/keys, two start hints, specs/ui.md (with Decision lines), specs/overview.md and docs/usage.md describe it all.Review
/doubletabnumber wrapped round into the valid range and was kept.unreadmode, but they do when nothing is unread./helptexts no test pinned; they now have a test anyway.lazygram -data <scratch> demo).make checkandgo test -race ./internal/tui/ ./internal/command/pass.Security: the two new state keys hold a word and a number. A window a Tab only passes through still sends no read receipt in any mode.