refactor(installer): 重写安装程序为命令行界面并优化加载流程

- 移除GUI界面改为更简洁的命令行交互
- 添加模块化加载进度显示
- 简化目录选择和安装确认流程
- 优化进度条显示实现
- 保留核心安装功能但重构代码结构
This commit is contained in:
2025-08-31 22:13:53 +08:00
parent 3c91b367a9
commit 4f87a305ae

View File

@@ -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 DEFAULT_ROM_DIR = "/rc"
local json, tu term.at(1, 2).write("Start loading LeonOS installer...\n")
local w, h term.write("[Installer] Loading module 1\n")
local mainWindow, progressBar, statusText
-- 确保中文字符正常显示
term.setEncoding("utf-8")
-- 下载函数
local function dl(f) local function dl(f)
local hand, err = http.get(f, nil, true) local hand, err = http.get(f, nil, true)
if not hand then if not hand then
@@ -20,259 +33,163 @@ local function dl(f)
return data return data
end end
term.write("[Installer] Loading done.\n")
-- 加载GitHub上的文件 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) local function ghload(f, c)
return assert(load(dl("https://gh.catmak.name/https://raw.githubusercontent.com/"..f), return assert(load(dl("https://gh.catmak.name/https://raw.githubusercontent.com/"..f),
"="..(c or f), "t", _G))() "="..(c or f), "t", _G))()
end end
term.write("[Installer] Loading done.\n")
-- 加载LeonOS的文件 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) local function rcload(f)
return ghload( return ghload(
"Leonmmcoset/LeonOS/refs/heads/main/data/computercraft/lua/rom/"..f, f) "Leonmmcoset/LeonOS/refs/heads/main/data/computercraft/lua/rom/"..f, f)
end 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 progress(y, a, b)
local function init() local progress = a/b
-- 加载必要的库
updateStatus("加载必要的库...")
json = ghload("rxi/json.lua/master/json.lua", "ghload(json)")
tu = rcload("apis/textutils.lua")
updateStatus("库加载完成")
-- 设置term.at函数 local w = term.getSize()
function term.at(x, y) local bar = (" "):rep(math.ceil((w-2) * progress))
term.setCursorPos(x, y) term.at(1, y)
return term tu.coloredPrint(colors.yellow, "[", {fg=colors.white, bg=colors.white}, bar,
end {fg=colors.white, bg=colors.black}, (" "):rep((w-2)-#bar),
colors.yellow, "]")
-- 获取屏幕大小 end
w, h = term.getSize() term.write("[Installer] Loading done.\n")
-- 只清除顶栏以下的区域
-- 创建主窗口 for y=2, term.getSize() do
mainWindow = window.create(term.native(), 1, 1, w, h) term.at(1, y).clearLine()
mainWindow.setVisible(true) end
term.at(1, 2)
-- 绘制标题 tu.coloredPrint(colors.yellow,
mainWindow.setTextColor(colors.yellow) "LeonOS Installer\n=======================")
mainWindow.setBackgroundColor(colors.black) print("You are going to install LeonOS to your computer.")
mainWindow.at(2, 2).write("===== LeonOS 安装程序 ======") print("This will overwrite any existing files in the installation directory.")
tu.coloredPrint(colors.yellow, "Are you sure? (y/n)")
-- 创建状态文本区域 local confirm = read()
statusText = window.create(mainWindow, 2, 4, w-2, 10) if confirm ~= "y" then
statusText.setTextColor(colors.white) print("Installation cancelled.")
statusText.setBackgroundColor(colors.black) return
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 end
-- 更新进度条 local ROM_DIR
local function updateProgress(current, total) tu.coloredPrint("Enter installation directory ", colors.yellow, "[",
progressBar.clear() colors.lightBlue, DEFAULT_ROM_DIR, colors.yellow, "]")
local percentage = math.floor((current / total) * 100) tu.coloredWrite(colors.yellow, "$ installer >>>")
local barWidth = w - 10
local filledWidth = math.floor((current / total) * barWidth)
progressBar.at(1, 1).write("进度: " .. percentage .. "%") ROM_DIR = read()
progressBar.at(1, 2).setBackgroundColor(colors.gray) if #ROM_DIR == 0 then ROM_DIR = DEFAULT_ROM_DIR end
progressBar.at(1, 2).write(string.rep(" ", barWidth))
progressBar.at(1, 2).setBackgroundColor(colors.green) ROM_DIR = "/"..shell.resolve(ROM_DIR)
progressBar.at(1, 2).write(string.rep(" ", filledWidth))
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 end
-- 更新状态文本 local function ok()
local function updateStatus(text) tu.coloredPrint(colors.green, "OK", colors.white)
statusText.clear() end
local lines = {}
for line in text:gmatch("[^]+") do bullet("Getting repository tree...")
table.insert(lines, line)
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 end
for i, line in ipairs(lines) do end
if i <= statusText.getSize() then ok()
statusText.at(1, i).write(line)
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 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)
local confirmButton = window.create(dialogWindow, dialogWindow.getSize() - 15, 6, 10, 2) term.at(1, pby).write((" "):rep((term.getSize())))
confirmButton.setTextColor(colors.white) term.at(okx, oky)
confirmButton.setBackgroundColor(colors.green) ok()
confirmButton.at(2, 1).write("确认")
local dirInput = defaultDir assert(io.open(
local editing = true 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 tu.coloredPrint(colors.yellow, "Your computer will restart in 3 seconds.")
local event = table.pack(os.pullEvent()) local _, y = term.getCursorPos()
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
dialogWindow.at(9, 4).write(string.rep(" ", 30)) for i=1, 3, 1 do
dialogWindow.at(9, 4).write(dirInput) progress(y, i, 5)
end os.sleep(1)
dialogWindow.setVisible(false)
return dirInput
end 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()