diff --git a/installer.lua b/installer.lua index fa78fef..65bfea1 100644 --- a/installer.lua +++ b/installer.lua @@ -1,14 +1,27 @@ --- LeonOS GUI Installer +-- LeonOS installer + +-- 程序顶部名称栏 +local term = require("term") +local colors = require("colors") + +-- 保存当前颜色设置 +local old_fg = term.getTextColor() +local old_bg = term.getBackgroundColor() + +-- 设置名称栏颜色并显示 +term.setTextColor(colors.white) +term.setBackgroundColor(colors.cyan) +term.at(1, 1).clearLine() +term.at(1, 1).write("=== LeonOS Installer ===") + +-- 恢复颜色设置 +term.setTextColor(old_fg) +term.setBackgroundColor(old_bg) +term.at(1, 2) local DEFAULT_ROM_DIR = "/rc" -local json, tu -local w, h -local mainWindow, progressBar, statusText - --- 确保中文字符正常显示 -term.setEncoding("utf-8") - --- 下载函数 +term.at(1, 2).write("Start loading LeonOS installer...\n") +term.write("[Installer] Loading module 1\n") local function dl(f) local hand, err = http.get(f, nil, true) if not hand then @@ -20,259 +33,163 @@ local function dl(f) return data end - --- 加载GitHub上的文件 +term.write("[Installer] Loading done.\n") +term.write("[Installer] Loading module 2\n") +-- set up package.loaded for LeonOS libs +package.loaded.rc = { + expect = require("cc.expect").expect, + write = write, sleep = sleep +} +term.write("[Installer] Loading done.\n") +term.write("[Installer] Loading module 3\n") +package.loaded.term = term +package.loaded.colors = colors +_G.require = require +term.write("[Installer] Loading done.\n") +term.write("[Installer] Loading module 4\n") +function term.at(x, y) + term.setCursorPos(x, y) + return term +end +term.write("[Installer] Loading done.\n") +term.write("[Installer] Loading module 5\n") local function ghload(f, c) return assert(load(dl("https://gh.catmak.name/https://raw.githubusercontent.com/"..f), "="..(c or f), "t", _G))() end - --- 加载LeonOS的文件 +term.write("[Installer] Loading done.\n") +term.write("[Installer] Loading module 6\n") +local json = ghload("rxi/json.lua/master/json.lua", "ghload(json)") +package.loaded["rc.json"] = json +term.write("[Installer] Loading module 7\n") local function rcload(f) return ghload( "Leonmmcoset/LeonOS/refs/heads/main/data/computercraft/lua/rom/"..f, f) end +term.write("[Installer] Loading done.\n") +term.write("[Installer] Loading module 8\n") +-- get LeonOS's textutils with its extra utilities +local tu = rcload("apis/textutils.lua") --- 初始化函数 -local function init() - -- 加载必要的库 - updateStatus("加载必要的库...") - json = ghload("rxi/json.lua/master/json.lua", "ghload(json)") - tu = rcload("apis/textutils.lua") - updateStatus("库加载完成") +local function progress(y, a, b) + local progress = a/b - -- 设置term.at函数 - function term.at(x, y) - term.setCursorPos(x, y) - return term - end - - -- 获取屏幕大小 - 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 + 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 +term.write("[Installer] Loading done.\n") +-- 只清除顶栏以下的区域 +for y=2, term.getSize() do + term.at(1, y).clearLine() +end +term.at(1, 2) +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 function updateProgress(current, total) - progressBar.clear() - local percentage = math.floor((current / total) * 100) - local barWidth = w - 10 - local filledWidth = math.floor((current / total) * barWidth) +local ROM_DIR +tu.coloredPrint("Enter installation directory ", colors.yellow, "[", + colors.lightBlue, DEFAULT_ROM_DIR, colors.yellow, "]") +tu.coloredWrite(colors.yellow, "$ installer >>>") - 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)) +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 updateStatus(text) - statusText.clear() - local lines = {} - for line in text:gmatch("[^]+") do - table.insert(lines, line) +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 end - for i, line in ipairs(lines) do - if i <= statusText.getSize() then - statusText.at(1, i).write(line) +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) + 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) end end end --- 目录选择对话框 -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) +parallel.waitForAll(table.unpack(parallels)) - local confirmButton = window.create(dialogWindow, dialogWindow.getSize() - 15, 6, 10, 2) - confirmButton.setTextColor(colors.white) - confirmButton.setBackgroundColor(colors.green) - confirmButton.at(2, 1).write("确认") +term.at(1, pby).write((" "):rep((term.getSize()))) +term.at(okx, oky) +ok() - local dirInput = defaultDir - local editing = true +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() - 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 +tu.coloredPrint(colors.yellow, "Your computer will restart in 3 seconds.") +local _, y = term.getCursorPos() - dialogWindow.at(9, 4).write(string.rep(" ", 30)) - dialogWindow.at(9, 4).write(dirInput) - end - - dialogWindow.setVisible(false) - return dirInput +for i=1, 3, 1 do + progress(y, i, 5) + os.sleep(1) end --- 主安装函数 -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() +os.reboot()