Skip to content

Conversation

@iZarrios
Copy link
Contributor

@iZarrios iZarrios commented Aug 23, 2025

Closes #1009

  • I have run go fmt ./... to format the code
  • I have run golangci-lint run and fixed any reported issues
  • I have tested my changes and verified they work as expected
  • I have reviewed the diff to make sure I’m not committing any debug logs or TODOs
  • I have filled out the PR template with description, context, and screenshots if needed

Summary by CodeRabbit

  • New Features

    • Help menu gains a searchable bar with fuzzy matching and a hotkey to focus it; typing filters results live.
  • Improvements

    • Rendering and layout updated for cleaner, consistent help menu visuals and dynamic sizing of the search bar.
    • Navigation, counts, and displayed items reflect filtered results for accurate layout.
  • Bug Fixes

    • Safer navigation with bounds checks, wrap behavior, and search state reset when closing.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 23, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbit review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Adds a searchable help menu by introducing a searchBar and filteredData in the helpMenu modal, a constructor newHelpMenuModal, fuzzy filtering pipeline and navigation updates, search-aware key handling, Update integration for the search input, and renderer-based help menu rendering.

Changes

Cohort / File(s) Summary
Types & Initialization
src/internal/type.go, src/internal/default_config.go
Adds filteredData []helpMenuModalData and searchBar textinput.Model to helpMenuModal; adds newHelpMenuModal() and replaces inline help menu init with it.
Filtering & Modal Control
src/internal/handle_modal.go
Adds fuzzySearch, removeOrphanSections, and filterHelpMenu; open/quit and navigation now operate on filteredData, reset search state when opening/closing, and guard cursor/render indices.
Input Handling
src/internal/key_function.go
helpMenuKey becomes search-aware: typing updates filter when searchBar focused, Confirm/Cancel blur the searchBar; navigation delegated to handleHelpMenuNavKeys; adds SearchBar hotkey to focus input.
Update Loop & Sizing
src/internal/model.go
Model.Update manually updates helpMenu.searchBar when focused and includes its command in the Tea batch; setHelpMenuSize syncs searchBar width to modal width.
Rendering & Renderer
src/internal/model_render.go, src/internal/ui/spf_renderers.go
Introduces HelpMenuRenderer(...), switches helpMenuRender to use renderer pipeline, prepends rendered search bar, and renders content from filteredData (key-length and layout updated).
Styling cleanup
src/internal/common/style_function.go
Removes HelpMenuModalBorderStyle(...) (function deleted).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User
    participant Model
    participant KeyHandler as helpMenuKey
    participant SearchBar
    participant Filter as filterHelpMenu
    participant Renderer

    User->>Model: keystroke
    Model->>KeyHandler: route help-menu keys
    alt SearchBar focused
        KeyHandler->>SearchBar: Update(msg)
        SearchBar-->>KeyHandler: value (query)
        KeyHandler->>Filter: filterHelpMenu(query)
        Filter-->>Model: update filteredData, reset cursor/index
    else Not focused
        KeyHandler->>Model: navigate (ListUp/Down) or focus SearchBar
    end
    Model->>Renderer: render with filteredData + searchBar
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Assessment against linked issues

