commit 5840ee4c5f0f9f206cd72b5e535832c630919f5f from: Brett Fisher date: Thu Jul 9 06:48:15 2026 UTC aestel/menu.lua: Refactor module for readability. - Update heading comment for consistency across project modules - Remove redundant narrative comments. - Apply consistent spacing and remove unnecessary blank lines. in functions. commit - 96122ce9a1fce816da654d60218cce92efc00123 commit + 5840ee4c5f0f9f206cd72b5e535832c630919f5f blob - ab41080edff33d734e5cc7b84b712ff5515c5da8 blob + 93a0d15e976cc8f5d930bd06d5dc8d9620666a9f --- aestel/menu.lua +++ aestel/menu.lua @@ -1,5 +1,5 @@ --- aestel/ui.lua --- Generic key-driven menu rendering and navigation +-- aestel/menu.lua +-- A module for rendering and navigating menus local M = {} @@ -7,70 +7,62 @@ local colors = require("aestel.colors") local display = require("aestel.display") local input = require("aestel.input") --- Utility to read full key (handles arrow escape sequences) local function read_key_full() local key = input.read_key() if key == "\27" then -- ESC prefix for arrows local second = input.read_key() if second == "[" then local third = input.read_key() - if third == "A" then return "up" -- Arrow up - elseif third == "B" then return "down" -- Arrow down - elseif third == "C" then return "right" -- NEW: Arrow right (enter/down dir) - elseif third == "D" then return "left" -- NEW: Arrow left (up dir) + if third == "A" then return "up" + elseif third == "B" then return "down" + elseif third == "C" then return "right" + elseif third == "D" then return "left" end end end - return key -- Single char (j, k, q, Enter, etc.) + return key end -- Display a key-navigated menu and return the selected index --- (1-based, 0 for "up", or nil (quit)) function M.menu(title, options, start_row, header_lines, empty_message, cwd) if not options or #options == 0 then if empty_message then start_row = start_row or 1 - -- Note: cwd is ignored for empty directories, empty_message replaces it local terminal_height = input.get_terminal_height() input.enable_raw() - - -- Write the empty message on the last terminal line local terminal_width = input.get_terminal_width() local padding = string.rep(" ", math.max(0, terminal_width - #empty_message)) io.stderr:write(string.format("\27[%d;1H\27[K", terminal_height)) io.stderr:write(colors.REVERSE .. empty_message .. padding .. colors.RESET) io.stderr:flush() - local key = read_key_full() input.disable_raw() if key == "h" or key == "left" then - return 0 -- Signal "go up" + return 0 end - return nil -- Quit + return nil end return nil end start_row = start_row or 1 - header_lines = header_lines or 0 -- Extra lines above menu (e.g., for path display) + header_lines = header_lines or 0 local selected_index = 1 local scroll_offset = 1 local running = true local result = nil - local terminal_height = input.get_terminal_height() local max_visible_items = terminal_height - start_row + 1 if cwd then - max_visible_items = max_visible_items - 2 -- Reserve one blank line + one status line + max_visible_items = max_visible_items - 2 end input.enable_raw() while running do - -- Clear menu area ONLY from start_row down (preserve headers) io.stderr:write(string.format("\27[%d;1H", start_row)) - io.stderr:write("\27[J") -- Erase down from start_row + io.stderr:write("\27[J") io.stderr:flush() -- Calculate visible range (ensure selected is always visible) @@ -89,20 +81,17 @@ function M.menu(title, options, start_row, header_line local option = options[i] local is_selected = (i == selected_index) local display_name = option.name or tostring(option) - - local prefix = (is_selected and "\27[1m > " or " ") -- Bold > for selected - + -- Display position + local prefix = (is_selected and "\27[1m > " or " ") -- Determine color by type local option_type = (type(option) == "table") and option.type or nil local color = display.color_for_type(option_type) - -- Position and write to specific row local row = start_row + visible_count - io.stderr:write(string.format("\27[%d;1H\27[K", row)) -- Position and clear line + io.stderr:write(string.format("\27[%d;1H\27[K", row)) io.stderr:write(prefix .. color .. display_name .. colors.RESET .. "\r\n") visible_count = visible_count + 1 end - if cwd then display.status_bar(cwd, selected_index, #options) end @@ -119,22 +108,23 @@ function M.menu(title, options, start_row, header_line if selected_index < #options then selected_index = selected_index + 1 end - elseif key == "h" or key == "left" then -- CHANGED: Added "left" for up dir - result = 0 -- Signal "go up" + elseif key == "h" or key == "left" then + result = 0 running = false - elseif key == "." then -- NEW: Toggle hidden + elseif key == "." then -- Hidden files toggle result = -1 running = false - elseif key == "l" or key == "right" or key == "\r" or key == "\n" then -- CHANGED: Added "right" for down dir/select - result = selected_index -- Select current (dir or file) + elseif key == "l" or key == "right" or key == "\r" or key == "\n" then + result = selected_index running = false elseif key == "q" then - result = nil -- Quit + result = nil running = false end end input.disable_raw() + return result end