luajitos

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 24f03437c48d300bed3c8033f0038a8c5309866a
parent a7f852f14c9223f1034d06d0cd1d9f88d51bb887
Author: luajitos <bbhbb2094@gmail.com>
Date:   Mon,  1 Dec 2025 23:45:28 +0000

Fixed Lunar Editor and Explorer

Diffstat:
M.gitignore | 3+++
MCLAUDE.md | 5+++--
Miso_includes/apps/com.luajitos.explorer/src/explorer.lua | 7++++---
Aiso_includes/apps/com.luajitos.lunareditor/src/editor.lua | 427+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Miso_includes/os/libs/Dialog.lua | 3+++
Miso_includes/os/libs/SafeFS.lua | 80+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
6 files changed, 492 insertions(+), 33 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -42,3 +42,6 @@ taskbar.bmp # Markdown files *.MD + +# Old Files +splash.png diff --git a/CLAUDE.md b/CLAUDE.md @@ -1 +1,2 @@ -- if you only change lua then just run ./repack_lua.sh and tell me that you have repacked it, if you have changed C code then rebuild it with ./build.sh -\ No newline at end of file +- if you only change lua then just run ./repack_lua.sh and tell me that you have repacked it, if you have changed C code then rebuild it with ./build.sh +- NEVER USE entry="src/... BECAUSE THE PATH IS RELATIVE TO src +\ No newline at end of file diff --git a/iso_includes/apps/com.luajitos.explorer/src/explorer.lua b/iso_includes/apps/com.luajitos.explorer/src/explorer.lua @@ -136,9 +136,10 @@ window.onClick = function(mx, my) osprint("[EXPLORER] onClick CALLED! Mouse click at (" .. mx .. ", " .. my .. ")\n") end - -- Check if click is in the file list area - if my > headerHeight and my < windowHeight - footerHeight then - local clickedIndex = math.floor((my - headerHeight) / lineHeight) + 1 + scrollOffset + -- Check if click is in the file list area (below header and column headers row) + local listStartY = headerHeight + lineHeight -- Account for column headers row + if my > listStartY and my < windowHeight - footerHeight then + local clickedIndex = math.floor((my - listStartY) / lineHeight) + 1 + scrollOffset if osprint then osprint("[EXPLORER] Clicked index: " .. clickedIndex .. " (total entries: " .. #entries .. ")\n") diff --git a/iso_includes/apps/com.luajitos.lunareditor/src/editor.lua b/iso_includes/apps/com.luajitos.lunareditor/src/editor.lua @@ -0,0 +1,427 @@ +-- Lunar Editor - Simple text editor for LuajitOS +-- Displays text files with a toolbar (New, Open, Save) + +local windowWidth = 640 +local windowHeight = 480 +local toolbarHeight = 30 +local statusBarHeight = 20 +local contentHeight = windowHeight - toolbarHeight - statusBarHeight + +-- Editor state +local lines = {""} -- Array of lines +local cursorLine = 1 +local cursorCol = 1 +local scrollY = 0 -- Scroll offset in lines +local currentFile = nil -- Currently open file path +local modified = false -- Has the file been modified? + +-- Font metrics (scale 1 = 8px base font, but we use larger spacing) +local fontScale = 1 +local charWidth = 8 +local lineHeight = 12 +local visibleLines = math.floor(contentHeight / lineHeight) + +-- Toolbar buttons +local toolbarButtons = { + { x = 5, y = 5, width = 50, height = 20, label = "New", action = "new" }, + { x = 60, y = 5, width = 50, height = 20, label = "Open", action = "open" }, + { x = 115, y = 5, width = 50, height = 20, label = "Save", action = "save" }, + { x = 170, y = 5, width = 55, height = 20, label = "SaveAs", action = "saveas" }, +} + +-- Helper: get window title +local function getTitle() + local title = "Lunar Editor" + if currentFile then + -- Get filename from path + local filename = currentFile:match("([^/]+)$") or currentFile + title = title .. " - " .. filename + else + title = title .. " - Untitled" + end + if modified then + title = title .. " *" + end + return title +end + +-- Create window +local window = app:newWindow(getTitle(), windowWidth, windowHeight, true) + +if not window then + print("Lunar Editor: Failed to create window") + return +end + +print("Lunar Editor: Starting...") + +-- Helper: update title +local function updateTitle() + window.title = getTitle() +end + +-- Helper: ensure cursor is in valid position +local function clampCursor() + if cursorLine < 1 then cursorLine = 1 end + if cursorLine > #lines then cursorLine = #lines end + if cursorCol < 1 then cursorCol = 1 end + local lineLen = #lines[cursorLine] + if cursorCol > lineLen + 1 then cursorCol = lineLen + 1 end +end + +-- Helper: ensure cursor is visible (scroll if needed) +local function ensureCursorVisible() + if cursorLine <= scrollY then + scrollY = cursorLine - 1 + elseif cursorLine > scrollY + visibleLines then + scrollY = cursorLine - visibleLines + end +end + +-- File operations +local function newFile() + lines = {""} + cursorLine = 1 + cursorCol = 1 + scrollY = 0 + currentFile = nil + modified = false + updateTitle() + window:markDirty() + print("Lunar Editor: New file") +end + +local function loadFile(path) + if not fs then + print("Lunar Editor: Filesystem not available") + return false + end + + local content, err = fs:read(path) + if not content then + print("Lunar Editor: Failed to read file: " .. tostring(err)) + return false + end + + -- Split content into lines + lines = {} + for line in (content .. "\n"):gmatch("([^\n]*)\n") do + table.insert(lines, line) + end + + -- Ensure at least one line + if #lines == 0 then + lines = {""} + end + + cursorLine = 1 + cursorCol = 1 + scrollY = 0 + currentFile = path + modified = false + updateTitle() + window:markDirty() + print("Lunar Editor: Loaded " .. path .. " (" .. #lines .. " lines)") + return true +end + +local function saveFile(path) + if not fs then + print("Lunar Editor: Filesystem not available") + return false + end + + -- Join lines with newlines + local content = table.concat(lines, "\n") + + local ok, err = fs:write(path, content) + if not ok then + print("Lunar Editor: Failed to save file: " .. tostring(err)) + return false + end + + currentFile = path + modified = false + updateTitle() + window:markDirty() + print("Lunar Editor: Saved " .. path) + return true +end + +-- Dialog functions +local function openFileDialog() + if not Dialog or not Dialog.fileOpen then + print("Lunar Editor: Dialog.fileOpen not available") + return + end + + local startPath = "/home" + if currentFile then + -- Use current file's directory + startPath = currentFile:match("(.*/)") + if not startPath then startPath = "/home" end + end + + local dialog = Dialog.fileOpen(startPath, { + app = app, + fs = fs, + title = "Open File" + }) + + dialog:openDialog(function(selectedPath) + if selectedPath then + loadFile(selectedPath) + end + end) +end + +local function saveFileDialog() + if not Dialog or not Dialog.fileSave then + print("Lunar Editor: Dialog.fileSave not available") + return + end + + local startPath = "/home" + local defaultName = "untitled.txt" + + if currentFile then + startPath = currentFile:match("(.*/)") + defaultName = currentFile:match("([^/]+)$") or "untitled.txt" + if not startPath then startPath = "/home" end + end + + local dialog = Dialog.fileSave(startPath, defaultName, { + app = app, + fs = fs, + title = "Save File" + }) + + dialog:openDialog(function(selectedPath) + if selectedPath then + saveFile(selectedPath) + end + end) +end + +-- Draw callback +window.onDraw = function(gfx) + -- Draw toolbar background + gfx:fillRect(0, 0, windowWidth, toolbarHeight, 0x404040) + + -- Draw toolbar buttons + for _, btn in ipairs(toolbarButtons) do + gfx:fillRect(btn.x, btn.y, btn.width, btn.height, 0x606060) + gfx:drawRect(btn.x, btn.y, btn.width, btn.height, 0x808080) + local textX = btn.x + (btn.width - #btn.label * 6) / 2 + local textY = btn.y + 6 + gfx:drawText(textX, textY, btn.label, 0xFFFFFF) + end + + -- Draw separator line + gfx:fillRect(0, toolbarHeight - 1, windowWidth, 1, 0x303030) + + -- Draw content area (white background) + gfx:fillRect(0, toolbarHeight, windowWidth, contentHeight, 0xFFFFFF) + + -- Draw text content + local y = toolbarHeight + 2 + local startLine = scrollY + 1 + local endLine = math.min(#lines, scrollY + visibleLines) + + for i = startLine, endLine do + local line = lines[i] or "" + gfx:drawText(5, y, line, 0x000000, fontScale) + + -- Draw cursor if on this line + if i == cursorLine then + local cursorX = 5 + (cursorCol - 1) * charWidth + local cursorY = y + -- Draw cursor as a vertical line + gfx:fillRect(cursorX, cursorY, 2, lineHeight, 0x000000) + end + + y = y + lineHeight + end + + -- Draw status bar + local statusY = windowHeight - statusBarHeight + gfx:fillRect(0, statusY, windowWidth, statusBarHeight, 0x333333) + + -- Status text: line/col info + local statusText = "Ln " .. cursorLine .. ", Col " .. cursorCol + if currentFile then + statusText = currentFile .. " | " .. statusText + else + statusText = "Untitled | " .. statusText + end + gfx:drawText(5, statusY + 4, statusText, 0xAAAAAA) + + -- Right side: modified indicator + if modified then + gfx:drawText(windowWidth - 70, statusY + 4, "Modified", 0xFFAAAA) + end +end + +-- Input callback +window.onInput = function(key, scancode) + local baseScancode = scancode % 128 + + -- Arrow keys + if baseScancode == 72 then -- Up + if cursorLine > 1 then + cursorLine = cursorLine - 1 + clampCursor() + ensureCursorVisible() + window:markDirty() + end + return + elseif baseScancode == 80 then -- Down + if cursorLine < #lines then + cursorLine = cursorLine + 1 + clampCursor() + ensureCursorVisible() + window:markDirty() + end + return + elseif baseScancode == 75 then -- Left + if cursorCol > 1 then + cursorCol = cursorCol - 1 + elseif cursorLine > 1 then + cursorLine = cursorLine - 1 + cursorCol = #lines[cursorLine] + 1 + end + clampCursor() + ensureCursorVisible() + window:markDirty() + return + elseif baseScancode == 77 then -- Right + if cursorCol <= #lines[cursorLine] then + cursorCol = cursorCol + 1 + elseif cursorLine < #lines then + cursorLine = cursorLine + 1 + cursorCol = 1 + end + clampCursor() + ensureCursorVisible() + window:markDirty() + return + elseif baseScancode == 71 then -- Home + cursorCol = 1 + window:markDirty() + return + elseif baseScancode == 79 then -- End + cursorCol = #lines[cursorLine] + 1 + window:markDirty() + return + elseif baseScancode == 73 then -- Page Up + cursorLine = math.max(1, cursorLine - visibleLines) + clampCursor() + ensureCursorVisible() + window:markDirty() + return + elseif baseScancode == 81 then -- Page Down + cursorLine = math.min(#lines, cursorLine + visibleLines) + clampCursor() + ensureCursorVisible() + window:markDirty() + return + end + + -- Text input + if key == "\b" then -- Backspace + if cursorCol > 1 then + local line = lines[cursorLine] + lines[cursorLine] = line:sub(1, cursorCol - 2) .. line:sub(cursorCol) + cursorCol = cursorCol - 1 + modified = true + updateTitle() + elseif cursorLine > 1 then + -- Join with previous line + local prevLine = lines[cursorLine - 1] + cursorCol = #prevLine + 1 + lines[cursorLine - 1] = prevLine .. lines[cursorLine] + table.remove(lines, cursorLine) + cursorLine = cursorLine - 1 + modified = true + updateTitle() + end + clampCursor() + ensureCursorVisible() + window:markDirty() + elseif key == "\n" then -- Enter + local line = lines[cursorLine] + local beforeCursor = line:sub(1, cursorCol - 1) + local afterCursor = line:sub(cursorCol) + lines[cursorLine] = beforeCursor + table.insert(lines, cursorLine + 1, afterCursor) + cursorLine = cursorLine + 1 + cursorCol = 1 + modified = true + updateTitle() + clampCursor() + ensureCursorVisible() + window:markDirty() + elseif key and #key == 1 and key:byte() >= 32 then -- Printable character + local line = lines[cursorLine] + lines[cursorLine] = line:sub(1, cursorCol - 1) .. key .. line:sub(cursorCol) + cursorCol = cursorCol + 1 + modified = true + updateTitle() + window:markDirty() + end +end + +-- Click callback +window.onClick = function(x, y, button) + -- Check toolbar + if y < toolbarHeight then + for _, btn in ipairs(toolbarButtons) do + if x >= btn.x and x < btn.x + btn.width and + y >= btn.y and y < btn.y + btn.height then + print("Lunar Editor: Button clicked: " .. btn.action) + if btn.action == "new" then + newFile() + elseif btn.action == "open" then + openFileDialog() + elseif btn.action == "save" then + if currentFile then + saveFile(currentFile) + else + saveFileDialog() + end + elseif btn.action == "saveas" then + saveFileDialog() + end + return + end + end + return + end + + -- Click in content area - move cursor + if y >= toolbarHeight and y < windowHeight - statusBarHeight then + local contentY = y - toolbarHeight + local clickedLine = scrollY + math.floor(contentY / lineHeight) + 1 + if clickedLine >= 1 and clickedLine <= #lines then + cursorLine = clickedLine + local clickedCol = math.floor((x - 5) / charWidth) + 1 + cursorCol = math.max(1, math.min(clickedCol, #lines[cursorLine] + 1)) + window:markDirty() + end + end +end + +-- Check for command line arguments to open a file +if args then + local argPath = args.o or args.open or args[1] + if argPath and argPath ~= "" then + -- Expand ~ to /home + if argPath:sub(1, 1) == "~" then + argPath = "/home" .. argPath:sub(2) + end + print("Lunar Editor: Opening file from arguments: " .. argPath) + loadFile(argPath) + end +end + +print("Lunar Editor: Ready") diff --git a/iso_includes/os/libs/Dialog.lua b/iso_includes/os/libs/Dialog.lua @@ -346,6 +346,9 @@ function Dialog.fileOpen(startPath, options) -- Click handler self.window.onClick = function(mx, my) + if osprint then + osprint("[Dialog.fileOpen.onClick] mx=" .. mx .. " my=" .. my .. " window.y=" .. tostring(self.window.y) .. "\n") + end local listY = 35 local listHeight = height - listY - 40 local itemHeight = 25 diff --git a/iso_includes/os/libs/SafeFS.lua b/iso_includes/os/libs/SafeFS.lua @@ -133,6 +133,33 @@ local function matchesPattern(path, pattern) return false end +-- Helper: Check if a path would clash with a pseudo file +local function checkPseudoFileClash(parentDir, filename) + if not parentDir or not parentDir.files then + return false, nil + end + + -- Check if there's a pseudo file with this exact name + for _, file in ipairs(parentDir.files) do + if file.isPseudo and file.name == filename then + return true, file + end + end + + -- Check if filename looks like it has arguments (matches "pseudofile .*") + local spacePos = filename:find(" ") + if spacePos then + local baseName = filename:sub(1, spacePos - 1) + for _, file in ipairs(parentDir.files) do + if file.isPseudo and file.name == baseName then + return true, file + end + end + end + + return false, nil +end + -- Helper: Check if creating at this path would place something directly in root -- Returns true if the path's parent is "/" (the root directory) local function isDirectlyInRoot(path) @@ -922,6 +949,14 @@ function SafeFS:write(path, content) -- Update existing file file.content = content + -- Also update in C ramdisk + if _CRamdiskOpen and _CRamdiskWrite and _CRamdiskClose then + local handle = _CRamdiskOpen(resolvedPath, "w") + if handle then + _CRamdiskWrite(handle, content) + _CRamdiskClose(handle) + end + end return true end end @@ -933,7 +968,7 @@ function SafeFS:write(path, content) return false, "Cannot create file: would clash with pseudo file '" .. pseudoFile.name .. "'" end - -- Create new file + -- Create new file in SafeFS tree if not parentDir.files then parentDir.files = {} end @@ -946,6 +981,22 @@ function SafeFS:write(path, content) } table.insert(parentDir.files, newFile) + -- Also write to C ramdisk so other apps can see it + if _CRamdiskOpen and _CRamdiskWrite and _CRamdiskClose then + local handle, err = _CRamdiskOpen(resolvedPath, "w") + if handle then + _CRamdiskWrite(handle, content) + _CRamdiskClose(handle) + if osprint then + osprint("[SafeFS] Wrote to C ramdisk: " .. resolvedPath .. "\n") + end + else + if osprint then + osprint("[SafeFS] Failed to open for write: " .. resolvedPath .. " err=" .. tostring(err) .. "\n") + end + end + end + return true end @@ -1717,33 +1768,6 @@ function SafeFS:move(source, destination) return true end --- Helper: Check if a path would clash with a pseudo file -local function checkPseudoFileClash(parentDir, filename) - if not parentDir or not parentDir.files then - return false, nil - end - - -- Check if there's a pseudo file with this exact name - for _, file in ipairs(parentDir.files) do - if file.isPseudo and file.name == filename then - return true, file - end - end - - -- Check if filename looks like it has arguments (matches "pseudofile .*") - local spacePos = filename:find(" ") - if spacePos then - local baseName = filename:sub(1, spacePos - 1) - for _, file in ipairs(parentDir.files) do - if file.isPseudo and file.name == baseName then - return true, file - end - end - end - - return false, nil -end - -- Create a pseudo file that uses callbacks instead of content -- The file node will call onRead(args) to get content and onWrite(data) to receive writes function SafeFS:createPseudoFile(path)