feat(tui): pin tab + auto-follow new downloads#438
Merged
Conversation
- Add pinnedTab field to RootModel to track which tab is pinned (-1 = none) - Press ctrl+tab to toggle pin on the active tab; a 📌 icon appears in the tab bar - When a tab is pinned, automatic tab switching is suppressed — the UI stays put - When no tab is pinned, the TUI now auto-follows new downloads started from the browser extension (sets SelectedDownloadID on DownloadStarted/Queued events) - Update Tab component to accept a Pinned field and render the pin icon - Add PinTab key binding to dashboard help view Closes #431
Binary Size Analysis
|
ctrl+tab is consumed by the terminal emulator and never reaches the TUI. Use 't' (toggle) as the pin key instead — consistent with single-key binding style used throughout the dashboard (q/w/e, p, r, x, etc.).
- Left/right arrows now switch tabs (prev/next); tab key retained for next - Remove vim j/k navigation bindings (closes #430) - Fix: pin tab now fully overrides ALL tab switches including: - Downloads started via 'a' key (startDownload in process.go) - Browser extension downloads (update_events.go) - Download completion follow-through (list.go)
Comment on lines
238
to
253
| Up: key.NewBinding( | ||
| key.WithKeys("up", "k"), | ||
| key.WithHelp("\u2191/k", "up"), | ||
| key.WithKeys("up"), | ||
| key.WithHelp("\u2191", "up"), | ||
| ), | ||
| Down: key.NewBinding( | ||
| key.WithKeys("down", "j"), | ||
| key.WithHelp("\u2193/j", "down"), | ||
| key.WithKeys("down"), | ||
| key.WithHelp("\u2193", "down"), | ||
| ), | ||
| LogUp: key.NewBinding( | ||
| key.WithKeys("up", "k"), | ||
| key.WithHelp("\u2191/k", "scroll up"), | ||
| key.WithKeys("up"), | ||
| key.WithHelp("↑", "scroll up"), | ||
| ), | ||
| LogDown: key.NewBinding( | ||
| key.WithKeys("down", "j"), | ||
| key.WithHelp("\u2193/j", "scroll down"), | ||
| key.WithKeys("down"), | ||
| key.WithHelp("↓", "scroll down"), | ||
| ), |
Contributor
There was a problem hiding this comment.
Vim-style
j/k navigation was removed from four key maps (Dashboard Up/Down, LogUp/LogDown, Settings Up/Down, CategoryManager Up/Down) with no replacement. Neither PinTab ("t") nor PrevTab ("left") conflicts with these keys, so the removal is unrelated to this feature and silently breaks navigation for users who rely on vim-style keys. This change should either be reverted or shipped in a separate PR.
Suggested change
| Up: key.NewBinding( | |
| key.WithKeys("up", "k"), | |
| key.WithHelp("\u2191/k", "up"), | |
| key.WithKeys("up"), | |
| key.WithHelp("\u2191", "up"), | |
| ), | |
| Down: key.NewBinding( | |
| key.WithKeys("down", "j"), | |
| key.WithHelp("\u2193/j", "down"), | |
| key.WithKeys("down"), | |
| key.WithHelp("\u2193", "down"), | |
| ), | |
| LogUp: key.NewBinding( | |
| key.WithKeys("up", "k"), | |
| key.WithHelp("\u2191/k", "scroll up"), | |
| key.WithKeys("up"), | |
| key.WithHelp("↑", "scroll up"), | |
| ), | |
| LogDown: key.NewBinding( | |
| key.WithKeys("down", "j"), | |
| key.WithHelp("\u2193/j", "scroll down"), | |
| key.WithKeys("down"), | |
| key.WithHelp("↓", "scroll down"), | |
| ), | |
| Up: key.NewBinding( | |
| key.WithKeys("up", "k"), | |
| key.WithHelp("↑/k", "up"), | |
| ), | |
| Down: key.NewBinding( | |
| key.WithKeys("down", "j"), | |
| key.WithHelp("↓/j", "down"), | |
| ), | |
| LogUp: key.NewBinding( | |
| key.WithKeys("up", "k"), | |
| key.WithHelp("↑/k", "scroll up"), | |
| ), | |
| LogDown: key.NewBinding( | |
| key.WithKeys("down", "j"), | |
| key.WithHelp("↓/j", "scroll down"), | |
| ), |
Rule Used: What: Flag commits that bundle unnecessary changes... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/tui/keys.go
Line: 238-253
Comment:
Vim-style `j`/`k` navigation was removed from four key maps (Dashboard Up/Down, LogUp/LogDown, Settings Up/Down, CategoryManager Up/Down) with no replacement. Neither `PinTab` (`"t"`) nor `PrevTab` (`"left"`) conflicts with these keys, so the removal is unrelated to this feature and silently breaks navigation for users who rely on vim-style keys. This change should either be reverted or shipped in a separate PR.
```suggestion
Up: key.NewBinding(
key.WithKeys("up", "k"),
key.WithHelp("↑/k", "up"),
),
Down: key.NewBinding(
key.WithKeys("down", "j"),
key.WithHelp("↓/j", "down"),
),
LogUp: key.NewBinding(
key.WithKeys("up", "k"),
key.WithHelp("↑/k", "scroll up"),
),
LogDown: key.NewBinding(
key.WithKeys("down", "j"),
key.WithHelp("↓/j", "scroll down"),
),
```
**Rule Used:** What: Flag commits that bundle unnecessary changes... ([source](https://app.greptile.com/review/custom-context?memory=74a28159-60fc-4201-a661-331594ebaf90))
How can I resolve this? If you propose a fix, please make it concise.
Member
Author
There was a problem hiding this comment.
this is intentional.
When a tab is pinned, pressing q/w/e, left/right, or tab now logs '◆ Tab is pinned — press t to unpin' instead of switching tabs.
This was referenced May 10, 2026
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.
Closes #431
Summary
Adds a pin tab feature to the TUI and implements auto-follow for new downloads initiated from the browser extension.
Pin Tab
ctrl+tabto pin/unpin the currently active tabctrl+tabagain on the same tab unpins it; a log entry confirms the actionAuto-follow New Downloads (when unpinned)
UX Design Rationale
Automatic switching alone would be disruptive to users browsing history or other tabs. The pin feature gives users explicit control: pin a tab to stay there, unpin to let the TUI follow activity.
Greptile Summary
Adds a pin tab feature (
tto toggle) and auto-follow for downloads started or queued from the browser extension. When unpinned, the TUI automatically navigates to the newly active tab by settingSelectedDownloadIDbefore callingUpdateListItems; a newstarted boolfield onDownloadModelensures downloads appear in TabActive immediately after the engine confirms start, before any progress events arrive.pinnedTab int(-1 = none) is added toRootModel; all tab-switch paths and auto-follow logic check this field before redirecting.renderTabsis promoted from a package-level function to aRootModelmethod so it can readpinnedTabto decorate the label with a◆indicator.follow_test.gocover brand-new download auto-follow, existing-download restart, pin suppression, and Queued→Active transition; theDownloadQueuedMsgauto-follow path remains untested.Confidence Score: 5/5
Safe to merge; the pin and auto-follow logic is straightforward and well-contained within the TUI layer.
All tab-switch paths consistently gate on
pinnedTab,startedis correctly initialised for every server-status case, and the core auto-follow flow is exercised by the new tests. The one open item —pinnedGuardlogging a spurious message when the user presses the shortcut for a tab they are already on — is cosmetic and does not affect correctness or data.No files require special attention beyond the minor guard-log behaviour in
update_dashboard.go.Important Files Changed
Pinned booltoTaband prepends◆when set; applied to bothRenderTabBarandRenderNumberedTabBar. Safe change.DownloadQueuedMsgauto-follow path (update_events.go:280) has no coverage here.PinTab(t) andPrevTab(left) bindings; removesj/kvim shortcuts (intentional per thread reply, closes #430). Full-help rows updated.m.pinnedTab == -1; addsd.startedto tab-classification conditions.started boolonDownloadModelandpinnedTab int(default -1) onRootModel;startedcorrectly initialised for all server-status cases.pinnedTab == -1guard so pinned users are not redirected when they add a download.pinnedGuardclosure andPinTab/PrevTabhandlers. Guard fires a log entry even when the user presses the shortcut for the already-active pinned tab, producing spurious noise.d.started = trueandm.SelectedDownloadIDbeforeUpdateListItemsforDownloadStartedMsg(both existing and new). Also addsSelectedDownloadIDforDownloadQueuedMsg. The queued-path auto-follow is untested.pinnedTab: -1so the optimistic-ID merge test continues to pass.renderTabsfrom a package-level function to aRootModelmethod so it can accessm.pinnedTabfor the tab label; clean refactor.renderTabs(...)tom.renderTabs(...)following the view.go refactor.Prompt To Fix All With AI
Reviews (4): Last reviewed commit: "fix(tui): address auto-follow edge cases..." | Re-trigger Greptile