feat: add preview panel with Vim-like navigation and key enhancements#18
feat: add preview panel with Vim-like navigation and key enhancements#18epilande merged 13 commits intoepilande:mainfrom
Conversation
Introduced a split-view screen where users can preview file content alongside the file tree. Added Vim-style navigation, improved key-sequence handling (e.g., 'gg', 'ctrl+g'), and support for toggling the preview panel. Updated color themes for better visibility and ensured smooth transitions between the file tree and preview panel.
epilande
left a comment
There was a problem hiding this comment.
This is an awesome job on these new features, and the PR is looking good! The enhanced keyboard navigation, Vim-style controls, and the new preview pane are all fantastic additions that significantly improve usability. Thanks so much for tackling these!
I've left a few specific comments on the diff, and I also noticed a couple of things while testing:
- Layout Bug on Small Screens: On smaller terminal sizes, the file and preview panels sometimes mismatch in height, causing the layout to jump.
- Preview in Search Mode: When in search mode (after pressing
/), the preview panel doesn't seem to update when navigating up and down through the search results. It correctly updates when not in search mode.
A small housekeeping item:
- Code Comments: Some of the existing comments might now be obvious or no longer relevant. It would be great to do a quick pass to clean those up.
Please let me know if you'd like any help with these updates or if you'd prefer me to jump in and make these adjustments – I'm happy to do so as I'm definitely keen to get these great features merged in.
Overall, this is a really great contribution, and thank you again for all your work on this!
internal/model/model.go
Outdated
| }, | ||
| cursor: 0, | ||
| showTokenCount: config.ShowTokenCount, | ||
| showPreview: true, |
There was a problem hiding this comment.
Let's default to no preview panel. This would provide a cleaner initial view, especially for users who might not need the preview immediately
| // File/directory colors | ||
| Directory: lipgloss.Color("#8caaee"), // blue | ||
| File: lipgloss.Color("#c6d0f5"), // text | ||
| File: lipgloss.Color("#f4b8e4"), // pink - more visible than text |
There was a problem hiding this comment.
For now, I'd prefer to keep the original file colors as they were. Perhaps we can revisit theme customizations and color visibility as a separate effort in the future if there's more feedback on it
There was a problem hiding this comment.
sorry, just because I couldn't see it 🙈 on my terminal theme, I have rolled back all the theme modifications.
internal/ui/styles.go
Outdated
| // Don't add background padding to the end of the line | ||
| // This makes the cursor highlight only cover the actual content | ||
| return cursorIndicator + cursorLineContent |
There was a problem hiding this comment.
Could you share a bit about the reasoning behind this specific change? I actually prefer the highlight to go all the way across.
internal/model/init_update.go
Outdated
| // Subtract minimal space for borders | ||
| adjustedWidth := totalWidth - (borderWidth * 2) // Only account for essential borders | ||
| // Allocate slightly more space to the file tree (55%) and less to preview (45%) | ||
| fileTreeWidth := int(float64(adjustedWidth) * 0.55) |
There was a problem hiding this comment.
For the initial implementation, perhaps we could default to an even 50/50 split for the file tree and preview pane (* 0.5).
Longer-term, making this panel split configurable by the user would be a really nice enhancement, but that can definitely be a future improvement.
There was a problem hiding this comment.
Also I noticed that the panel width calculation logic appears in a couple of places (here and when toggling the preview pane). To keep things DRY and make it easier to manage, could we extract this logic into a reusable helper function, maybe something like calculateLayout?
internal/model/init_update.go
Outdated
| // Special handling for 'g' key in help mode | ||
| if currentKey == "g" { | ||
| // If this is the first 'g', just record it and wait for second 'g' | ||
| if m.lastKey != "g" { | ||
| m.lastKey = "g" | ||
| m.lastKeyTime = currentTime | ||
| // Don't do anything else for the first 'g' | ||
| return m, nil | ||
| } else if (currentTime - m.lastKeyTime) < 500 { | ||
| // This is the second 'g' within time window - go to top | ||
| m.lastKey = "" // Reset after handling the sequence | ||
| m.viewport.GotoTop() | ||
| return m, nil | ||
| } | ||
| } | ||
|
|
||
| // For non-'g' keys in help mode, update tracking info | ||
| if currentKey != "g" { | ||
| m.lastKey = currentKey | ||
| m.lastKeyTime = currentTime | ||
| } |
There was a problem hiding this comment.
The core logic for detecting the gg sequence is written twice, here and at line#255. The action taken after detecting gg differs slightly based on whether help is shown or if the file/preview pane is focused. Let's consolidate the gg sequence detection logic at the beginning of the tea.KeyMsg handler. This way, we can detect the sequence first and then determine the appropriate action based on the current state (m.showHelp, m.previewFocused). i.e.
case tea.KeyMsg:
currentKey := msg.String()
currentTime := utils.GetCurrentTimeMillis()
if currentKey == "g" {
if m.lastKey == "g" && (currentTime-m.lastKeyTime) < 500 {
m.lastKey = ""
if m.showHelp {
m.viewport.GotoTop()
} else if m.previewFocused && m.showPreview {
m.previewViewport.GotoTop()
} else {
m.viewport.GotoTop()
m.cursor = 0
m.refreshViewportContent()
}
return m, nil
} else {
m.lastKey = "g"
m.lastKeyTime = currentTime
return m, nil
}
} else {
m.lastKey = currentKey
m.lastKeyTime = currentTime
}
if m.showHelp {
switch currentKey {
case "q", "esc", "?":
// ...
internal/model/init_update.go
Outdated
| m.lastKeyTime = currentTime | ||
| // Don't do anything else for the first 'g' | ||
| return m, nil | ||
| } else if (currentTime - m.lastKeyTime) < 500 { |
There was a problem hiding this comment.
Let's extract the magic number 500 to be a named constant. Something like ggKeySequenceTimeoutMs or doubleKeyTimeoutMs.
|
I'll handle these issues. By the way, can we add a .grab folder and use something like toml for the configuration? |
Extracted layout calculations into a reusable `calculateLayout` method to simplify and centralize logic. Consolidated 'gg' key sequence handling for better readability and consistency.
Changed the default value of `showPreview` to `false` in the model settings. This ensures previews are no longer displayed unless explicitly enabled.
Introduce `ctrl+u` and `ctrl+d` shortcuts for half-page up and down scrolling. These commands now work both in the file tree and preview panel, ensuring a seamless navigation experience across the interface.
Yes, it should be the project name tho, i.e., To keep this current PR focused on the UI and navigation enhancements you've already implemented, it would be best to address the configuration file feature in a separate PR. Perhaps you could create a new issue for it, or even tackle it in a future contribution if you're interested |
Centralized UI constants like padding, headers, and borders into `panel_constants.go` to improve reusability and clarity. Refactored layout calculations in `View` and related rendering functions for accurate sizing and alignment, accommodating both single and split-pane modes with dynamic width and height adjustments.
Refined viewport height calculations to accurately account for UI elements, avoiding last line cutoffs and extra space. Updated styles across components to ensure consistent padding and spacing, improving overall UI alignment and readability. Adjusted file tree panel header to include an emoji for a more descriptive label.
Updated header, footer, and panel height calculations to use dynamic measurements via `lipgloss.Height`. This ensures accurate spacing and prevents negative values in viewport dimensions. Improved overall layout handling for better responsiveness and visuals.
Ensure names and suffixes fit within the calculated available width by truncating overly long names and appending ellipsis. This improves layout consistency and prevents visual overflow in the UI.
Updated navigation instructions to reflect the addition of ctrl+u and ctrl+d for half-page scrolling in both the file tree and preview. Removed redundant bindings for preview navigation to improve clarity.
Added padding and styling logic to ensure full-width highlighting for the preview panel when focused. Introduced a `StylePreviewContent` function to handle content styling, maintaining consistent visual behavior. Updated the view rendering logic to incorporate these enhancements.
|
@epilande Please take a look! Feel free to make any modifications to my PR, and I'll be happy to accept them. |
| // Handle directory icon state (open/closed) | ||
| if node.IsDir && icon == "" { | ||
| if m.collapsed[node.Path] { | ||
| icon = "" | ||
| icon = "" // Collapsed icon | ||
| } else { | ||
| icon = "" | ||
| icon = "" // Expanded icon |
There was a problem hiding this comment.
Oops, looks like the icons were accidentally removed here.
| footerText := "Exit: esc" // Example footer text for height calculation | ||
| footer := ui.GetStyleHelp().Render(footerText) | ||
| footerHeight := lipgloss.Height(footer) | ||
| availableHeight := m.height - headerHeight - footerHeight |
There was a problem hiding this comment.
We need to account for the border here to fix the help screen viewport
| availableHeight := m.height - headerHeight - footerHeight | |
| availableHeight := m.height - headerHeight - footerHeight - (2 * ui.BorderSize) |
New Features Added:
Ctrl+Gfor generating output files (as an intuitive alternative to the existinggkey)Modified Functionality:
ggare detectedgtwice within 500ms triggers theggcommand)Why I Made These Changes
These improvements address several usability issues:
Ctrl+Gshortcut aligns with standard patterns in other applications, making the interface more intuitive