Skip to content

feat: add goto interactive modal for quick navigation#1347

Open
robert-zaremba wants to merge 3 commits intoyorukot:mainfrom
robert-zaremba:goto_interactive
Open

feat: add goto interactive modal for quick navigation#1347
robert-zaremba wants to merge 3 commits intoyorukot:mainfrom
robert-zaremba:goto_interactive

Conversation

@robert-zaremba
Copy link
Copy Markdown
Contributor

@robert-zaremba robert-zaremba commented Feb 3, 2026

Summary

Adds a new modal accessible via the 'g' hotkey that provides:

  • Fuzzy search for files and directories
  • Interactive tab-based autocomplete for directories
  • Quick navigation to parent directory via ".."
  • Keyboard navigation with arrow keys and page up/down
  • Loops through the results list
  • Autocompletes based on cursor selection

Usage

  1. Press g to open the Goto modal
  2. Type to filter files/directories
  3. Use arrows to navigate through results
  4. Press Tab to autocomplete (cursor selection takes priority)
  5. Press Enter to navigate to directory or open file
  6. Type ".." or select ".." and press Tab to go to parent directory
  7. Press Page Up/Down to quickly scroll through results
  8. Press Esc to close without selecting anything

Key Features

Navigation

  • Fuzzy Search: Type to filter files/directories in current directory
  • Tab Autocomplete:
    • On a directory → Navigate into it
    • On a file → Fill in the filename
    • On ".." → Navigate to parent directory
  • Cursor-based Tab: Use arrows to select, then Tab to autocomplete
  • Parent Directory: Type ".." or press ".." in the list to go up
  • Keyboard Navigation:
    • Up/Down arrows: Move one item
    • Page Up/Down: Move 14 items at a time
    • Wraps around at ends of list

Visual

  • Wider modal (240 chars initial, 80 chars minimum)
  • Shows 15 results in selection window
  • Title: "Goto: /current/path" (truncated with suffix if needed)
  • Directory and file icons
  • Scroll indicators for long lists

Hotkey

  • Press g to open the modal
  • Press Esc or Ctrl+C to close
  • Press Enter to confirm selection

Summary by CodeRabbit

  • New Features
    • Interactive "goto" modal (hotkey: 'g') for quick file/directory navigation with search filtering, tab-completion, cursor & page navigation, open/CD actions, and clear "no results" feedback. Opens from the current panel path and adapts size for improved visibility.
  • Tests
    • Added unit tests covering modal lifecycle, navigation, filtering, and edge cases.

Add a new "Goto Interactive" modal that allows users to quickly navigate to files and directories with fuzzy search and autocomplete.

Features:
- Fuzzy search filtering for files and directories in current directory
- Tab autocomplete to navigate into directories or autocomplete filenames
- Support for ".." to navigate to parent directory
- Keyboard navigation with up/down arrows and page up/down
- Looping navigation through results
- Shows ".." in the results list when parent directory exists
- Pressing Enter on a file opens it, on a directory navigates into it
- Wider modal window (240 chars initial, 80 chars minimum) for better visibility
- Title changed to "Goto:" with truncated path display (suffix instead of prefix)
- Hotkey binding: press 'g' to open the modal
Implementation:
- New package: src/internal/ui/gotointeractive/
- Added to main model with proper integration
- Config hotkey: open_goto_interactive = ['g', '']
- Comprehensive test coverage

Signed-off-by: Robert Zaremba <robert@zaremba.ch>
Copilot AI review requested due to automatic review settings February 3, 2026 21:51
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 3, 2026

📝 Walkthrough

Walkthrough

Adds an interactive "goto" modal component and integrates it into the main model, exposes a new hotkey binding to open it, and introduces the gotointeractive package (types, model, utils, render, tests) plus wiring in core model, key handling, defaults, and hotkey configs.

Changes

