commit - 60e1b851d9eaef063c50b64c27f1c1c0ad4efcde
commit + 076f13abe43fdd57cd7d2e8b1920ca6caead8e2b
blob - /dev/null
blob + 3e81c40527435d22cd0680eb42fda45c862eab9b (mode 644)
--- /dev/null
+++ aestel/browse.lua
+-- aestel/browse.lua
+-- A module for browsing directory structure and content.
+
+local M = {}
+
+-- Dependencies
+local lfs = require("lfs")
+local colors = require("aestel.colors")
+local display = require("aestel.display")
+local input = require("aestel.input")
+local menu = require("aestel.menu")
+
+local home_dir = os.getenv("HOME")
+
+local function normalize_path(p)
+ p = p:gsub("//+", "/")
+ if p == "/." or p == "./" then p = "/" end
+ return p:gsub("/+$", "")
+end
+
+local function join_path(base, name)
+ if base == "/" then
+ return normalize_path("/" .. name)
+ end
+ return normalize_path(base .. "/" .. name)
+end
+
+local function change_dir(cur, target, base_dir)
+ local new_path
+ if target == ".." then
+ local cur_no_trail = normalize_path(cur):gsub("/+$", "")
+ if cur_no_trail == "/" then
+ return nil, "Cannot go above root directory (/)"
+ end
+ new_path = cur_no_trail:match("(.+)/[^/]+$") or "/"
+ else
+ new_path = join_path(cur, target)
+ end
+
+ new_path = normalize_path(new_path)
+
+ if base_dir then
+ local normalized_base = normalize_path(base_dir) .. "/"
+ local normalized_new = new_path .. "/"
+ if not normalized_new:find(normalized_base, 1, true) then
+ return nil, "Access denied -- cannot navigate above base directory."
+ end
+ end
+
+ if new_path ~= "/" then
+ local attr = lfs.attributes(new_path)
+ if not attr then
+ return nil, "Path does not exist: " .. (new_path or "unknown")
+ end
+ if attr.mode ~= "directory" then
+ return nil, new_path .. " is not a directory."
+ end
+ end
+ return new_path
+end
+
+local function build_list(path, show_hidden)
+ local dirs, files = {}, {}
+ for item in lfs.dir(path) do
+ if item ~= "." and item ~= ".." then
+ if not show_hidden and item:sub(1,1) == "." then
+ -- skip hidden
+ else
+ local full = join_path(path, item)
+ local attr = lfs.attributes(full)
+ if attr then
+ if attr.mode == "directory" then
+ table.insert(dirs, item)
+ elseif attr.mode == "file" then
+ table.insert(files, item)
+ end
+ end
+ end
+ end
+ end
+ table.sort(dirs)
+ table.sort(files)
+
+ local combined = {}
+ for _, d in ipairs(dirs) do
+ table.insert(combined, {name = d, type = "dir"})
+ end
+ for _, f in ipairs(files) do
+ table.insert(combined, {name = f, type = "file"})
+ end
+ return combined
+end
+
+function M.browse(start_dir, base_dir, title)
+ title = title or ""
+ local cwd = normalize_path(start_dir or lfs.currentdir() or home_dir)
+ local show_hidden = false
+ local header_lines = display.write_header(title)
+
+ local running = true
+ local selected_file = nil
+
+ while running do
+ local items = build_list(cwd, show_hidden)
+ if #items == 0 then
+ local selected_index = menu.menu(title, items, header_lines + 1, header_lines, "Empty directory. Press 'h' to go up, 'q' to quit.")
+ if selected_index == 0 then
+ local new_cwd, err = change_dir(cwd, "..", base_dir)
+ if new_cwd then
+ cwd = new_cwd
+ display.write_header(title)
+ else
+ display.status_line("Error: " .. (err or "unknown"), colors.REVERSE)
+ os.execute("sleep 1")
+ end
+ else
+ running = false
+ end
+ else
+ local selected_index = menu.menu(title, items, header_lines + 1, header_lines, nil, cwd)
+
+ if not selected_index then
+ running = false
+ elseif selected_index == -1 then
+ show_hidden = not show_hidden
+ elseif selected_index == 0 then
+ local new_cwd, err = change_dir(cwd, "..", base_dir)
+ if new_cwd then
+ cwd = new_cwd
+ display.write_header(title)
+ else
+ display.status_line("Error: " .. (err or "unknown"), colors.REVERSE)
+ os.execute("sleep 2")
+ end
+
+ else
+ local sel = items[selected_index]
+ if sel.type == "dir" then
+ local new_cwd, err = change_dir(cwd, sel.name, base_dir)
+ if new_cwd then
+ cwd = new_cwd
+ display.write_header(title, cwd)
+ else
+ display.write_header(title, cwd)
+ local height = input.get_terminal_height()
+ io.write(string.format("\27[%d;1H\27[K", height))
+ io.write("Error: " .. (err or "unknown") .. "\n")
+ io.flush()
+ os.execute("sleep 1")
+ io.write(string.format("\27[%d;1H\27[K", height))
+ io.flush()
+ end
+ else
+ selected_file = join_path(cwd, sel.name)
+ running = false
+ end
+ end
+ end
+ end
+
+ return selected_file, cwd
+end
+
+return M
+
blob - 1ae918510c51a162be90f8426c383b4913c331ef (mode 644)
blob + /dev/null
--- aestel/navigate.lua
+++ /dev/null
--- aestel/navigate.lua
--- A Lua module for navigating directories
-
-local M = {}
-
--- Dependencies
-local lfs = require("lfs")
-local colors = require("aestel.colors")
-local display = require("aestel.display")
-local input = require("aestel.input")
-local menu = require("aestel.menu")
-
-local home_dir = os.getenv("HOME")
-
--- Path utilities (unchanged)
-local function normalize_path(p)
- p = p:gsub("//+", "/")
- if p == "/." or p == "./" then p = "/" end
- return p:gsub("/+$", "")
-end
-
-local function join_path(base, name)
- if base == "/" then
- return normalize_path("/" .. name)
- end
- return normalize_path(base .. "/" .. name)
-end
-
-local function change_dir(cur, target, base_dir)
- local new_path
- if target == ".." then
- local cur_no_trail = normalize_path(cur):gsub("/+$", "")
- if cur_no_trail == "/" then
- return nil, "Cannot go above root directory (/)"
- end
- new_path = cur_no_trail:match("(.+)/[^/]+$") or "/"
- else
- new_path = join_path(cur, target)
- end
-
- new_path = normalize_path(new_path)
-
- if base_dir then
- local normalized_base = normalize_path(base_dir) .. "/"
- local normalized_new = new_path .. "/"
- if not normalized_new:find(normalized_base, 1, true) then
- return nil, "Access denied -- cannot navigate above base directory."
- end
- end
-
- -- Attr check: Skip for "/", assume exists
- if new_path ~= "/" then
- local attr = lfs.attributes(new_path)
- if not attr then
- return nil, "Path does not exist: " .. (new_path or "unknown")
- end
- if attr.mode ~= "directory" then
- return nil, new_path .. " is not a directory."
- end
- end
- return new_path
-end
-
-local function build_list(path, show_hidden)
- local dirs, files = {}, {}
- for item in lfs.dir(path) do
- if item ~= "." and item ~= ".." then
- if not show_hidden and item:sub(1,1) == "." then
- -- skip hidden
- else
- local full = join_path(path, item)
- local attr = lfs.attributes(full)
- if attr then
- if attr.mode == "directory" then
- table.insert(dirs, item)
- elseif attr.mode == "file" then
- table.insert(files, item)
- end
- end
- end
- end
- end
- table.sort(dirs)
- table.sort(files)
-
- local combined = {}
- for _, d in ipairs(dirs) do
- table.insert(combined, {name = d, type = "dir"})
- end
- for _, f in ipairs(files) do
- table.insert(combined, {name = f, type = "file"})
- end
- return combined
-end
-
-function M.browse(start_dir, base_dir, title)
- title = title or ""
- local cwd = normalize_path(start_dir or lfs.currentdir() or home_dir)
- local show_hidden = false
- local header_lines = display.write_header(title)
-
- local running = true
- local selected_file = nil
-
- while running do
- local items = build_list(cwd, show_hidden)
- if #items == 0 then
- local selected_index = menu.menu(title, items, header_lines + 1, header_lines, "Empty directory. Press 'h' to go up, 'q' to quit.")
- if selected_index == 0 then
- local new_cwd, err = change_dir(cwd, "..", base_dir)
- if new_cwd then
- cwd = new_cwd
- display.write_header(title)
- else
- display.status_line("Error: " .. (err or "unknown"), colors.REVERSE)
- os.execute("sleep 1")
- end
- else
- running = false
- end
- else
- local selected_index = menu.menu(title, items, header_lines + 1, header_lines, nil, cwd)
-
- if not selected_index then
- running = false
- elseif selected_index == -1 then
- show_hidden = not show_hidden
- elseif selected_index == 0 then
- local new_cwd, err = change_dir(cwd, "..", base_dir)
- if new_cwd then
- cwd = new_cwd
- display.write_header(title)
- else
--- display.write_header(title)
--- local height = input.get_terminal_height()
--- io.write(string.format("\27[%d;1H\27[K", height))
--- io.write("Error: " .. (err or "unknown") .. "\n")
--- io.flush()
--- os.execute("sleep 1")
--- io.write(string.format("\27[%d;1H\27[K", height))
--- io.flush()
- display.status_line("Error: " .. (err or "unknown"), colors.REVERSE)
- os.execute("sleep 2")
- end
-
- else
- local sel = items[selected_index]
- if sel.type == "dir" then
- local new_cwd, err = change_dir(cwd, sel.name, base_dir)
- if new_cwd then
- cwd = new_cwd
- display.write_header(title, cwd)
- else
- display.write_header(title, cwd)
- local height = input.get_terminal_height()
- io.write(string.format("\27[%d;1H\27[K", height))
- io.write("Error: " .. (err or "unknown") .. "\n")
- io.flush()
- os.execute("sleep 1")
- io.write(string.format("\27[%d;1H\27[K", height))
- io.flush()
- end
- else
- selected_file = join_path(cwd, sel.name)
- running = false
- end
- end
- end
- end
-
- return selected_file, cwd
-end
-
-return M
-