commit e6d2dbf825fd8cc59b2e9b71f66137b744aa1fba from: Brett Fisher date: Sun Jul 5 06:57:46 2026 UTC aestel/display.lua, aestel/input.lua, aestel/menu.lua, bin/launchprogram.lua: Move user interface to stderr. - Route text user interface output (headers, menus, status lines, escapes) through io.stderr:write and io.stderr:flush. - Reserve stdout for the current working directory on exit feature in navigate.browse, browsefiles.lua, setbackground.lua. commit - ef5bb0fc465e5b76c85d63db323e216ed52ea224 commit + e6d2dbf825fd8cc59b2e9b71f66137b744aa1fba blob - 1299751ba0bb185628edcb3c9f6ed5ba77854e49 blob + 8537fb186cea61cb2915a7c5760d3ce78803467c --- aestel/display.lua +++ aestel/display.lua @@ -12,22 +12,22 @@ function M.status_line(message, style) local height = input.get_terminal_height() local width = input.get_terminal_width() local padding = string.rep(" ", math.max(0, width - #message)) - io.write(string.format("\27[%d;1H\27[K", height)) - io.write((style or "") .. message .. padding .. colors.RESET) - io.flush() + io.stderr:write(string.format("\27[%d;1H\27[K", height)) + io.stderr:write((style or "") .. message .. padding .. colors.RESET) + io.stderr:flush() end -- Clear entire screen and position cursor at (1, 1) function M.clear_screen() - io.write("\27[2J\27[1;1H") - io.flush() + io.stderr:write("\27[2J\27[1;1H") + io.stderr:flush() end -- Position cursor at (row, col) and clear from there function M.clear_and_position(row, col) - io.write("\27[2J") - io.write(string.format("\27[%d;%dH", row or 1, col or 1)) - io.flush() + io.stderr:write("\27[2J") + io.stderr:write(string.format("\27[%d;%dH", row or 1, col or 1)) + io.stderr:flush() end local TYPE_COLORS = { @@ -50,16 +50,16 @@ function M.write_header(title, cwd) M.clear_and_position(1, 1) local lines = 0 if title and title ~= "" then - io.write(colors.BOLD .. title .. colors.RESET .. "\n") + io.stderr:write(colors.BOLD .. title .. colors.RESET .. "\n") lines = lines + 1 end - io.write("Current Path: " .. (cwd or "") .. "\n") + io.stderr:write("Current Path: " .. (cwd or "") .. "\n") lines = lines + 1 if title and title ~= "" then - io.write("\n") + io.stderr:write("\n") lines = lines + 1 end - io.flush() + io.stderr:flush() return lines end blob - 1615ad247222d520eef88653ed1ecb2a9bf459e8 blob + 3b65eea30764ceb2574f930dad4a1ca7a8bad37f --- aestel/input.lua +++ aestel/input.lua @@ -8,8 +8,8 @@ local original_stty = nil function M.enable_raw() original_stty = io.popen("stty -g"):read("*a") os.execute("stty raw -echo") - io.write("\027[?25l") -- Hide cursor - io.flush() + io.stderr:write("\027[?25l") -- Hide cursor + io.stderr:flush() end function M.disable_raw() @@ -17,8 +17,8 @@ function M.disable_raw() os.execute("stty " .. original_stty) original_stty = nil end - io.write("\027[?25h") -- Show cursor - io.flush() + io.stderr:write("\027[?25h") -- Show cursor + io.stderr:flush() end function M.read_key() blob - f89d7b550fa44a21b0c5b51b9402826b6ab5a280 blob + 1cf47622f08a3b13024c65583164ce73ff48bf2c --- aestel/menu.lua +++ aestel/menu.lua @@ -36,9 +36,9 @@ function M.menu(title, options, start_row, header_line -- 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.write(string.format("\27[%d;1H\27[K", terminal_height)) - io.write(colors.REVERSE .. empty_message .. padding .. colors.RESET) - io.flush() + 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() @@ -65,9 +65,9 @@ function M.menu(title, options, start_row, header_line while running do -- Clear menu area ONLY from start_row down (preserve headers) - io.write(string.format("\27[%d;1H", start_row)) - io.write("\27[J") -- Erase down from start_row - io.flush() + io.stderr:write(string.format("\27[%d;1H", start_row)) + io.stderr:write("\27[J") -- Erase down from start_row + io.stderr:flush() -- Calculate visible range (ensure selected is always visible) local end_index = math.min(scroll_offset + max_visible_items - 1, #options) @@ -94,8 +94,8 @@ function M.menu(title, options, start_row, header_line -- Position and write to specific row local row = start_row + visible_count - io.write(string.format("\27[%d;1H\27[K", row)) -- Position and clear line - io.write(prefix .. color .. display_name .. colors.RESET .. "\r\n") + io.stderr:write(string.format("\27[%d;1H\27[K", row)) -- Position and clear line + io.stderr:write(prefix .. color .. display_name .. colors.RESET .. "\r\n") visible_count = visible_count + 1 end @@ -103,12 +103,12 @@ function M.menu(title, options, start_row, header_line if end_index < #options then local overflow_row = start_row + visible_count if overflow_row <= terminal_height then - io.write(string.format("\27[%d;1H\27[K", overflow_row)) - io.write("... more items ..." .. "\r\n") + io.stderr:write(string.format("\27[%d;1H\27[K", overflow_row)) + io.stderr:write("... more items ..." .. "\r\n") end end - io.flush() + io.stderr:flush() -- Read and handle input local key = read_key_full() blob - 3bab7435e8d7447387c2b82ce00f0661efb55fe9 blob + b6ab1f9d971e91c779d442b5611dec5c522936c7 --- bin/launchprogram.lua +++ bin/launchprogram.lua @@ -45,8 +45,8 @@ local function launch_program() local items = build_list() display.clear_and_position(1, 1) - io.write(colors.BOLD .. TITLE .. colors.RESET .. "\n\n") - io.flush() + io.stderr:write(colors.BOLD .. TITLE .. colors.RESET .. "\n\n") + io.stderr:flush() local selected_index = menu.menu(nil, items, 3)