Objective Addressed Explanation
Add search functionality in help menu; add searchbox component to the help menu [#1009]

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Removed HelpMenuModalBorderStyle (function deletion) (src/internal/common/style_function.go) Styling helper removal is not required by the search objective; it alters public-style helper surface unrelated to adding the search box.

Possibly related PRs

  • Model unit tests #803 — touches defaultModelConfig / help menu initialization; likely related to the refactor of help menu construction.

Poem

I tap my paws: slash, star, and key,
A search bar sprouts where tips roam free.
Fuzzy hops and filtered trails,
Orphaned hints go soft as tails.
Cursor twitches — results for me. 🐇✨

✨ Finishing Touches
🧪 Generate unit tests
  • 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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

codescene-delta-analysis[bot]

This comment was marked as outdated.

Copy link
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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/internal/model_render.go (1)

343-352: Separator length math bug inflates/deflates column width

saprateLen := len(data.hotkey) - 13 computes (len-3), not 3(len-1). This miscalculates spacing between hotkeys, skewing maxKeyLength and downstream width allocation.

Apply:

-    saprateLen := len(data.hotkey) - 1*3
+    // Each separator " | " is 3 chars; there are (n-1) separators for n hotkeys
+    separatorLen := max(0, (len(data.hotkey)-1)) * 3
-    if data.subTitle == "" && totalKeyLen+saprateLen > maxKeyLength {
-      maxKeyLength = totalKeyLen + saprateLen
+    if data.subTitle == "" && totalKeyLen+separatorLen > maxKeyLength {
+      maxKeyLength = totalKeyLen + separatorLen
     }

Also consider renaming saprateLen -> separatorLen for readability.

🧹 Nitpick comments (8)
src/internal/type.go (1)

118-126: New fields align with the search feature; consider enforcing cursor invariants

The additions of filteredData and searchBar are consistent with the PR wiring and other components using textinput. To prevent edge-cases when filtering (e.g., cursor pointing at a subtitle or out-of-range after filter shrinks the list), consider encapsulating cursor/render bounds in helpers (e.g., helpMenu.clampCursorToNextItem()) and calling them after each filter/reset.

If helpful, I can draft a small helper API on helpMenuModal to clamp cursor/render to the nearest non-title item.

src/internal/default_config.go (1)

46-57: Constructor looks correct; consider initial cursor semantics

Initializing cursor to 1 matches the first non-title entry in getHelpMenuData() (“General” is a title at index 0). Keep this in sync if the dataset changes ordering. Optionally, compute the initial cursor dynamically (first non-title index) to avoid future drift.

Example adjustment:

 func newHelpMenuModal() helpMenuModal {
   helpMenuData := getHelpMenuData()
+  // Find first non-title row to place the cursor on an actionable item
+  firstItem := 0
+  for i, d := range helpMenuData {
+    if d.subTitle == "" {
+      firstItem = i
+      break
+    }
+  }
   return helpMenuModal{
-    renderIndex:  0,
-    cursor:       1,
+    renderIndex:  0,
+    cursor:       firstItem,
     data:         helpMenuData,
     filteredData: helpMenuData,
     open:         false,
     searchBar:    common.GenerateSearchBar(),
   }
 }
src/internal/model.go (1)

289-290: Size sync for search bar

Keeping the search bar width in sync with the modal width is correct. Consider guarding against very small widths (rare) to avoid negative/zero widths.

- m.helpMenu.searchBar.Width = m.helpMenu.width - 4
+ if w := m.helpMenu.width - 4; w > 0 {
+   m.helpMenu.searchBar.Width = w
+ }
src/internal/model_render.go (2)

383-399: Column width should use visible width, not ANSI-styled byte length

len(common.HelpMenuHotkeyStyle.Render(hotkey)) counts escape sequences and can misalign columns. Use visible rune width.

Apply:

-  renderHotkeyLength := 0
+  renderHotkeyLength := 0
+  // Compute visible width; import "github.com/charmbracelet/x/ansi" as ansiwidth
   for i := m.helpMenu.renderIndex; i < m.helpMenu.height+m.helpMenu.renderIndex && i < len(m.helpMenu.filteredData); i++ {
     hotkey := ""
     if m.helpMenu.filteredData[i].subTitle != "" {
       continue
     }
-    for i, key := range m.helpMenu.filteredData[i].hotkey {
-      if i != 0 {
+    for j, key := range m.helpMenu.filteredData[i].hotkey {
+      if j != 0 {
         hotkey += " | "
       }
       hotkey += key
     }
-    renderHotkeyLength = max(renderHotkeyLength, len(common.HelpMenuHotkeyStyle.Render(hotkey)))
+    renderHotkeyLength = max(renderHotkeyLength, ansiwidth.StringWidth(hotkey))
   }

Additional change outside this hunk:

  • Add import alias in this file’s imports:
    ansiwidth "github.com/charmbracelet/x/ansi"

403-432: Minor cleanups: shadowed loop variable and safer bottom border on empty results

  • The inner loop uses i in for i, key := range m.helpMenu.filteredData[i].hotkey which shadows the outer loop’s i. Rename inner index to j for clarity.
  • When the filter returns only titles or zero actionable items, bottom counter can become 1/0 or negative on the left term. Clamp current index to [0, totalItems], and display 0 when no items.

Apply:

- for i, key := range m.helpMenu.filteredData[i].hotkey {
+ for j, key := range m.helpMenu.filteredData[i].hotkey {
-   if i != 0 {
+   if j != 0 {
      hotkey += " | "
    }
    hotkey += key
  }

And in helpMenuRender (where bottomBorder is constructed), after computing totalTitleCount:

- bottomBorder := common.GenerateFooterBorder(fmt.Sprintf("%s/%s",
-   strconv.Itoa(m.helpMenu.cursor+1-cursorBeenTitleCount),
-   strconv.Itoa(len(m.helpMenu.filteredData)-totalTitleCount)), m.helpMenu.width-2)
+ totalItems := max(0, len(m.helpMenu.filteredData)-totalTitleCount)
+ currentItem := m.helpMenu.cursor + 1 - cursorBeenTitleCount
+ if currentItem < 0 || totalItems == 0 {
+   currentItem = 0
+ } else if currentItem > totalItems {
+   currentItem = totalItems
+ }
+ bottomBorder := common.GenerateFooterBorder(
+   fmt.Sprintf("%d/%d", currentItem, totalItems),
+   m.helpMenu.width-2,
+ )
src/internal/handle_modal.go (3)

193-211: Orphan-section pruning is fine; consider a single-pass variant for clarity.

Current look-ahead works with the data model (subTitle followed by hotkeys). An alternative is to track “lastSubtitleKept” and only keep it if at least one subsequent hotkey is appended before the next subtitle, removing the need for index peeking. Nice-to-have for readability; no functional change required.


222-259: Fuzzy search blocks the UI; throttle and offload to async command.

utils.FzfSearch is a blocking call (per TODO), so invoking it on every keystroke can freeze the TUI. Mitigations:

  • Debounce/throttle queries (e.g., 120–200ms).
  • Run the search in a goroutine and return a Tea message when done.
  • Precompute the haystack/idx map once per dataset, not on each keypress.

I can sketch a Bubble Tea-friendly flow (new message types, a search cmd with context timeout, and an Update branch that consumes results). Want me to open a follow-up PR for this?


268-270: Also reset cursor/render and searchBar on open to ensure a clean slate.

Currently, toggling the menu off via openHelpMenu() doesn’t reset the searchBar, and opening doesn’t reset navigation. Suggest resetting all state in both open and toggle-close paths for consistent UX.

Apply this diff:

 func (m *model) openHelpMenu() {
   if m.helpMenu.open {
-    m.helpMenu.open = false
+    // Toggle close: reset transient state for next open.
+    m.helpMenu.searchBar.Reset()
+    m.helpMenu.open = false
     return
   }
 
   // Reset filteredData to the full data whenever the helpMenu is opened
   m.helpMenu.filteredData = m.helpMenu.data
+  m.helpMenu.cursor = 1
+  m.helpMenu.renderIndex = 0
+  m.helpMenu.searchBar.Reset()
   m.helpMenu.open = true
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 77a63fe and 47c960f.

📒 Files selected for processing (6)
  • src/internal/default_config.go (2 hunks)
  • src/internal/handle_modal.go (3 hunks)
  • src/internal/key_function.go (1 hunks)
  • src/internal/model.go (3 hunks)
  • src/internal/model_render.go (4 hunks)
  • src/internal/type.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
src/internal/model.go (1)
src/internal/ui/sidebar/sidebar.go (1)
  • UpdateState (67-79)
src/internal/default_config.go (1)
src/internal/common/style_function.go (1)
  • GenerateSearchBar (250-262)
src/internal/type.go (1)
src/internal/ui/prompt/type.go (1)
  • Model (6-30)
src/internal/key_function.go (3)
src/internal/ui/sidebar/sidebar.go (2)
  • HandleSearchBarKey (82-91)
  • UpdateState (67-79)
src/internal/handle_panel_movement.go (2)
  • m (182-192)
  • m (169-180)
src/internal/ui/sidebar/utils.go (1)
  • SearchBarFocus (54-56)
src/internal/handle_modal.go (1)
src/internal/utils/fzf_utils.go (1)
  • FzfSearch (6-14)
src/internal/model_render.go (3)
src/internal/common/style_function.go (2)
  • GenerateFooterBorder (336-343)
  • HelpMenuModalBorderStyle (159-171)
src/internal/common/style.go (1)
  • HelpMenuTitleStyle (57-57)
src/internal/common/string_function.go (1)
  • TruncateText (19-26)
🔇 Additional comments (6)
src/internal/default_config.go (1)

35-35: Good: centralized constructor for help menu

Switching to newHelpMenuModal() keeps initialization consistent and will simplify future changes to the modal state.

src/internal/model.go (2)

69-77: LGTM: manual Update() path for help menu search bar

Updating the help menu’s textinput here mirrors Sidebar’s UpdateState and ensures filterHelpMenu sees the latest value in the same tick. Good placement and ordering.


115-116: Batch includes helpMenuCmd

Returning helpMenuCmd in the Tea batch is necessary for cursor blink and textinput side-effects. All good.

src/internal/handle_modal.go (3)

8-10: Import of internal utils looks good.

The new dependency is used by fuzzySearch; no issues spotted here.


275-276: Good call resetting the search bar on quit.

This ensures the next open starts fresh.


157-167: No action required for max() usage

The repository’s go.mod specifies Go 1.25.0, so the built-in max/min functions introduced in Go 1.21 are fully supported. A search confirmed there is no local max helper, and max() is already in use elsewhere in the codebase (model_render.go, model.go, UI components, etc.). You can safely keep the current calls to max() without adding custom bounds checks.

@iZarrios iZarrios force-pushed the feat/add-help-menu-search branch from 47c960f to 8011ae1 Compare August 23, 2025 13:39
codescene-delta-analysis[bot]

This comment was marked as outdated.

Copy link
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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/internal/model_render.go (1)

404-434: Pad styled strings using lipgloss (fmt width + ANSI yields misalignment) and reserve one row for the search bar

Using fmt’s “%*s” with ANSI-styled strings pads by rune count (including escapes), not visible cells; alignment drifts. Also, render bounds should subtract the search bar’s row. Switch to lipgloss width-aware padding and a strings.Builder.

 func (m *model) getHelpMenuContent(renderHotkeyLength int, valueLength int) string {
-	helpMenuContent := ""
-	for i := m.helpMenu.renderIndex; i < m.helpMenu.height+m.helpMenu.renderIndex && i < len(m.helpMenu.filteredData); i++ {
-		if i != m.helpMenu.renderIndex {
-			helpMenuContent += "\n"
-		}
+	var b strings.Builder
+	// Visible rows for the list = total height - 1 row for the search bar
+	rows := max(0, m.helpMenu.height-1)
+	for i := m.helpMenu.renderIndex; i < rows+m.helpMenu.renderIndex && i < len(m.helpMenu.filteredData); i++ {
+		if i != m.helpMenu.renderIndex {
+			b.WriteByte('\n')
+		}
 
 		if m.helpMenu.filteredData[i].subTitle != "" {
-			helpMenuContent += common.HelpMenuTitleStyle.Render(" " + m.helpMenu.filteredData[i].subTitle)
+			b.WriteString(common.HelpMenuTitleStyle.Render(" " + m.helpMenu.filteredData[i].subTitle))
 			continue
 		}
 
 		hotkey := ""
-		description := common.TruncateText(m.helpMenu.filteredData[i].description, valueLength, "...")
+		description := common.TruncateText(m.helpMenu.filteredData[i].description, valueLength, "...")
 
-		for i, key := range m.helpMenu.filteredData[i].hotkey {
-			if i != 0 {
+		for j, key := range m.helpMenu.filteredData[i].hotkey {
+			if j != 0 {
 				hotkey += " | "
 			}
 			hotkey += key
 		}
 
 		cursor := "  "
 		if m.helpMenu.cursor == i {
 			cursor = common.FilePanelCursorStyle.Render(icon.Cursor + " ")
 		}
-		helpMenuContent += cursor + common.ModalStyle.Render(fmt.Sprintf("%*s%s", renderHotkeyLength,
-			common.HelpMenuHotkeyStyle.Render(hotkey+" "), common.ModalStyle.Render(description)))
+		// Width-aware cell for hotkey column
+		hotkeyCell := lipgloss.NewStyle().Width(renderHotkeyLength).
+			Render(common.HelpMenuHotkeyStyle.Render(hotkey + " "))
+		b.WriteString(cursor)
+		b.WriteString(hotkeyCell)
+		b.WriteString(common.ModalStyle.Render(description))
 	}
-	return helpMenuContent
+	return b.String()
 }
♻️ Duplicate comments (1)
src/internal/model_render.go (1)

383-401: Reserve one row for the search bar and measure hotkey width with lipgloss.Width

The loop still renders m.helpMenu.height rows, which, after prepending the search bar, overflows by 1 row and clips the last list item. Also, len(styled) mis-measures due to ANSI codes.

 func (m *model) getRenderHotkeyLengthHelpmenuModal() int {
-	renderHotkeyLength := 0
-	for i := m.helpMenu.renderIndex; i < m.helpMenu.height+m.helpMenu.renderIndex && i < len(m.helpMenu.filteredData); i++ {
+	renderHotkeyLength := 0
+	// Visible rows for the list = total height - 1 row for the search bar
+	rows := max(0, m.helpMenu.height-1)
+	for i := m.helpMenu.renderIndex; i < rows+m.helpMenu.renderIndex && i < len(m.helpMenu.filteredData); i++ {
 		hotkey := ""
 
 		if m.helpMenu.filteredData[i].subTitle != "" {
 			continue
 		}
 
-		for i, key := range m.helpMenu.filteredData[i].hotkey {
-			if i != 0 {
+		for j, key := range m.helpMenu.filteredData[i].hotkey {
+			if j != 0 {
 				hotkey += " | "
 			}
 			hotkey += key
 		}
 
-		renderHotkeyLength = max(renderHotkeyLength, len(common.HelpMenuHotkeyStyle.Render(hotkey)))
+		// include a trailing space so the description has breathing room
+		styled := common.HelpMenuHotkeyStyle.Render(hotkey + " ")
+		renderHotkeyLength = max(renderHotkeyLength, lipgloss.Width(styled))
 	}
 	return renderHotkeyLength
 }
🧹 Nitpick comments (3)
src/internal/model_render.go (3)

343-352: Use display width (ansi-aware) instead of len() to size hotkey columns

len() counts bytes, not on-screen cells, and ignores ANSI. This mis-sizes columns when keys include wide glyphs or styling, leading to ragged alignment.

Apply:

-	for _, data := range m.helpMenu.filteredData {
-		totalKeyLen := 0
-		for _, key := range data.hotkey {
-			totalKeyLen += len(key)
-		}
-
-		separatorLen := max(0, (len(data.hotkey)-1)) * 3
-		if data.subTitle == "" && totalKeyLen+separatorLen > maxKeyLength {
-			maxKeyLength = totalKeyLen + separatorLen
-		}
-	}
+	for _, data := range m.helpMenu.filteredData {
+		if data.subTitle != "" {
+			continue
+		}
+		totalKeyWidth := 0
+		for _, key := range data.hotkey {
+			totalKeyWidth += lipgloss.Width(key)
+		}
+		separatorWidth := max(0, len(data.hotkey)-1) * lipgloss.Width(" | ")
+		if w := totalKeyWidth + separatorWidth; w > maxKeyLength {
+			maxKeyLength = w
+		}
+	}

363-370: Fix “current/total” to exclude titles and handle cursor-on-title/empty states

When the cursor lands on a title, the numerator can be off (e.g., “1/0”). Compute counts over non-title items only and clamp the current index.

-	totalTitleCount := 0
-	cursorBeenTitleCount := 0
-
-	for i, data := range m.helpMenu.filteredData {
-		if data.subTitle != "" {
-			if i < m.helpMenu.cursor {
-				cursorBeenTitleCount++
-			}
-			totalTitleCount++
-		}
-	}
+	// Count only actionable (non-title) items, and compute the 1-based index of the
+	// last actionable item at or before the cursor. If the cursor is on a title, the
+	// index reflects the previous actionable item (or 0 if none).
+	totalItems := 0
+	currentItemIndex := 0
+	for i, data := range m.helpMenu.filteredData {
+		if data.subTitle != "" {
+			continue
+		}
+		totalItems++
+		if i <= m.helpMenu.cursor {
+			currentItemIndex = totalItems
+		}
+	}
@@
-	bottomBorder := common.GenerateFooterBorder(fmt.Sprintf("%s/%s",
-		strconv.Itoa(m.helpMenu.cursor+1-cursorBeenTitleCount),
-		strconv.Itoa(len(m.helpMenu.filteredData)-totalTitleCount)), m.helpMenu.width-2)
+	bottomBorder := common.GenerateFooterBorder(
+		fmt.Sprintf("%d/%d", currentItemIndex, totalItems),
+		m.helpMenu.width-2,
+	)

Please verify navigation never focuses titles; if it can, the new logic above ensures sane counts. If you’d like, I can add unit tests for these edge-cases.

Also applies to: 376-379


372-376: Optional: avoid double-render of ModalStyle for description

The current line wraps ModalStyle around both the formatted string and the description again. After the refactor above, ensure only the description is styled to prevent nested/redundant styling.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 47c960f and 8011ae1.

📒 Files selected for processing (6)
  • src/internal/default_config.go (2 hunks)
  • src/internal/handle_modal.go (2 hunks)
  • src/internal/key_function.go (1 hunks)
  • src/internal/model.go (3 hunks)
  • src/internal/model_render.go (4 hunks)
  • src/internal/type.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/internal/default_config.go
  • src/internal/model.go
  • src/internal/type.go
  • src/internal/key_function.go
  • src/internal/handle_modal.go
🧰 Additional context used
🧬 Code graph analysis (1)
src/internal/model_render.go (3)
src/internal/common/style_function.go (2)
  • GenerateFooterBorder (336-343)
  • HelpMenuModalBorderStyle (159-171)
src/internal/common/style.go (1)
  • HelpMenuTitleStyle (57-57)
src/internal/common/string_function.go (1)
  • TruncateText (19-26)
🔇 Additional comments (1)
src/internal/model_render.go (1)

406-406: Sanity-check renderIndex after filtering to avoid empty viewports

If filtering shrinks filteredData, renderIndex could point past the end, resulting in an empty page despite there being items above. Clamp renderIndex and cursor in the filtering/update path.

I can generate a focused script to scan the update logic for clamping after filter changes if needed.

@lazysegtree lazysegtree changed the base branch from main to develop August 27, 2025 13:53
codescene-delta-analysis[bot]

This comment was marked as outdated.

@lazysegtree
Copy link
Collaborator

@iZarrios There is a problem with the rendering .

image

We will have to fix it before merging it.

@lazysegtree
Copy link
Collaborator

I am gonna fix unit tests later, we will merge this without that.

@iZarrios
Copy link
Contributor Author

iZarrios commented Aug 27, 2025

@lazysegtree Oh on Alacritty there isn't a rendering problem but using another terminal emulator I can reproduce it too.

Will try and fix it.

@lazysegtree
Copy link
Collaborator

lazysegtree commented Aug 27, 2025

Also @iZarrios lets add one line of vertical space between search bar and the data.

And also 1-2 space character (horizontal space) between search bar and border veritcal borders

codescene-delta-analysis[bot]

This comment was marked as outdated.

@lazysegtree
Copy link
Collaborator

lazysegtree commented Aug 28, 2025

Now the content is getting cropped on lower dimensions.

image

@lazysegtree
Copy link
Collaborator

Uploading image.png…

Larger dimensions works fine.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@lazysegtree lazysegtree force-pushed the feat/add-help-menu-search branch from 552ca18 to db898d0 Compare August 28, 2025 05:09
codescene-delta-analysis[bot]

This comment was marked as outdated.

@lazysegtree
Copy link
Collaborator

@CodeRabbit summary

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 28, 2025

✅ Actions performed

Summary regeneration triggered.

Foreground(ModalFGColor)
}

// Generate help menu modal border style
Copy link
Collaborator

Choose a reason for hiding this comment

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

not used anymore

@lazysegtree
Copy link
Collaborator

image

Now wrapping works correctly

@lazysegtree
Copy link
Collaborator

image

When there is nothing we see 2/0 , which is wrong.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@lazysegtree
Copy link
Collaborator

@iZarrios #1011 (comment) and #1011 (comment) needs to be fixed. And please also do the manual tests that rabbit mentioned.

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Code Health Improved (2 files improve in Code Health)

Gates Failed
Prevent hotspot decline (2 hotspots with String Heavy Function Arguments, Complex Method)
Enforce critical code health rules (1 file with Bumpy Road Ahead)

Gates Passed
1 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Prevent hotspot decline Violations Code Health Impact
key_function.go 1 rule in this hotspot 8.33 → 8.07 Suppress
model_render.go 1 rule in this hotspot 8.68 → 8.66 Suppress
Enforce critical code health rules Violations Code Health Impact
handle_modal.go 1 critical rule 9.31 → 9.54 Suppress
View Improvements
File Code Health Impact Categories Improved
handle_modal.go 9.31 → 9.54 Code Duplication
key_function.go 8.33 → 8.07 Overall Code Complexity
style_function.go 8.55 → 8.82 Code Duplication, Primitive Obsession

Quality Gate Profile: The Bare Minimum
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

Comment on lines +325 to +327
r := ui.HelpMenuRenderer(m.helpMenu.height, m.helpMenu.width)
r.AddLines(" " + m.helpMenu.searchBar.View())
r.AddLines("") // one-line separation between searchbar and content

Choose a reason for hiding this comment

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

❌ Getting worse: Complex Method
helpMenuRender increases in cyclomatic complexity from 9 to 10, threshold = 9

Suppress

@iZarrios
Copy link
Contributor Author

@lazysegtree Pushed fixes for both comments, I also did the manual testing that rabbit mentioned.

@lazysegtree
Copy link
Collaborator

Looks good now.

Comment on lines 145 to 203
if m.helpMenu.cursor > 1 {
m.helpMenu.cursor--
if m.helpMenu.cursor < m.helpMenu.renderIndex {
m.helpMenu.renderIndex--
if m.helpMenu.data[m.helpMenu.cursor].subTitle != "" {
m.helpMenu.renderIndex--
}
m.helpMenu.renderIndex = m.helpMenu.cursor
}
if m.helpMenu.data[m.helpMenu.cursor].subTitle != "" {
if m.helpMenu.filteredData[m.helpMenu.cursor].subTitle != "" {
m.helpMenu.cursor--
}
} else {
m.helpMenu.cursor = len(m.helpMenu.data) - 1
m.helpMenu.renderIndex = len(m.helpMenu.data) - m.helpMenu.height
// Set the cursor to the last item in the list.
// We use max(..., 0) as a safeguard to prevent a negative cursor index
// in case the filtered list is empty.
m.helpMenu.cursor = max(len(m.helpMenu.filteredData)-1, 0)

// Adjust the render index to show the bottom of the list.
// Similarly, we use max(..., 0) to ensure the renderIndex doesn't become negative,
// which can happen if the number of items is less than the view height.
// This prevents a potential out-of-bounds panic during rendering.
m.helpMenu.renderIndex = max(len(m.helpMenu.filteredData)-(m.helpMenu.height-4), 0)
}
}

// Help menu panel list down
func (m *model) helpMenuListDown() {
if len(m.helpMenu.data) == 0 {
if len(m.helpMenu.filteredData) == 0 {
return
}

if m.helpMenu.cursor < len(m.helpMenu.data)-1 {
m.helpMenu.cursor++
if m.helpMenu.cursor > m.helpMenu.renderIndex+m.helpMenu.height-1 {
if m.helpMenu.cursor < len(m.helpMenu.filteredData)-1 {
// Compute the next selectable row (skip subtitles).
next := m.helpMenu.cursor + 1
for next < len(m.helpMenu.filteredData) && m.helpMenu.filteredData[next].subTitle != "" {
next++
}
if next >= len(m.helpMenu.filteredData) {
// Wrap if no more selectable rows.
m.helpMenu.cursor = 1
m.helpMenu.renderIndex = 0
return
}
m.helpMenu.cursor = next

// Scroll down if cursor moved past the viewport.
if m.helpMenu.cursor > m.helpMenu.renderIndex+m.helpMenu.height-5 {
m.helpMenu.renderIndex++
if m.helpMenu.data[m.helpMenu.cursor].subTitle != "" {
m.helpMenu.renderIndex++
}
}
if m.helpMenu.data[m.helpMenu.cursor].subTitle != "" {
m.helpMenu.cursor++
// Clamp renderIndex to bottom.
bottom := len(m.helpMenu.filteredData) - (m.helpMenu.height - 4)
if bottom < 0 {
bottom = 0
}
if m.helpMenu.renderIndex > bottom {
m.helpMenu.renderIndex = bottom
}
} else {
m.helpMenu.cursor = 1
m.helpMenu.renderIndex = 0
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai

These up down fucntions are
(1) Too complicated
(2) Can be simplified.
(3) Similar to sidebar up down
(4) Not unit tested

Create an issue to fix all this mess.

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

@lazysegtree I'll create a GitHub issue to track the help menu navigation improvements you've identified.


🏁 Script executed:

#!/bin/bash
# Create GitHub issue for help menu navigation complexity
gh issue create \
  --title "Refactor help menu navigation functions for simplicity and consistency" \
  --body "## Problem

The help menu navigation functions (\`helpMenuListUp\` and \`helpMenuListDown\`) in \`src/internal/handle_modal.go\` have several issues that need to be addressed:

1. **Too complicated** - The navigation logic is overly complex and hard to follow
2. **Can be simplified** - There's significant room for refactoring to make the code more maintainable  
3. **Similar to sidebar up down** - The functions appear to duplicate patterns from sidebar navigation, suggesting inconsistency in approach
4. **Not unit tested** - The navigation functions lack proper test coverage

## Proposed Solution

- Simplify the navigation logic by following patterns established in sidebar navigation
- Reduce complexity and improve readability
- Add comprehensive unit tests for the navigation functions
- Ensure consistency with other navigation implementations in the codebase

## Context

This issue was identified during review of PR #1011 which adds search functionality to the help menu. The navigation functions were modified as part of that PR but the underlying complexity issues should be addressed separately.

**Reference:**
- PR: https://github.com/yorukot/superfile/pull/1011
- Comment: https://github.com/yorukot/superfile/pull/1011#discussion_r2310233048" \
  --assignee lazysegtree

Length of output: 1440


Perfect! I've created issue #1028 to track the help menu navigation improvements you identified. The issue documents all four problems you mentioned:

  1. Overly complicated logic
  2. Potential for simplification
  3. Inconsistency with sidebar navigation patterns
  4. Lack of unit test coverage

This allows the current PR to focus on the search functionality while tracking the navigation refactoring work separately for future improvement.


✏️ Learnings added
Learnt from: lazysegtree
PR: yorukot/superfile#1011
File: src/internal/handle_modal.go:145-203
Timestamp: 2025-08-29T13:56:33.930Z
Learning: lazysegtree identified that help menu navigation functions (helpMenuListUp and helpMenuListDown) in src/internal/handle_modal.go are too complicated, can be simplified, are similar to sidebar navigation but inconsistent, and lack unit tests. He prefers to track such technical debt in separate GitHub issues rather than addressing it in the current PR scope.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: lazysegtree
PR: yorukot/superfile#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
PR: yorukot/superfile#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
PR: yorukot/superfile#969
File: src/internal/key_function.go:40-40
Timestamp: 2025-08-03T09:34:55.721Z
Learning: lazysegtree emphasizes proper dependency direction in software architecture, preferring that low-level components (like modal handlers) should not depend on high-level components (like the main model object). He also prioritizes performance considerations, noting that creating objects on every keypress in hot code paths like key handling is inefficient and should be avoided.

Learnt from: iZarrios
PR: yorukot/superfile#1011
File: src/internal/handle_modal.go:147-155
Timestamp: 2025-08-23T09:21:02.742Z
Learning: In the superfile help menu filtering system, the `removeOrphanSections` function in `src/internal/handle_modal.go` removes subtitle entries that don't have any following hotkey entries after filtering, ensuring no orphan sections exist in the filtered data.

Learnt from: lazysegtree
PR: yorukot/superfile#963
File: src/internal/default_config.go:16-16
Timestamp: 2025-07-27T07:40:51.938Z
Learning: lazysegtree prefers simpler implementation approaches when the alternatives are significantly more complex, even if the alternatives might be architecturally cleaner, prioritizing maintainability and avoiding over-engineering.

Learnt from: lazysegtree
PR: yorukot/superfile#963
File: src/internal/default_config.go:16-16
Timestamp: 2025-07-27T07:40:51.938Z
Learning: lazysegtree prefers simpler implementation approaches when the alternatives are significantly more complex, even if the alternatives might be architecturally cleaner, prioritizing maintainability and avoiding over-engineering.

Learnt from: lazysegtree
PR: yorukot/superfile#985
File: src/internal/model.go:0-0
Timestamp: 2025-08-11T01:49:30.040Z
Learning: lazysegtree prefers maintaining code correctness through proper design and invariants rather than adding defensive bounds checks at every slice access point, viewing such defensive programming as "duct taping" that can mask actual bugs instead of fixing them at their source.

Learnt from: lazysegtree
PR: yorukot/superfile#973
File: src/internal/ui/processbar/model_update.go:7-27
Timestamp: 2025-08-03T14:49:31.221Z
Learning: lazysegtree prefers to keep test-only code simple without adding production-level concerns like goroutine synchronization, cancellation contexts, or complex lifecycle management, even when such patterns might prevent potential issues, since the complexity isn't justified for test utilities.

Learnt from: lazysegtree
PR: yorukot/superfile#950
File: src/internal/ui/metadata/README.md:7-7
Timestamp: 2025-07-24T03:46:29.516Z
Learning: lazysegtree prefers to defer comprehensive unit testing to separate PRs when the current PR has grown too large, maintaining focus on the primary refactoring objectives while tracking testing requirements in dedicated GitHub issues.

Learnt from: lazysegtree
PR: yorukot/superfile#967
File: src/internal/key_function.go:45-47
Timestamp: 2025-08-02T11:47:07.713Z
Learning: lazysegtree prefers to track technical debt and architectural improvements in dedicated GitHub issues when they are identified during PR reviews but are beyond the scope of the current PR, particularly for complex refactoring needs like input handling architecture that would require significant changes.

Learnt from: lazysegtree
PR: yorukot/superfile#963
File: src/internal/handle_file_operations.go:567-570
Timestamp: 2025-07-27T08:49:09.687Z
Learning: lazysegtree prefers to defer technical debt issues like model mutation concerns to later PRs when the current PR has already grown too large, maintaining focus on the primary objectives while acknowledging the need to track such issues for future work.

Learnt from: lazysegtree
PR: yorukot/superfile#924
File: src/internal/model_test.go:147-168
Timestamp: 2025-08-09T02:52:22.516Z
Learning: In superfile tests, lazysegtree prefers tests that accurately mimic the actual production behavior, including file operations like creating and deleting the LastDir file, rather than isolating such operations to temporary locations when the real behavior involves modifying/deleting those specific files.

Learnt from: lazysegtree
PR: yorukot/superfile#970
File: src/internal/ui/processbar/model.go:11-26
Timestamp: 2025-08-02T17:00:37.262Z
Learning: lazysegtree prefers to track memory leak issues and feature enhancements via dedicated GitHub issues when identified during PR reviews but are beyond the current PR scope, particularly for processbar-related improvements like cleanup mechanisms and keyboard shortcuts.

@lazysegtree lazysegtree merged commit 0c30694 into yorukot:develop Aug 29, 2025
6 of 7 checks passed
@lazysegtree
Copy link
Collaborator

@iZarrios
Copy link
Contributor Author

Alright, thank you

yorukot added a commit that referenced this pull request Aug 30, 2025
…p menu (#1013)

PRs
- #1012
- #1021
- #1011

Issues 
- #1009
- #1007

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- New Features
- Redesigned file preview with improved image, directory and code/text
rendering, smarter sizing, and on-demand refresh; help menu now supports
searchable, filterable navigation.

- Bug Fixes
- Fixed UTF-8 BOM handling so previews no longer show stray characters.

- Refactor
- Centralized file-reading and preview behavior for consistent
truncation/preview length; unified test setup across suites.

- Tests
  - Added unit tests covering file reading and BOM handling.

- Chores
  - .gitignore updated to exclude CLAUDE.md.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Foreground(ModalFGColor)
}

// Generate help menu modal border style
Copy link
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai
Please generate a task descriptions for this PR. I will give someone this task. He doesn't know anything about this product. Context - we have superfile , it has help meny, doesn't have search bar.

Add search bar. Should hotkeys only filtered via searchbar value. Use fzf. Be consistent with design like sidebar search and filepanle search.
make sure to adjust width and height based on new searchbar
handle search hotkey
Migrate helpmenu rendering to Render in src/internal/ui/rendering like other panels
Add Anything else that I missed, which is done in this PR, and should be in task description.
make sure blinking works by sending the tea.msg to searchbar and returning its tea.cmd like searchbar in sidebar.

Keep it in 6-7 points or less. Format should be markdow. Make sure to highlight what to be done, not how or where.

Give basic context about superfile and what is the requirement. problem description should be stated such that a reasonably competent software engineer could write the correct PR for it without too much familiarity on the repo

Also generate a TL;DR

Copy link
Contributor

@coderabbitai coderabbitai bot Sep 2, 2025

Choose a reason for hiding this comment

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

@lazysegtree Here's a task description for implementing search functionality in the help menu:

Task: Add Search Functionality to Help Menu

Context

Superfile is a terminal-based file manager with various UI components including a help menu that displays available hotkeys and their descriptions. Currently, users must manually scroll through the help menu to find specific commands, which is inefficient for discovering functionality.

Problem Statement

The help menu lacks search capability, making it difficult for users to quickly find specific hotkeys or commands. The menu should support fuzzy search filtering to improve discoverability and user experience, similar to existing search functionality in other components like the sidebar and file panels.

Requirements

  1. Add Search Bar Component: Integrate a search input field at the top of the help menu modal that accepts user input for filtering content.

  2. Implement Fuzzy Search Filtering: Filter help menu items based on search input using fuzzy matching against hotkey descriptions, showing only relevant results while preserving section organization.

  3. Handle Search Hotkey: Add keyboard shortcut support to focus the search bar when the help menu is open, allowing users to quickly start searching without mouse interaction.

  4. Ensure Design Consistency: Match the visual design and behavior of existing search components (sidebar search, file panel search) including styling, placeholder text, and interaction patterns.

  5. Adjust Layout Dynamically: Automatically resize the help menu dimensions to accommodate the new search bar while maintaining proper spacing and border rendering.

  6. Implement Proper Message Handling: Ensure the search bar receives Tea.js messages for cursor blinking and returns appropriate commands, following the same pattern used in other search components.

  7. Migrate to Modern Rendering System: Update help menu rendering to use the centralized renderer system (src/internal/ui/rendering) instead of manual string building, consistent with other UI panels.

TL;DR

Add a fuzzy search bar to the help menu that filters hotkeys in real-time, with proper keyboard navigation, consistent UI design, and modern rendering architecture.


🧠 Learnings used
Learnt from: lazysegtree
PR: yorukot/superfile#1013
File: src/internal/model_render.go:329-341
Timestamp: 2025-08-29T14:11:21.333Z
Learning: lazysegtree prefers to defer help menu rendering optimizations and other technical debt improvements to separate GitHub issues when the current PR scope has grown too large, maintaining focus on the primary refactoring objectives while tracking performance improvements for future work.

Learnt from: lazysegtree
PR: yorukot/superfile#1011
File: src/internal/handle_modal.go:145-203
Timestamp: 2025-08-29T13:56:33.930Z
Learning: lazysegtree identified that help menu navigation functions (helpMenuListUp and helpMenuListDown) in src/internal/handle_modal.go are too complicated, can be simplified, are similar to sidebar navigation but inconsistent, and lack unit tests. He prefers to track such technical debt in separate GitHub issues rather than addressing it in the current PR scope.

Learnt from: lazysegtree
PR: yorukot/superfile#963
File: src/internal/default_config.go:16-16
Timestamp: 2025-07-27T07:40:51.938Z
Learning: lazysegtree prefers simpler implementation approaches when the alternatives are significantly more complex, even if the alternatives might be architecturally cleaner, prioritizing maintainability and avoiding over-engineering.

Learnt from: lazysegtree
PR: yorukot/superfile#963
File: src/internal/default_config.go:16-16
Timestamp: 2025-07-27T07:40:51.938Z
Learning: lazysegtree prefers simpler implementation approaches when the alternatives are significantly more complex, even if the alternatives might be architecturally cleaner, prioritizing maintainability and avoiding over-engineering.

Learnt from: lazysegtree
PR: yorukot/superfile#969
File: src/internal/key_function.go:40-40
Timestamp: 2025-08-03T09:34:55.721Z
Learning: lazysegtree emphasizes proper dependency direction in software architecture, preferring that low-level components (like modal handlers) should not depend on high-level components (like the main model object). He also prioritizes performance considerations, noting that creating objects on every keypress in hot code paths like key handling is inefficient and should be avoided.

Learnt from: lazysegtree
PR: yorukot/superfile#985
File: src/internal/model.go:0-0
Timestamp: 2025-08-11T01:49:30.040Z
Learning: lazysegtree prefers maintaining code correctness through proper design and invariants rather than adding defensive bounds checks at every slice access point, viewing such defensive programming as "duct taping" that can mask actual bugs instead of fixing them at their source.

Learnt from: lazysegtree
PR: yorukot/superfile#973
File: src/internal/ui/processbar/model_update.go:7-27
Timestamp: 2025-08-03T14:49:31.221Z
Learning: lazysegtree prefers to keep test-only code simple without adding production-level concerns like goroutine synchronization, cancellation contexts, or complex lifecycle management, even when such patterns might prevent potential issues, since the complexity isn't justified for test utilities.

Learnt from: lazysegtree
PR: yorukot/superfile#950
File: src/internal/ui/metadata/README.md:7-7
Timestamp: 2025-07-24T03:46:29.516Z
Learning: lazysegtree prefers to defer comprehensive unit testing to separate PRs when the current PR has grown too large, maintaining focus on the primary refactoring objectives while tracking testing requirements in dedicated GitHub issues.

Learnt from: lazysegtree
PR: yorukot/superfile#967
File: src/internal/key_function.go:45-47
Timestamp: 2025-08-02T11:47:07.713Z
Learning: lazysegtree prefers to track technical debt and architectural improvements in dedicated GitHub issues when they are identified during PR reviews but are beyond the scope of the current PR, particularly for complex refactoring needs like input handling architecture that would require significant changes.

Learnt from: lazysegtree
PR: yorukot/superfile#963
File: src/internal/handle_file_operations.go:567-570
Timestamp: 2025-07-27T08:49:09.687Z
Learning: lazysegtree prefers to defer technical debt issues like model mutation concerns to later PRs when the current PR has already grown too large, maintaining focus on the primary objectives while acknowledging the need to track such issues for future work.

Learnt from: lazysegtree
PR: yorukot/superfile#924
File: src/internal/model_test.go:147-168
Timestamp: 2025-08-09T02:52:22.516Z
Learning: In superfile tests, lazysegtree prefers tests that accurately mimic the actual production behavior, including file operations like creating and deleting the LastDir file, rather than isolating such operations to temporary locations when the real behavior involves modifying/deleting those specific files.

tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Oct 15, 2025
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [yorukot/superfile](https://github.com/yorukot/superfile) | minor | `v1.3.3` -> `v1.4.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>yorukot/superfile (yorukot/superfile)</summary>

### [`v1.4.0`](https://github.com/yorukot/superfile/releases/tag/v1.4.0)

[Compare Source](yorukot/superfile@v1.3.3...v1.4.0)

Hey folks. Releasing v1.4.0 with many new features, improvements, and bug fixes.

We have an async file preview now, a zoxide panel, and various new features improving UX.

#### Install:

[**Click me to know how to install**](https://github.com/yorukot/superfile?tab=readme-ov-file#installation)

#### Highlights

- We have the Zoxide Panel now. Ensure zoxide is installed on your system, set `zoxide_support` to `true` in the config, and press `z` to use zoxide. <img width="645" height="295" alt="Image" src="https://github.com/user-attachments/assets/238f6549-5318-49d1-a3a0-14cf8a686955" />

- File previewing is now async, meaning reduced lag while scrolling through images, or on slow systems.

- Many bug fixes. See 'Detailed Change Summary'

##### Internal Updates

- Most file operations are now truly async with the usage of the recommended `tea.Cmd` pattern.
- Enabled many new linters to improve code quality.
- Moved golangci-lint to v2. Now developers don't need to keep the old v1 in their systems.
- Refactored file preview in its own package for better maintainability and readability.
- Fixed flaky unit tests.

#### Detailed Change Summary

<details><summary>Details</summary>
<p>

##### Update
- feat: File operation via tea cmd [#&#8203;963](yorukot/superfile#963) by @&#8203;lazysegtree
- feat: processbar improvements, package separation, better channel management [#&#8203;973](yorukot/superfile#973) by @&#8203;lazysegtree
- feat: enable lll and recvcheck linter, fix tests, more refactors [#&#8203;977](yorukot/superfile#977) by @&#8203;lazysegtree
- feat: Remove channel for notification models [#&#8203;979](yorukot/superfile#979) by @&#8203;lazysegtree
- feat: enable cyclop, funlen, gocognit, gocyclo linters, and refactor large functions [#&#8203;984](yorukot/superfile#984) by @&#8203;lazysegtree
- feat: Add a new hotkey to handle cd-on-quit whenever needed [#&#8203;924](yorukot/superfile#924) by @&#8203;ahmed-habbachi
- feat: added option to permanently delete files [#&#8203;987](yorukot/superfile#987) by @&#8203;hupender
- feat: Preview panel separation [#&#8203;1021](yorukot/superfile#1021) by @&#8203;lazysegtree
- feat: Add search functionality to help menu [#&#8203;1011](yorukot/superfile#1011) by @&#8203;iZarrios
- feat: Use zoxide lib [#&#8203;1036](yorukot/superfile#1036) by @&#8203;lazysegtree
- feat: Add zoxide directory tracking on navigation [#&#8203;1041](yorukot/superfile#1041) by @&#8203;lazysegtree
- feat: Zoxide integration [#&#8203;1039](yorukot/superfile#1039) by @&#8203;lazysegtree
- feat: Select mode with better feedback [#&#8203;1074](yorukot/superfile#1074) by @&#8203;lazysegtree
- feat: owner/group in the metadata [#&#8203;1093](yorukot/superfile#1093) by @&#8203;xelavopelk
- feat: Async zoxide [#&#8203;1104](yorukot/superfile#1104) by @&#8203;lazysegtree

##### Bug Fix
- fix: sorting in searchbar [#&#8203;985](yorukot/superfile#985) by @&#8203;hupender
- fix: Async rendering, Include clipboard check in paste items, and update linter configs [#&#8203;997](yorukot/superfile#997) by @&#8203;lazysegtree
- fix: Move utility functions to utils package [#&#8203;1012](yorukot/superfile#1012) by @&#8203;lazysegtree
- fix: Refactoring and separation of preview panel and searchbar in help menu [#&#8203;1013](yorukot/superfile#1013) by @&#8203;lazysegtree
- fix(filePanel): allow focusType to be set correctly [#&#8203;1033](yorukot/superfile#1033) by @&#8203;faisal-990
- fix(ci): Update gomod2nix.toml, allow pre release in version output, release 1.4.0-rc1, bug fixes, and improvements [#&#8203;1054](yorukot/superfile#1054) by @&#8203;lazysegtree
- fix(nix): resolve build failures in the nix flake [#&#8203;1068](yorukot/superfile#1068) by @&#8203;Frost-Phoenix
- fix: Retry the file deletion to prevent flakies (#&#8203;938) [#&#8203;1076](yorukot/superfile#1076) by @&#8203;lazysegtree
- fix(issue-1066): Fixed issue where enter was not searchable [#&#8203;1078](yorukot/superfile#1078) by @&#8203;Simpaqt
- fix(#&#8203;1073): Tech debt fix [#&#8203;1077](yorukot/superfile#1077) by @&#8203;Simpaqt
- fix: fix deleted directory not able to remove from pins (#&#8203;1067) [#&#8203;1081](yorukot/superfile#1081) by @&#8203;yorukot
- fix: fix child process spawning attached [#&#8203;1084](yorukot/superfile#1084) by @&#8203;guemidiborhane
- fix: always clear images when showing a FullScreenStyle [#&#8203;1094](yorukot/superfile#1094) by @&#8203;snikoletopoulos
- fix: Allow j and k keys in zoxide [#&#8203;1102](yorukot/superfile#1102) by @&#8203;lazysegtree
- fix: Zoxide improvements and 1.4.0-rc2 [#&#8203;1105](yorukot/superfile#1105) by @&#8203;lazysegtree
- fix: rename cursor beginning on wrong character because of multiple dots in name (#&#8203;813) [#&#8203;1112](yorukot/superfile#1112) by @&#8203;SyedAsadK
- fix: check and fix file panel scroll position on height changes [#&#8203;1095](yorukot/superfile#1095) by @&#8203;snikoletopoulos

##### Optimization
- perf(website): optimize font loading and asset organization [#&#8203;1089](yorukot/superfile#1089) by @&#8203;yorukot

##### Documentation
- docs: fix incorrect zoxide plugin config name [#&#8203;1049](yorukot/superfile#1049) by @&#8203;shree-xvi
- docs(hotkeys): Fix typo in vimHotkeys.toml comments [#&#8203;1080](yorukot/superfile#1080) by @&#8203;wleoncio
- docs: add section for core maintainers in README.md [#&#8203;1088](yorukot/superfile#1088) by @&#8203;yorukot
- chore: add winget install instruction to readme and website [#&#8203;943](yorukot/superfile#943) by @&#8203;claykom

##### Dependencies
- chore(deps): update dependency go to v1.25.0, golangci-lint to v2, golangci-lint actions to v8 [#&#8203;750](yorukot/superfile#750) by @&#8203;renovate[bot]
- chore(deps): update amannn/action-semantic-pull-request action to v6 [#&#8203;1006](yorukot/superfile#1006) by @&#8203;renovate[bot]
- chore(deps): update actions/first-interaction action to v3 [#&#8203;1005](yorukot/superfile#1005) by @&#8203;renovate[bot]
- chore(deps): update actions/checkout action to v5 [#&#8203;1004](yorukot/superfile#1004) by @&#8203;renovate[bot]
- chore(deps): bump astro from 5.10.1 to 5.12.8 [#&#8203;982](yorukot/superfile#982) by @&#8203;dependabot[bot]
- fix(deps): update module golang.org/x/mod to v0.27.0 [#&#8203;989](yorukot/superfile#989) by @&#8203;renovate[bot]
- fix(deps): update dependency @&#8203;expressive-code/plugin-collapsible-sections to v0.41.3 [#&#8203;990](yorukot/superfile#990) by @&#8203;renovate[bot]
- fix(deps): update dependency sharp to v0.34.3 [#&#8203;992](yorukot/superfile#992) by @&#8203;renovate[bot]
- fix(deps): update dependency @&#8203;expressive-code/plugin-line-numbers to v0.41.3 [#&#8203;991](yorukot/superfile#991) by @&#8203;renovate[bot]
- chore(deps): update dependency go to v1.25.0 [#&#8203;994](yorukot/superfile#994) by @&#8203;renovate[bot]
- fix(deps): update astro monorepo [#&#8203;995](yorukot/superfile#995) by @&#8203;renovate[bot]
- fix(deps): update dependency @&#8203;astrojs/starlight to ^0.35.0 [#&#8203;1000](yorukot/superfile#1000) by @&#8203;renovate[bot]
- fix(deps): update module github.com/urfave/cli/v3 to v3.4.1 [#&#8203;1001](yorukot/superfile#1001) by @&#8203;renovate[bot]
- fix(deps): update module golang.org/x/text to v0.28.0 [#&#8203;1003](yorukot/superfile#1003) by @&#8203;renovate[bot]

##### Misc
- chore: migrate from superfile.netlify.app to superfile.dev [#&#8203;1087](yorukot/superfile#1087) by @&#8203;yorukot
- refactor(filepanel): replace filePanelFocusType with isFocused boolean [#&#8203;1040](yorukot/superfile#1040) by @&#8203;faisal-990
- refactor(ansi): Migrate from github.com/charmbracelet/x/exp/term/ansi to github.com/charmbracelet/x/ansi [#&#8203;1044](yorukot/superfile#1044) by @&#8203;faisal-990
- refactor: common operation on pinned directory file using PinnedManager [#&#8203;1085](yorukot/superfile#1085) by @&#8203;Manaswa-S
- test: unit tests for pinned manager [#&#8203;1090](yorukot/superfile#1090) by @&#8203;Manaswa-S

</p>
</details> 

#### New Contributors
* @&#8203;hupender made their first contribution in yorukot/superfile#985
* @&#8203;ahmed-habbachi made their first contribution in yorukot/superfile#924
* @&#8203;iZarrios made their first contribution in yorukot/superfile#1011
* @&#8203;faisal-990 made their first contribution in yorukot/superfile#1033
* @&#8203;shree-xvi made their first contribution in yorukot/superfile#1049
* @&#8203;Simpaqt made their first contribution in yorukot/superfile#1078
* @&#8203;wleoncio made their first contribution in yorukot/superfile#1080
* @&#8203;guemidiborhane made their first contribution in yorukot/superfile#1084
* @&#8203;Manaswa-S made their first contribution in yorukot/superfile#1085
* @&#8203;xelavopelk made their first contribution in yorukot/superfile#1093
* @&#8203;snikoletopoulos made their first contribution in yorukot/superfile#1094
* @&#8203;SyedAsadK made their first contribution in yorukot/superfile#1112

**Full Changelog**: <yorukot/superfile@v1.3.3...v1.4.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNDYuMCIsInVwZGF0ZWRJblZlciI6IjQxLjE0Ni4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiXX0=-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add search functionality in help menu

2 participants