Cohort / File(s) Summary
Configuration & Hotkeys
src/internal/common/config_type.go, src/superfile_config/hotkeys.toml, src/superfile_config/vimHotkeys.toml
Added OpenGotoInteractive []string to HotkeysType and registered open_goto_interactive = ['g',''] in both hotkey TOML files.
Core model integration
src/internal/type.go, src/internal/model.go, src/internal/key_function.go, src/internal/handle_panel_movement.go, src/internal/default_config.go
Wired new gotoModal field into model struct, added setGotoModelSize, applyGotoModalAction, update/render handling for goto modal, hotkey handling to open modal (gotoModalOpen()), and initialized the modal in default config.
Gotointeractive — types & consts
src/internal/ui/gotointeractive/type.go, src/internal/ui/gotointeractive/consts.go
New public types (Result, Model, UpdateMsg) and exported constants GotoMinWidth / GotoMinHeight plus helper prompt text.
Gotointeractive — behavior & utils
src/internal/ui/gotointeractive/model.go, src/internal/ui/gotointeractive/utils.go
New interactive modal logic: constructors (DefaultModel/GenerateModel), HandleUpdate loop, async query command (GetQueryCmd) + UpdateMsg handling, navigation/confirm/cancel/tab-completion, and state accessors/mutators (Open/Close/SetWidth/SetMaxHeight/SetCurrentPath).
Gotointeractive — rendering & tests
src/internal/ui/gotointeractive/render.go, src/internal/ui/gotointeractive/model_test.go
Render() implementation with visible-result window, truncation, file/dir markers and scroll indicators; comprehensive unit tests for lifecycle, sizing, updates, navigation, and filtering.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Input as "Input Handler"
    participant Main as "Main Model"
    participant Goto as "GotoModal"
    participant FS as "FileSystem"

    User->>Input: press hotkey (g)
    Input->>Main: dispatch OpenGotoInteractive
    Main->>Goto: SetCurrentPath(current panel) + Open()
    Goto->>Goto: GetQueryCmd() (initial query)
    User->>Input: type query
    Input->>Goto: HandleUpdate(KeyMsg)
    Goto->>FS: ReadDir/Stat(currentPath)
    FS-->>Goto: entries
    Goto->>Goto: filterResults + updateResults
    Goto-->>Main: send UpdateMsg(results)
    Main->>Goto: Apply(UpdateMsg)
    User->>Input: navigate / confirm
    Input->>Goto: HandleUpdate(KeyMsg)
    Goto->>Main: return ModelAction (open/cd)
    Main->>Main: applyGotoModalAction()
    Main->>Goto: Close()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

new feature, test related, awaiting pr review

Suggested reviewers

  • lazysegtree
  • yorukot

Poem

