mirror of
https://github.com/CCLeonOS/LeonOS.git
synced 2026-03-03 15:17:01 +00:00
refactor(installer): 重写安装程序为命令行界面并优化加载流程
- 移除GUI界面改为更简洁的命令行交互 - 添加模块化加载进度显示 - 简化目录选择和安装确认流程 - 优化进度条显示实现 - 保留核心安装功能但重构代码结构
This commit is contained in:
395
installer.lua
395
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 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()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user