From 19c7e1fa5e2ed57e93e069afc93ec5a11130441a Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Sun, 31 Aug 2025 21:47:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(installer):=20=E9=87=8D=E5=86=99=E5=AE=89?= =?UTF-8?q?=E8=A3=85=E7=A8=8B=E5=BA=8F=E4=B8=BA=E5=9B=BE=E5=BD=A2=E5=8C=96?= =?UTF-8?q?=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加图形用户界面,改进用户体验 - 实现窗口化界面和进度条显示 - 添加目录选择对话框 - 优化状态显示和进度更新 - 支持鼠标操作和中文显示 --- installer.lua | 379 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 243 insertions(+), 136 deletions(-) diff --git a/installer.lua b/installer.lua index 0501d28..a6c8e1c 100644 --- a/installer.lua +++ b/installer.lua @@ -1,8 +1,14 @@ --- LeonOS installer +-- LeonOS GUI Installer local DEFAULT_ROM_DIR = "/rc" -print("Start loading LeonOS installer...") -print("[Installer] Loading module 1") +local json, tu +local w, h +local mainWindow, progressBar, statusText + +-- 确保中文字符正常显示 +term.setEncoding("utf-8") + +-- 下载函数 local function dl(f) local hand, err = http.get(f, nil, true) if not hand then @@ -14,159 +20,260 @@ local function dl(f) return data end -print("[Installer] Loading done.") -print("[Installer] Loading module 2") --- set up package.loaded for LeonOS libs -package.loaded.rc = { - expect = require("cc.expect").expect, - write = write, sleep = sleep -} -print("[Installer] Loading done.") -print("[Installer] Loading module 3") -package.loaded.term = term -package.loaded.colors = colors -_G.require = require -print("[Installer] Loading done.") -print("[Installer] Loading module 4") -function term.at(x, y) - term.setCursorPos(x, y) - return term -end -print("[Installer] Loading done.") -print("[Installer] Loading module 5") + +-- 加载GitHub上的文件 local function ghload(f, c) return assert(load(dl("https://gh.catmak.name/https://raw.githubusercontent.com/"..f), "="..(c or f), "t", _G))() end -print("[Installer] Loading done.") -print("[Installer] Loading module 6") -local json = ghload("rxi/json.lua/master/json.lua", "ghload(json)") -package.loaded["rc.json"] = json -print("[Installer] Loading module 7") + +-- 加载LeonOS的文件 local function rcload(f) return ghload( "Leonmmcoset/LeonOS/refs/heads/main/data/computercraft/lua/rom/"..f, f) end -print("[Installer] Loading done.") -print("[Installer] Loading module 8") --- get LeonOS's textutils with its extra utilities -local tu = rcload("apis/textutils.lua") -local function progress(y, a, b) - local progress = a/b +-- 初始化函数 +local function init() + -- 加载必要的库 + updateStatus("加载必要的库...") + json = ghload("rxi/json.lua/master/json.lua", "ghload(json)") + tu = rcload("apis/textutils.lua") + updateStatus("库加载完成") - local w = term.getSize() - local bar = (" "):rep(math.ceil((w-2) * progress)) - term.at(1, y) - tu.coloredPrint(colors.yellow, "[", {fg=colors.white, bg=colors.white}, bar, - {fg=colors.white, bg=colors.black}, (" "):rep((w-2)-#bar), - colors.yellow, "]") -end -print("[Installer] Loading done.") -term.at(1,1).clear() -tu.coloredPrint(colors.yellow, - "LeonOS Installer\n=======================") -print("You are going to install LeonOS to your computer.") -print("This will overwrite any existing files in the installation directory.") -tu.coloredPrint(colors.yellow, "Are you sure? (y/n)") -local confirm = read() -if confirm ~= "y" then - print("Installation cancelled.") - return -end - -local ROM_DIR -tu.coloredPrint("Enter installation directory ", colors.yellow, "[", - colors.lightBlue, DEFAULT_ROM_DIR, colors.yellow, "]") -tu.coloredWrite(colors.yellow, "$ installer >>>") - -ROM_DIR = read() -if #ROM_DIR == 0 then ROM_DIR = DEFAULT_ROM_DIR end - -ROM_DIR = "/"..shell.resolve(ROM_DIR) - -settings.set("LeonOS.rom_dir", ROM_DIR) -settings.save() - -tu.coloredPrint(colors.white, "Installing LeonOS to ", colors.lightBlue, - ROM_DIR, colors.white) - -local function bullet(t) - tu.coloredWrite(colors.red, "- ", colors.white, t) -end - -local function ok() - tu.coloredPrint(colors.green, "OK", colors.white) -end - -bullet("Getting repository tree...") - -local repodata = dl("https://api.github.com/repos/Leonmmcoset/LeonOS/git/trees/main?recursive=1") - -repodata = json.decode(repodata) - -ok() - -bullet("Filtering files...") -local look = "data/computercraft/lua/" -local to_dl = {} -for _, v in pairs(repodata.tree) do - if v.path and v.path:sub(1,#look) == look then - v.path = v.path:sub(#look+1) - v.real_path = v.path:gsub("^/?rom", ROM_DIR) - to_dl[#to_dl+1] = v + -- 设置term.at函数 + function term.at(x, y) + term.setCursorPos(x, y) + return term end -end -ok() -bullet("Creating directories...") -for i=#to_dl, 1, -1 do - local v = to_dl[i] - if v.type == "tree" then - fs.makeDir(fs.combine(v.real_path)) - table.remove(to_dl, i) + -- 获取屏幕大小 + w, h = term.getSize() + + -- 创建主窗口 + mainWindow = window.create(term.native(), 1, 1, w, h) + mainWindow.setVisible(true) + + -- 绘制标题 + mainWindow.setTextColor(colors.yellow) + mainWindow.setBackgroundColor(colors.black) + mainWindow.at(2, 2).write("===== LeonOS 安装程序 ======") + + -- 创建状态文本区域 + statusText = window.create(mainWindow, 2, 4, w-2, 10) + statusText.setTextColor(colors.white) + statusText.setBackgroundColor(colors.black) + statusText.clear() + statusText.at(1, 1).write("欢迎使用LeonOS安装程序") + statusText.at(1, 2).write("该程序将安装LeonOS到您的计算机") + statusText.at(1, 3).write("安装过程会覆盖目标目录中的现有文件") + + -- 创建进度条 + progressBar = window.create(mainWindow, 2, h-4, w-2, 3) + progressBar.setTextColor(colors.white) + progressBar.setBackgroundColor(colors.gray) + progressBar.clear() + progressBar.at(1, 1).write("进度: ") + + -- 创建按钮 + local buttonWidth = 15 + local yesButton = window.create(mainWindow, w/2 - buttonWidth - 2, h-8, buttonWidth, 3) + yesButton.setTextColor(colors.white) + yesButton.setBackgroundColor(colors.green) + yesButton.at(3, 2).write("确定") + + local noButton = window.create(mainWindow, w/2 + 2, h-8, buttonWidth, 3) + noButton.setTextColor(colors.white) + noButton.setBackgroundColor(colors.red) + noButton.at(3, 2).write("取消") + + return yesButton, noButton +end + +-- 更新进度条 +local function updateProgress(current, total) + progressBar.clear() + local percentage = math.floor((current / total) * 100) + local barWidth = w - 10 + local filledWidth = math.floor((current / total) * barWidth) + + progressBar.at(1, 1).write("进度: " .. percentage .. "%") + progressBar.at(1, 2).setBackgroundColor(colors.gray) + progressBar.at(1, 2).write(string.rep(" ", barWidth)) + progressBar.at(1, 2).setBackgroundColor(colors.green) + progressBar.at(1, 2).write(string.rep(" ", filledWidth)) +end + +-- 更新状态文本 +local function updateStatus(text) + statusText.clear() + local lines = {} + for line in text:gmatch("[^ +]+") do + table.insert(lines, line) end -end -ok() - -bullet("Downloading files...") -local okx, oky = term.getCursorPos() -io.write("\n") -local _, pby = term.getCursorPos() - -local parallels = {} -local done = 0 - -for i=1, #to_dl, 1 do - local v = to_dl[i] - if v.type == "blob" then - parallels[#parallels+1] = function() - local data = dl("https://gh.catmak.name/https://raw.githubusercontent.com/Leonmmcoset/LeonOS/refs/heads/main/data/computercraft/lua/"..v.path) - assert(io.open(v.real_path, "w")):write(data):close() - done = done + 1 - progress(pby, done, #to_dl) + for i, line in ipairs(lines) do + if i <= statusText.getSize() then + statusText.at(1, i).write(line) end end end -parallel.waitForAll(table.unpack(parallels)) +-- 目录选择对话框 +local function selectDirDialog(defaultDir) + local dialogWindow = window.create(term.native(), w/4, h/4, w/2, 8) + dialogWindow.setTextColor(colors.white) + dialogWindow.setBackgroundColor(colors.black) + dialogWindow.clear() + dialogWindow.at(2, 2).write("选择安装目录") + dialogWindow.at(2, 4).write("目录: ") + dialogWindow.at(9, 4).write(defaultDir) -term.at(1, pby).write((" "):rep((term.getSize()))) -term.at(okx, oky) -ok() + local confirmButton = window.create(dialogWindow, dialogWindow.getSize() - 15, 6, 10, 2) + confirmButton.setTextColor(colors.white) + confirmButton.setBackgroundColor(colors.green) + confirmButton.at(2, 1).write("确认") -assert(io.open( - fs.exists("/startup.lua") and "/unbios-rc.lua" or "/startup.lua", "w")) - :write(dl( - "https://gh.catmak.name/https://raw.githubusercontent.com/Leonmmcoset/LeonOS/refs/heads/main/unbios.lua" - )):close() + local dirInput = defaultDir + local editing = true -tu.coloredPrint(colors.yellow, "Your computer will restart in 3 seconds.") -local _, y = term.getCursorPos() + while editing do + local event = table.pack(os.pullEvent()) + if event[1] == "mouse_click" then + local x, y = event[3], event[4] + -- 检查是否点击确认按钮 + if x >= dialogWindow.getX() + dialogWindow.getSize() - 15 and + x <= dialogWindow.getX() + dialogWindow.getSize() - 5 and + y >= dialogWindow.getY() + 6 and + y <= dialogWindow.getY() + 7 then + editing = false + end + elseif event[1] == "key" then + if event[2] == keys.enter then + editing = false + elseif event[2] == keys.backspace then + dirInput = dirInput:sub(1, -2) + end + elseif event[1] == "char" then + dirInput = dirInput .. event[2] + end -for i=1, 3, 1 do - progress(y, i, 5) - os.sleep(1) + dialogWindow.at(9, 4).write(string.rep(" ", 30)) + dialogWindow.at(9, 4).write(dirInput) + end + + dialogWindow.setVisible(false) + return dirInput end -os.reboot() +-- 主安装函数 +local function install(romDir) + updateStatus("正在获取仓库文件列表...\n请稍候...") + + -- 获取仓库文件列表 + local repodata = dl("https://api.github.com/repos/Leonmmcoset/LeonOS/git/trees/main?recursive=1") + repodata = json.decode(repodata) + updateProgress(1, 5) + + updateStatus("正在筛选文件...\n请稍候...") + -- 筛选文件 + local look = "data/computercraft/lua/" + local to_dl = {} + for _, v in pairs(repodata.tree) do + if v.path and v.path:sub(1, #look) == look then + v.path = v.path:sub(#look + 1) + v.real_path = v.path:gsub("^/?rom", romDir) + to_dl[#to_dl + 1] = v + end + end + updateProgress(2, 5) + + updateStatus("正在创建目录...\n请稍候...") + -- 创建目录 + for i = #to_dl, 1, -1 do + local v = to_dl[i] + if v.type == "tree" then + fs.makeDir(fs.combine(v.real_path)) + table.remove(to_dl, i) + end + end + updateProgress(3, 5) + + updateStatus("正在下载文件...\n这可能需要一些时间...") + -- 下载文件 + local done = 0 + local parallels = {} + + for i = 1, #to_dl, 1 do + local v = to_dl[i] + if v.type == "blob" then + parallels[#parallels + 1] = function() + local data = dl("https://gh.catmak.name/https://raw.githubusercontent.com/Leonmmcoset/LeonOS/refs/heads/main/data/computercraft/lua/" .. v.path) + assert(io.open(v.real_path, "w")):write(data):close() + done = done + 1 + updateProgress(3 + (done / #to_dl), 5) + end + end + end + + parallel.waitForAll(table.unpack(parallels)) + updateProgress(4, 5) + + updateStatus("正在设置启动文件...\n请稍候...") + -- 设置启动文件 + assert(io.open( + fs.exists("/startup.lua") and "/unbios-rc.lua" or "/startup.lua", "w")) + :write(dl( + "https://gh.catmak.name/https://raw.githubusercontent.com/Leonmmcoset/LeonOS/refs/heads/main/unbios.lua" + )):close() + updateProgress(5, 5) + + updateStatus("安装完成!\n计算机将在3秒后重启...") + -- 重启倒计时 + for i = 3, 1, -1 do + statusText.at(1, 3).write("计算机将在 " .. i .. " 秒后重启...") + os.sleep(1) + end + + os.reboot() +end + +-- 主函数 +local function main() + term.clear() + local yesButton, noButton = init() + + while true do + local event = table.pack(os.pullEvent()) + if event[1] == "mouse_click" then + local x, y = event[3], event[4] + + -- 检查是否点击"确定"按钮 + if x >= yesButton.getX() and x <= yesButton.getX() + yesButton.getSize() and + y >= yesButton.getY() and y <= yesButton.getY() + 2 then + mainWindow.setVisible(false) + local romDir = selectDirDialog(DEFAULT_ROM_DIR) + if #romDir == 0 then romDir = DEFAULT_ROM_DIR end + romDir = "/" .. shell.resolve(romDir) + settings.set("LeonOS.rom_dir", romDir) + settings.save() + mainWindow.setVisible(true) + updateStatus("开始安装LeonOS到目录: " .. romDir .. "\n请稍候...") + install(romDir) + break + end + + -- 检查是否点击"取消"按钮 + if x >= noButton.getX() and x <= noButton.getX() + noButton.getSize() and + y >= noButton.getY() and y <= noButton.getY() + 2 then + mainWindow.setVisible(false) + term.clear() + print("安装已取消") + break + end + end + end +end + +-- 启动程序 +main()