feat: modal to select a pinned directory#1345
Conversation
Features - Press b to open the modal - Fuzzy search through pinned directories using fzf - Arrow keys (j/k/up/down) to navigate results - Enter to navigate to selected directory - Esc to close modal - Shows directory name and path in results - Properly integrates with existing file panel navigation The implementation follows the same pattern as the zoxide modal Signed-off-by: Robert Zaremba <robert@zaremba.ch>
📝 WalkthroughWalkthroughAdds a new "pinned modal" feature: hotkey config and handling, default model initialization, model integration (update/render/overlay sizing), sidebar accessor for pinned directories, a new Changes
Sequence DiagramsequenceDiagram
participant User
participant KeyHandler as Key Handler
participant Model
participant Sidebar
participant PinnedModal as Pinned Modal
participant Renderer
User->>KeyHandler: press 'b' (goto_pinned)
KeyHandler->>Model: trigger GotoPinned
Model->>Sidebar: GetPinnedDirectories()
Sidebar-->>Model: pinned dirs
Model->>PinnedModal: LoadPinnedDirs(dirs)
Model->>PinnedModal: Open()
PinnedModal-->>Model: tea.Cmd (init)
User->>PinnedModal: type / navigate / confirm
PinnedModal->>PinnedModal: Filter & navigate
PinnedModal-->>Model: UpdateMsg / ModelAction (selection)
Model->>Model: applyPinnedModalAction()
Model->>Renderer: updateRenderForOverlay()
Renderer->>PinnedModal: pinnedModal.Render()
PinnedModal-->>Renderer: rendered overlay
Renderer-->>User: display modal
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Signed-off-by: Robert Zaremba <robert@zaremba.ch>
There was a problem hiding this comment.
Pull request overview
Adds a new “pinned directories” navigation modal (triggered via a new goto_pinned hotkey) that lets users fuzzy-search pinned directories and jump the active file panel to the selected directory, modeled after the existing zoxide modal.
Changes:
- Introduces a new
pinnedmodalUI component (model/navigation/rendering) and integrates it into the main Bubble Tea update/render flow. - Adds a new
goto_pinnedhotkey (b) to the default and vim hotkey configs and exposes it in the help menu. - Wires pinned-directory data from the sidebar’s pinned manager into the new modal.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| src/superfile_config/vimHotkeys.toml | Adds goto_pinned hotkey mapping. |
| src/superfile_config/hotkeys.toml | Adds goto_pinned hotkey mapping. |
| src/internal/validation.go | Treats pinned modal as an overlay model for validation/interaction logic. |
| src/internal/ui/sidebar/sidebar.go | Exposes pinned directories for consumption by the pinned modal. |
| src/internal/ui/pinnedmodal/utils.go | Adds open/close/sizing helpers for the pinned modal. |
| src/internal/ui/pinnedmodal/type.go | Defines pinned modal model/message types and directory DTO. |
| src/internal/ui/pinnedmodal/render.go | Implements pinned modal rendering (input + results list). |
| src/internal/ui/pinnedmodal/navigation.go | Adds cursor movement + scrolling logic for results list. |
| src/internal/ui/pinnedmodal/model.go | Implements pinned modal update handling, filtering, and confirm action. |
| src/internal/ui/pinnedmodal/consts.go | Adds pinned modal UI constants. |
| src/internal/ui/helpmenu/data.go | Adds help menu entry describing the new hotkey. |
| src/internal/type.go | Adds pinnedModal to the main model struct. |
| src/internal/model_render.go | Adds a pinned modal render helper. |
| src/internal/model.go | Integrates pinned modal sizing, update handling, and overlay placement; adds openPinnedModal(). |
| src/internal/key_function.go | Hooks goto_pinned hotkey to open the pinned modal. |
| src/internal/default_config.go | Initializes the pinned modal in the default model config. |
| src/internal/common/config_type.go | Adds GotoPinned to the hotkeys config type for TOML parsing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func (m *Model) handleNormalKeyInput(msg tea.KeyMsg) tea.Cmd { | ||
| var cmd tea.Cmd | ||
| m.textInput, cmd = m.textInput.Update(msg) | ||
| m.FilterPinnedDirs(m.textInput.Value()) | ||
| return cmd |
There was a problem hiding this comment.
handleNormalKeyInput calls FilterPinnedDirs synchronously on every keypress. FilterPinnedDirs uses utils.FzfSearch, which blocks on a results channel (see utils/fzf_utils.go) and can freeze the UI for slow queries. Consider following the zoxide modal pattern: return tea.Batch(cmd, m.GetQueryCmd(query)) and have the cmd compute results and send an UpdateMsg so the search runs asynchronously.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@src/internal/ui/pinnedmodal/model.go`:
- Around line 30-59: The HandleUpdate method currently prevents alphanumeric
list hotkeys by using the !isKeyAlphaNum guard; update HandleUpdate to check
hotkey matches for common.Hotkeys.ListUp and common.Hotkeys.ListDown (and
common.Hotkeys.Quit) before deferring to text input so alphanumeric bindings
like "j"/"k" are handled by navigateUp()/navigateDown() (and trigger
Close()/quit handling) instead of being forwarded into handleNormalKeyInput;
specifically remove or invert the isKeyAlphaNum condition around the
ListUp/ListDown cases (or move the slices.Contains checks above the default
text-input branch) and add a case to handle common.Hotkeys.Quit so the quit
hotkey is not absorbed by m.textInput.Update.
- Around line 92-124: In FilterPinnedDirs, the haystack entries are built as
"Name Location" but dirMap is keyed by dir.Location, causing lookup failures
when utils.FzfSearch returns matches keyed by the haystack string; update the
dirMap key to use the same searchText (the "Name Location" string stored in
haystack) so that match.Key can be looked up successfully (adjust the loop that
builds dirMap in Model.FilterPinnedDirs to set dirMap[searchText] = dir).
In `@src/internal/ui/pinnedmodal/render.go`:
- Around line 34-49: In Model.renderVisibleResults ensure availablePathWidth is
clamped to a non-negative value before dividing: compute availablePathWidth :=
max(0, m.width - columnWidth) and then derive leftWidth/rightWidth (e.g.,
availablePathWidth/2 and availablePathWidth - availablePathWidth/2) to pass
non-negative integers to common.TruncateTextBeginning for name and path; update
references to availablePathWidth/2 in the TruncateTextBeginning calls so both
truncation widths are safe on narrow terminals.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Robert Zaremba <robert@zaremba.ch>
Signed-off-by: Robert Zaremba <robert@zaremba.ch>
Signed-off-by: Robert Zaremba <robert@zaremba.ch>
|
@robert-zaremba Could you fix the merge conflicts? Thanks! |
Features
Summary by CodeRabbit
New Features
Configuration
UX
Tests