🐇 I hopped in through the terminal light,
A tiny goto to guide your flight,
Type and see the paths appear,
Up, down, confirm — the folder's near,
Hooray! Off you go, swift and bright.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a goto interactive modal for quick navigation, which is the primary feature introduced across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@src/internal/ui/gotointeractive/model.go`:
- Around line 186-196: GetQueryCmd currently captures the receiver m and calls
m.filterResults(query) later, which can produce results that are filtered
against a changed m.currentPath; to fix, capture the current path at command
creation (e.g., path := m.currentPath) and either call a refactored
filterResults(query, path) or pass the captured path into NewUpdateMsg so Apply
can verify both query and path staleness; update filterResults to accept a path
parameter (or make it a standalone function) and add path validation in Apply
(compare msg.path to m.currentPath) to drop stale results.

In `@src/internal/ui/gotointeractive/utils.go`:
- Line 50: Update the slog.Warn call that currently logs "Goto initialized with
too less maxHeight" (in src/internal/ui/gotointeractive/utils.go) to use correct
grammar by replacing the message with "Goto initialized with insufficient
maxHeight"; locate the slog.Warn invocation (search for slog.Warn("Goto
initialized with too less maxHeight", "maxHeight", maxHeight)) and change only
the human-readable message text, leaving the "maxHeight" key and maxHeight
variable intact.
- Around line 41-42: The warning message string passed to slog.Warn in the Goto
initialization should be corrected for grammar: update the call in the block
that sets width to GotoMinWidth (the slog.Warn invocation referencing "width"
and variable width) to use a clearer phrase such as "insufficient width" or "too
little width" instead of "too less width" while keeping the same structured
field ("width", width).
🧹 Nitpick comments (3)
src/internal/ui/gotointeractive/render.go (2)

43-55: Consider caching directory status to avoid repeated os.Stat calls during render.

The os.Stat call inside renderVisibleResults executes for each visible item on every render frame. With 15 visible results, this means 15 syscalls per render, which could cause UI lag especially on slow filesystems or network mounts.

Consider determining isDir status when results are filtered (in filterResults) and storing it alongside the result strings, or caching the stat results.

💡 Suggested approach

Store results as a struct with name and isDir fields instead of just strings:

type resultItem struct {
    name  string
    isDir bool
}

Then populate isDir once in filterResults using entry.IsDir() from the os.DirEntry which is already available there without additional syscalls.


16-20: Minor: ANSI-aware width calculation for title truncation.

len(m.headline) returns byte length, but the headline contains icon characters (nerd font glyphs) which may have different display widths. Consider using ansi.StringWidth(m.headline) for accurate width calculation, consistent with how TruncateTextBeginning handles ANSI width internally.

💡 Proposed fix
-	availableWidth := m.width - len(m.headline) - 3 //nolint:mnd // Space for " - " separator
+	availableWidth := m.width - ansi.StringWidth(m.headline) - 3 //nolint:mnd // Space for " - " separator

You'll need to import "github.com/charmbracelet/x/ansi".

src/internal/ui/gotointeractive/model.go (1)

215-260: Consider refactoring filterResults to reduce code duplication.

The function has two similar code paths: one when a parent directory exists (lines 217-244) and one when at root (lines 246-259). Both perform the same directory reading and filtering logic, just with different handling of the ".." entry.

♻️ Proposed simplification
 func (m *Model) filterResults(query string) []string {
 	parentDir := filepath.Dir(m.currentPath)
-	if parentDir != m.currentPath {
-		hasParent := true
-		if query != "" {
-			queryLower := strings.ToLower(query)
-			hasParent = strings.Contains(strings.ToLower(".."), queryLower)
-		}
-
-		entries, err := os.ReadDir(m.currentPath)
-		if err != nil {
-			if hasParent {
-				return []string{".."}
-			}
-			return []string{}
-		}
-
-		results := make([]string, 0, len(entries)+1)
-		if hasParent {
-			results = append(results, "..")
-		}
-
-		for _, entry := range entries {
-			name := entry.Name()
-			if query == "" || strings.Contains(strings.ToLower(name), strings.ToLower(query)) {
-				results = append(results, name)
-			}
-		}
-		return results
-	}
-
+	hasParent := parentDir != m.currentPath
+	queryLower := strings.ToLower(query)
+	
 	entries, err := os.ReadDir(m.currentPath)
 	if err != nil {
+		if hasParent && (query == "" || strings.Contains("..", queryLower)) {
+			return []string{".."}
+		}
 		return []string{}
 	}
 
-	queryLower := strings.ToLower(query)
-	results := make([]string, 0, len(entries))
+	results := make([]string, 0, len(entries)+1)
+	if hasParent && (query == "" || strings.Contains("..", queryLower)) {
+		results = append(results, "..")
+	}
+	
 	for _, entry := range entries {
 		name := entry.Name()
-		if strings.Contains(strings.ToLower(name), queryLower) {
+		if query == "" || strings.Contains(strings.ToLower(name), queryLower) {
 			results = append(results, name)
 		}
 	}
 	return results
 }

Comment thread src/internal/ui/gotointeractive/model.go
Comment thread src/internal/ui/gotointeractive/utils.go Outdated
Comment thread src/internal/ui/gotointeractive/utils.go Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “Goto” interactive modal (opened via the g hotkey) to quickly navigate/open items in the current panel using a searchable, keyboard-navigable result list.

Changes:

  • Adds open_goto_interactive hotkey to the default and vim-like hotkey TOML configs and wires it into the hotkeys struct.
  • Introduces a new gotointeractive UI package (model, rendering, navigation/filtering logic, basic tests).
  • Integrates the modal into the main application model (state, sizing, update loop, overlay rendering, and key dispatch).

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/superfile_config/vimHotkeys.toml Adds default vim-like binding for open_goto_interactive.
src/superfile_config/hotkeys.toml Adds default binding for open_goto_interactive.
src/internal/ui/gotointeractive/consts.go Adds modal sizing and rendering constants + prompt helper.
src/internal/ui/gotointeractive/type.go Defines Model and async UpdateMsg type for goto queries.
src/internal/ui/gotointeractive/utils.go Adds open/close/state + dimension/result accessors.
src/internal/ui/gotointeractive/model.go Implements input handling, filtering, navigation, and async query command plumbing.
src/internal/ui/gotointeractive/render.go Implements modal rendering of input + results list with scroll indicators.
src/internal/ui/gotointeractive/model_test.go Adds initial unit tests for construction/open/close/dimensions/update msg.
src/internal/type.go Adds gotoModal field to the main model struct and imports the package.
src/internal/model.go Routes goto UpdateMsg, sizes the modal, handles modal updates, and renders it as an overlay.
src/internal/key_function.go Adds g hotkey handling to open the goto modal.
src/internal/handle_panel_movement.go Adds gotoModalOpen() helper to set path from focused panel and open modal.
src/internal/default_config.go Initializes the goto modal in the default model config.
src/internal/common/config_type.go Adds OpenGotoInteractive to the hotkeys config struct for TOML parsing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/internal/ui/gotointeractive/model.go Outdated
Comment thread src/internal/ui/gotointeractive/model.go
Comment thread src/internal/ui/gotointeractive/model.go Outdated
Comment thread src/internal/ui/gotointeractive/model.go
Comment thread src/internal/ui/gotointeractive/render.go Outdated
Comment thread src/internal/ui/gotointeractive/render.go Outdated
robert-zaremba and others added 2 commits February 3, 2026 23:18
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Robert Zaremba <robert@zaremba.ch>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/internal/ui/gotointeractive/render.go`:
- Around line 15-22: The availableWidth calculation uses m.width (total modal
width) but ignores left/right border chars; update the logic in render.go so
availableWidth is computed from the content width (either subtract 2 for
borders: availableWidth := m.width - 2 - headlineWidth - separatorWidth, or read
the content width provided by PromptRenderer when BorderRequired = true), and
then use that adjusted availableWidth before calling
common.TruncateTextBeginning on titlePath; update any related variables (m.width
usage in this block) to reference the content width instead of the total width.
🧹 Nitpick comments (3)
src/internal/ui/gotointeractive/utils.go (1)

66-69: Consider whether updateResults() should return a tea.Cmd for consistency.

SetCurrentPath calls updateResults() synchronously, which performs file I/O. While this may be acceptable for the current use case, other path-changing operations like handleGoUp() in model.go also call updateResults() synchronously. If directory reads become slow (network mounts, large directories), this could block the UI.

For now this is fine given the interactive modal context, but worth noting for future async refactoring if performance issues arise.

src/internal/ui/gotointeractive/model.go (1)

280-285: Minor: Redundant ToLower on constant string "..".

The string ".." contains no alphabetic characters, so strings.ToLower("..") is always "..".

📝 Proposed simplification
 	if hasParent {
 		queryLower := strings.ToLower(query)
-		if strings.Contains(strings.ToLower(".."), queryLower) {
+		if strings.Contains("..", queryLower) {
 			results = append(results, Result{Name: "..", IsDir: true})
 		}
 	}
src/internal/ui/gotointeractive/render.go (1)

69-84: Consider showing both scroll indicators simultaneously when applicable.

Currently, if m.renderIndex > 0 (more results above), a section is added before the "above" indicator. Then if there are also more results below, no new section is added (line 79-81 check). However, both indicators could be shown together when the user is in the middle of the list.

The current logic appears correct for this case, but verify that both indicators display when scrolled to the middle of a long list.

#!/bin/bash
# Check if there are tests covering scroll indicator behavior
rg -n "renderScrollIndicators|More results" src/internal/ui/gotointeractive/

Comment on lines +15 to +22
separator := " "
headlineWidth := lipgloss.Width(m.headline)
separatorWidth := lipgloss.Width(separator)
availableWidth := m.width - headlineWidth - separatorWidth
titlePath := m.currentPath
if lipgloss.Width(titlePath) > availableWidth {
titlePath = common.TruncateTextBeginning(titlePath, availableWidth, "...")
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, find the PromptRenderer struct definition and understand m.width
fd render.go src/internal/ui/gotointeractive/

Repository: yorukot/superfile

Length of output: 102


🏁 Script executed:

# Search for PromptRenderer struct definition and width field
rg -n "type PromptRenderer\|m.width\|type.*Renderer" src/internal/ui/gotointeractive/ -A 5 -B 2

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Check how the render function sets m.width and if borders are added
cat -n src/internal/ui/gotointeractive/render.go

Repository: yorukot/superfile

Length of output: 2762


🏁 Script executed:

# Look for border rendering in the PromptRenderer
rg -n "border\|Border\|lipgloss" src/internal/ui/gotointeractive/render.go -A 2 -B 2

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Find PromptRenderer function definition
rg -n "func PromptRenderer" src/internal/ui/ -A 5

Repository: yorukot/superfile

Length of output: 528


🏁 Script executed:

# Find Model struct definition in gotointeractive
rg -n "type Model struct" src/internal/ui/gotointeractive/ -A 10

Repository: yorukot/superfile

Length of output: 729


🏁 Script executed:

# Look at rendering.Renderer struct and SetBorderTitle
rg -n "SetBorderTitle\|type Renderer" src/internal/ui/rendering/ -A 5

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Check how Model.width is initialized
rg -n "m.width\s*=" src/internal/ui/gotointeractive/ -B 3 -A 1

Repository: yorukot/superfile

Length of output: 439


🏁 Script executed:

# Look at DefaultRendererConfig to understand how width is handled with borders
rg -n "DefaultRendererConfig\|func.*RendererConfig" src/internal/ui/rendering/ -A 10

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Check the rendering.Renderer implementation for border handling
rg -n "type Renderer struct" src/internal/ui/rendering/ -A 20

Repository: yorukot/superfile

Length of output: 1683


🏁 Script executed:

# Look at SetBorderTitle and Render methods
rg -n "func.*SetBorderTitle\|func.*Render\(\)" src/internal/ui/rendering/ -A 8 | head -50

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Look at full context around m.width initialization in utils.go
sed -n '35,55p' src/internal/ui/gotointeractive/utils.go

Repository: yorukot/superfile

Length of output: 565


🏁 Script executed:

# Check what GotoMinWidth and modalInputPadding are
rg -n "GotoMinWidth\|modalInputPadding" src/internal/ui/gotointeractive/

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Search for constants in gotointeractive package
rg -n "GotoMinWidth\|modalInputPadding\|const" src/internal/ui/gotointeractive/ -B 1 -A 1 | head -40

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Look at the complete rendering.Renderer initialization and border handling
cat -n src/internal/ui/rendering/renderer.go | head -80

Repository: yorukot/superfile

Length of output: 3222


🏁 Script executed:

# Search for how border width affects content width in Renderer
rg -n "contentWidth\|GetBorder\|borderWidth" src/internal/ui/rendering/ -A 3

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Check the Render function of Renderer to see how borders are applied
rg -n "func.*\(r \*Renderer\).*Render" src/internal/ui/rendering/renderer.go -A 20

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Search for DefaultRendererConfig function
rg -n "func DefaultRendererConfig" src/internal/ui/rendering/ -A 20

Repository: yorukot/superfile

Length of output: 1608


🏁 Script executed:

# Look for where contentWidth is calculated from totalWidth
rg -n "contentWidth\s*=" src/internal/ui/rendering/ -B 2 -A 2

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Check the BorderConfig.GetBorder method mentioned in the learnings
rg -n "GetBorder\|contentWidth" src/internal/ui/rendering/border.go -B 3 -A 5

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Look at how the Renderer.Render() method applies borders
rg -n "func \(r \*Renderer\) Render" src/internal/ui/rendering/renderer.go -A 30

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Look at the full border.go file to understand border width handling
cat -n src/internal/ui/rendering/border.go

Repository: yorukot/superfile

Length of output: 5481


🏁 Script executed:

# Look at NewRenderer function to see how contentWidth is set
sed -n '108,200p' src/internal/ui/rendering/renderer.go

Repository: yorukot/superfile

Length of output: 1516


🏁 Script executed:

# Search for any place where contentWidth or border width subtraction happens
rg -n "contentWidth|totalWidth.*2|totalWidth.*border" src/internal/ui/rendering/

Repository: yorukot/superfile

Length of output: 1462


🏁 Script executed:

# Check if SetBorderTitle enables borders
rg -n "func.*SetBorderTitle\|SetBorderTitle" src/internal/ui/rendering/ -A 10

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Look at the spf_renderers.go to see if borders are explicitly set in PromptRenderer
cat -n src/internal/ui/spf_renderers.go | sed -n '84,110p'

Repository: yorukot/superfile

Length of output: 1160


🏁 Script executed:

# Check if borders are applied elsewhere in the code
rg -n "BorderRequired.*true\|SetBorder\|Border.*true" src/internal/ui/gotointeractive/

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Check the constants for border width
rg -n "borderCornerWidth\|borderDividerWidth\|borderPaddingWidth\|minTitleWidth" src/internal/ui/rendering/ | head -20

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Search for constants definition
rg -n "const.*borderCornerWidth|const.*borderDividerWidth" src/internal/ui/rendering/

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Check if m.width is ever adjusted for borders in gotointeractive
rg -n "m.width\|SetWidth" src/internal/ui/gotointeractive/ -B 2 -A 2

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Look at the constants file to understand the dimensions
cat -n src/internal/ui/rendering/consts.go

Repository: yorukot/superfile

Length of output: 128


🏁 Script executed:

# Find where the border constants are defined
rg -n "borderCornerWidth\|borderDividerWidth" src/ -B 2 -A 1

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# List all files in rendering directory
fd . src/internal/ui/rendering/ -type f

Repository: yorukot/superfile

Length of output: 231


🏁 Script executed:

# Look at render_core.go which was mentioned in the search results
cat -n src/internal/ui/rendering/renderer_core.go

Repository: yorukot/superfile

Length of output: 5453


🏁 Script executed:

# Look for how m.width is set and documented
rg -n "width.*int\|SetWidth" src/internal/ui/gotointeractive/ -B 2 -A 2 | head -40

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Check the complete render.go to see all width calculations
cat -n src/internal/ui/gotointeractive/render.go | grep -E "width|Width" -n

Repository: yorukot/superfile

Length of output: 674


🏁 Script executed:

# Look at lines 45-55 to see the other width calculation in renderVisibleResults
sed -n '45,70p' src/internal/ui/gotointeractive/render.go

Repository: yorukot/superfile

Length of output: 794


🏁 Script executed:

# Verify if contentWidth is exposed or used anywhere in gotointeractive
rg -n "contentWidth\|GetContentWidth\|GetWidth" src/internal/ui/gotointeractive/

Repository: yorukot/superfile

Length of output: 43


🏁 Script executed:

# Search for constant definitions in border.go
head -100 src/internal/ui/rendering/border.go | tail -50

Repository: yorukot/superfile

Length of output: 1863


🏁 Script executed:

# Look for constant definitions
rg -n "const|borderCornerWidth|borderDividerWidth|borderPaddingWidth" src/internal/ui/rendering/border.go

Repository: yorukot/superfile

Length of output: 503


🏁 Script executed:

# Check if there are any constants in other files that might define border widths
rg -n "^const" src/internal/ui/rendering/

Repository: yorukot/superfile

Length of output: 212


🏁 Script executed:

# Look at end of border.go for constant definitions
tail -50 src/internal/ui/rendering/border.go

Repository: yorukot/superfile

Length of output: 1643


🏁 Script executed:

# Look at the constants.go file
cat -n src/internal/ui/rendering/constants.go

Repository: yorukot/superfile

Length of output: 849


Account for border width when calculating available width for title path.

The availableWidth calculation on line 18 uses m.width directly, but m.width is the total modal width including the left and right border characters (2 characters total). Since PromptRenderer sets BorderRequired = true, the actual content width available for the title is m.width - 2. The calculation should be:

availableWidth := m.width - 2 - headlineWidth - separatorWidth

Alternatively, have PromptRenderer return the content width separately so it can be used for width calculations in the modal, similar to how the sidebar component (from retrieved learnings) distinguishes between SidebarWidth (content) and total width (including borders).

🤖 Prompt for AI Agents
In `@src/internal/ui/gotointeractive/render.go` around lines 15 - 22, The
availableWidth calculation uses m.width (total modal width) but ignores
left/right border chars; update the logic in render.go so availableWidth is
computed from the content width (either subtract 2 for borders: availableWidth
:= m.width - 2 - headlineWidth - separatorWidth, or read the content width
provided by PromptRenderer when BorderRequired = true), and then use that
adjusted availableWidth before calling common.TruncateTextBeginning on
titlePath; update any related variables (m.width usage in this block) to
reference the content width instead of the total width.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can solve the problem above if there is a consensus to merge this feature

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robert-zaremba, thank you for your willingness to address this issue!

The border width calculation fix is indeed important for correct text truncation in the modal title. Since the PromptRenderer sets BorderRequired = true, the 2-character border width (left + right) should be subtracted from m.width when calculating availableWidth. Without this, the title path could be truncated prematurely or overflow.

I'd recommend proceeding with the fix now—it's a straightforward adjustment that will make the feature render correctly:

availableWidth := m.width - 2 - headlineWidth - separatorWidth

This will ensure your goto modal is merge-ready and displays the title path with accurate truncation. The feature looks solid overall, and addressing this rendering detail will polish it nicely.


🧠 Learnings used
Learnt from: lazysegtree
Repo: yorukot/superfile PR: 781
File: src/internal/ui/rendering/border.go:104-114
Timestamp: 2025-04-28T04:02:28.747Z
Learning: The BorderConfig.GetBorder method in src/internal/ui/rendering/border.go already protects against negative availWidth values through the condition `actualWidth >= cnt*4` which ensures that availWidth is at least 1.

Learnt from: lazysegtree
Repo: yorukot/superfile PR: 1227
File: src/internal/common/config_type.go:94-94
Timestamp: 2025-12-25T04:09:23.241Z
Learning: In the superfile sidebar component (src/internal/ui/sidebar/), the SidebarWidth configuration value represents the sidebar content width excluding borders, while the minWidth = 7 constant in consts.go represents the minimum total width including borders. The actual sidebar width is computed as SidebarWidth + BorderPadding in sidebar.go's New() function.

Learnt from: lazysegtree
Repo: yorukot/superfile PR: 1044
File: src/internal/ui/rendering/truncate.go:6-6
Timestamp: 2025-09-09T14:23:14.164Z
Learning: The truncate test failure in src/internal/ui/rendering/truncate_test.go during the ANSI package migration from experimental to stable was caused by a truncation bug in the experimental package. The experimental package incorrectly truncated strings even when input length equaled maxWidth (e.g., "1234" with maxWidth=4 became "1..."), while the stable package correctly only truncates when input exceeds maxWidth. The test expectation was based on the buggy behavior and needs to be corrected.

Learnt from: lazysegtree
Repo: yorukot/superfile PR: 825
File: src/internal/ui/rendering/renderer_core.go:95-108
Timestamp: 2025-05-22T04:42:07.369Z
Learning: In src/internal/ui/rendering/renderer_core.go, when truncating rendered output that exceeds the maximum height, we should drop all remaining lines after reaching totalHeight without adding an ellipsis or other truncation indicator, as the goal is to have exactly totalHeight lines.

Learnt from: lazysegtree
Repo: yorukot/superfile PR: 973
File: src/internal/ui/processbar/model.go:118-120
Timestamp: 2025-08-03T15:07:56.341Z
Learning: In the processbar package (src/internal/ui/processbar/model.go), the Progress.Width field is modified temporarily during rendering on a copy of the process data, and this modification is intentionally not persisted to the stored process data since it's only needed for display purposes during the render call.

Learnt from: lazysegtree
Repo: yorukot/superfile PR: 770
File: src/internal/ui/sidebar/render.go:0-0
Timestamp: 2025-04-12T13:51:24.691Z
Learning: In the sidebar component of yorukot/superfile, the partial list rendering in render.go is intentional. The sidebar implements scrolling functionality where ListUp/ListDown methods in navigation.go update cursor position and renderIndex to show the appropriate portion of the directory list based on available space.

Learnt from: lazysegtree
Repo: yorukot/superfile PR: 770
File: src/internal/ui/sidebar/render.go:0-0
Timestamp: 2025-04-12T13:51:24.691Z
Learning: In the sidebar component of yorukot/superfile, the partial list rendering in render.go is intentional. The sidebar implements scrolling functionality where ListUp/ListDown methods in navigation.go update cursor position and renderIndex to show the appropriate portion of the directory list based on available space.

Learnt from: lazysegtree
Repo: yorukot/superfile PR: 0
File: :0-0
Timestamp: 2025-04-12T12:00:32.688Z
Learning: In PR `#767` for yorukot/superfile, the focus is on moving code (especially sidebar-related functionality) to a more organized structure without changing functionality. Pre-existing issues should be ignored since the purpose is code reorganization, not fixing existing problems.

@yorukot
Copy link
Copy Markdown
Owner

yorukot commented Apr 4, 2026

Hey @robert-zaremba, first of all, sorry for the late review, and thanks for your effort on this PR!

I have a quick question: what is the difference between the go-to panel and the search functionality? Also, what are the intended use cases for the goto panel?

@yorukot yorukot added waiting for info Issue is not actionable due to some required info is missing and needs to be provided and removed awaiting pr review labels Apr 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting for info Issue is not actionable due to some required info is missing and needs to be provided

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants