if not http then error("This installer does not work without http API!") end print("Please wait...") local authenticate = _G._GIT_API_KEY and {Authorization = "Bearer ".._G._GIT_API_KEY} local basaltDL = http.get("https://raw.githubusercontent.com/Kristify/kristify/main/src/libs/basalt.lua", authenticate) assert(basaltDL, "Couldn't load Basalt into memory!") local basaltFile = basaltDL.readAll() local basalt = load(basaltFile)() basaltDL.close() local errors = false -- Disk space local nBarW = 0 local nLen = 0 local nStartPos = 0 -- Basic frame construction local base = basalt.createFrame() local title = base:addLabel("_title") :setText("Kristify") :setPosition(2,2) :setFontSize(2) local page = base:addLabel("_page") :setText("1/4") :setPosition("parent.w-self.w-1",4) base:addPane() :setPosition(2,5) :setSize("parent.w-2",1) :setBackground(false, '\140', colors.gray) local content = base:addFrame("_content") :setPosition(2,6) :setSize("parent.w-2","parent.h-5") :setBackground(colors.lightGray) -- En- or disable keyboard input local blockInput = false basalt.onEvent(function(event, value) if not blockInput then return end if event == "char" or (event:find("key") and value == keys.backspace or value == keys.enter) then return false end end) local welcome,licence,destination,install,done = content:addFrame("_welcome") :setBackground(colors.lightGray):show(), content:addFrame("_licence") :setBackground(colors.lightGray):hide(), content:addFrame("_destination"):setBackground(colors.lightGray):hide(), content:addFrame("_install") :setBackground(colors.lightGray):hide(), content:addFrame("_done") :setBackground(colors.lightGray):hide() local function justinWeHaveAProblem(err) error(err) end --[[ Actuall installer ]] local nRequired = 0 local tURLs = {} tURLs.owner = "Kristify" tURLs.repo = "kristify" tURLs.branch = "main" tURLs.tree = "https://api.github.com/repos/"..tURLs.owner.."/"..tURLs.repo.."/contents/?ref="..tURLs.branch tURLs.infos = "https://api.github.com/repos/"..tURLs.owner.."/"..tURLs.repo local function httpError(response,err,errResponse) if not response then errors = true justinWeHaveAProblem("Request to GitHub denied; Reason: \'.."..err.."..\' (code "..errResponse.getResponseCode()..").") return false end return true end local function getJSON(response) if not response then return {} end local tData = response.readAll() response.close() return textutils.unserialiseJSON(tData) end local function generateTree(sURL) sURL = sURL or tURLs.tree local function convertItem(item) if item.type == "file" then return item.name, item.download_url elseif item.type == "dir" then return item.name, generateTree(item.url) end end local response,sErr,errResponse = http.get(sURL, authenticate) httpError(response,sErr,errResponse) local tData = getJSON(response) local tTree = { } for _,v in pairs(tData) do local sName,tItem = convertItem(v) -- Filter stuff that is not needed if not (sName:sub(1,1) == '.' or sName:find(".md") or sName == "installer.lua" or sName:find("basalt") or sName == "docs") then tTree[sName] = tItem end end return tTree end local function downloadBlob(sURL, sPath) local response,sErr,errResponse = http.get(sURL, authenticate) if not httpError(response,sErr,errResponse) then return false end local sData = response.readAll() response.close() local file = fs.open(sPath, 'w') file.write(sData) file.close() return true end -- Installs kristify local pathToInstall = "" local function installKristify(install) sleep(0.2) if pathToInstall == "" then pathToInstall = "/" end local status = install:getObject("_status") status:editLine(1,"Status: Create folder..") justinWeHaveAProblem = function(errMsg) status:editLine(2,"ERROR! "..errMsg) return true end fs.delete(pathToInstall) fs.makeDir(pathToInstall) status:editLine(1,"Status: Generating tree..") status:addLine("") status:addLine(" \026 "..(pathToInstall:sub(1,1) == "/" and pathToInstall:sub(2) or pathToInstall)) local tree = generateTree() local function listItems(tree,depth) local size = 0 for _,_ in pairs(tree) do size = size+1 end local index = 1 for name,item in pairs(tree) do local begin = " "..("\149 "):rep(depth-1)..(index>=size and '\141' or '\157'):rep(depth-(depth-1))..' ' status:addLine(begin..name) if type(item) == "table" then listItems(item, depth+1) end index = index+1 end end listItems(tree, 1) status:editLine(1,"Status: Download files..") local index = 3 local function nextStep(char) local line = status:getLine(index) if line then line = " "..char.." "..line:sub(4) status:editLine(index, line) end index = index+1 line = status:getLine(index) if line then line = " \026 "..line:sub(4) status:editLine(index, line) end end nextStep(' ') local function downloadItems(tree,sPath) sleep(0.3) for name,item in pairs(tree) do local nextPath = fs.combine(sPath,name) if type(item) == "table" then nextStep(' ') downloadItems(item,nextPath) else nextStep(downloadBlob(item,nextPath) and '\183' or '\019') end end end downloadItems(tree,pathToInstall) for _=1,3 do status:addLine("") end -- Next page install:addButton() :setPosition("parent.w-9","parent.h-1") :setSize(10,1) :setText("Done") :setBackground(colors.green) :setForeground(colors.white) :onClick(function() if errors then os.queueEvent("terminate") return end install:hide() title:setText("Done") page:setText(" :)") done:show() blockInput = false end) end local function addFrame(frame,nX,nY, nW) frame:addLabel() :setPosition(nX,nY) :setBackground(colors.gray) :setForeground(colors.lightGray) :setText('\159') frame:addPane() :setPosition(nX,nY) :setSize(nX+nW,1) :setBackground(colors.gray, '\143', colors.lightGray) frame:addLabel() :setPosition(nX+nW+1,nY) :setBackground(colors.lightGray) :setForeground(colors.gray) :setText('\144') frame:addLabel() :setPosition(nX,nY+1) :setBackground(colors.gray) :setForeground(colors.lightGray) :setText("\149") frame:addLabel() :setPosition(nX+nW+1,nY+1) :setBackground(colors.lightGray) :setForeground(colors.gray) :setText("\149") frame:addPane() :setPosition(nX,nY+2) :setSize(nX+nW,1) :setBackground(colors.lightGray, '\131', colors.gray) frame:addLabel() :setPosition(nX,nY+2) :setBackground(colors.lightGray) :setForeground(colors.gray) :setText('\130') frame:addLabel() :setPosition(nX+nW+1,nY+2) :setBackground(colors.lightGray) :setForeground(colors.gray) :setText('\129') end --[[ Welcome ]] welcome:addLabel() :setPosition(1,1) :setSize("parent.w-2","4") :setText("Welcome! Thank you for choosing this product! With Kristify you are able to set up a shop easily and comfortable!") welcome:addLabel() :setPosition(1,5) :setSize("parent.w-10","3") :setText("To continue, click on \"Next\". To cancel, press CTRL+T at any time.") -- Next page welcome:addButton() :setPosition("parent.w-9","parent.h-1") :setSize(10,1) :setText("Next") :setBackground(colors.green) :setForeground(colors.white) :onClick(function() welcome:hide() title:setText("Licence") page:setText("2/4") licence:show() blockInput = true end) --[[ Licence agreement ]] local textfield = licence:addTextfield() :setPosition(1,2) :setSize("parent.w","parent.h-4") :setForeground(colors.white) do local sKristifyLicence = http.get("https://raw.githubusercontent.com/Kristify/kristify/main/LICENSE") repeat local line = sKristifyLicence.readLine() if line then textfield:addLine(line) end until not line sKristifyLicence.close() end -- Checkbox frame local nW,nH = licence:getSize() addFrame(licence, 1, nH-2, 1) -- Checkbox label licence:addLabel() :setPosition(4,"parent.h-1") :setText("I accept the agreement") -- Instruction licence:addLabel() :setPosition(1,1) :setText("Scroll to read the licence agreement.") -- Checkbox local checkbox = licence:addCheckbox() :setPosition(2,"parent.h-1") :setBackground(colors.white) :setSymbol('x') -- Next page local nextBtn = licence:addButton() :setPosition("parent.w-9","parent.h-1") :setSize(10,1) :setText("Next") :setBackground(colors.gray) :setForeground(colors.black) :onClick(function() if checkbox:getValue() then licence:hide() title:setText("Destination") page:setText("3/4") destination:show() blockInput = false local note = destination:getObject("_note") justinWeHaveAProblem = function(err) note:setText(err) destination:removeObject("_next") end local response,sErr,errResponse = http.get(tURLs.infos, authenticate) httpError(response,sErr,errResponse) local tData = getJSON(response) nRequired = math.floor(tData.size-(#basaltFile/1000)/100*70) or 0 if nRequired <= 0 then destination:removeObject("_next") note:setText("Something went wrong! Reason: \'"..sErr.."\' ("..errResponse.getResponseCode()..')') end -- Required space local nRequiredLen = (nRequired*1000)/fs.getCapacity('/')*100 destination:addFrame() :setPosition(nStartPos,6) :setSize(nRequiredLen,1) :setBackground(colors.lightGray) :addPane() :setPosition(1,1) :setSize("parent.w",1) :setBackground(false, '\140', colors.lime) destination:addLabel() :setPosition(nStartPos+1,9) :setText("\024Required") if nRequiredLen+nLen >= 65 then destination:removeObject("_freeLabel") end if nRequiredLen+nLen >= 95 then destination:removeObject("_next") note:setText("Not enough space! At least ~"..nRequired.."kb must be free.") end -- Cooldown if destination:getObject("_next") then destination:addThread() :start(function() local btn = destination:getObject("_next") for i=3,1,-1 do btn:setText(tostring(i)) sleep(0.8) end btn:setText("install") :setBackground(colors.green) :setForeground(colors.white) end) end end end) -- Checkbox checkbox:onChange(function(self) if self:getValue() then nextBtn :setBackground(colors.green) :setForeground(colors.white) else nextBtn :setBackground(colors.gray) :setForeground(colors.black) end end) --[[ Destination ]] local nW = destination:getSize() destination:addLabel() :setPosition(1,1) :setText("Select the installation location:") -- Input frame addFrame(destination, 2,2, nW-4) -- Input local input = destination:addInput() :setPosition(3,3) :setSize("parent.w-4") :setBackground(colors.white) :setInputType("text") :setDefaultText("/") :setValue("/kristify") -- (from/to) destination:addLabel() :setPosition(1,6) :setText("0b") local maxMb = destination:addLabel() :setPosition("parent.w-self.w+1",6) :setText(math.floor(fs.getCapacity('/')/1000000).."mb") -- Disk Space local spaceLeft = destination:addProgressbar() :setPosition(3,6) :setSize(nW-(2+maxMb:getSize()),1) :setDirection(0) :setProgressBar(colors.lightGray, "\140", colors.green) :setBackground(colors.lightGray) :setForeground(colors.gray) :setBackgroundSymbol('\140') :setProgress( (fs.getCapacity('/')-fs.getFreeSpace('/'))/fs.getCapacity('/')*100+1 ) -- Disk space labels nBarW = spaceLeft:getSize() nLen = math.floor(spaceLeft:getProgress()-0.9) nStartPos = 3+(nBarW/100*nLen) destination:addLabel() :setPosition(nStartPos-1+(nLen<=1 and 1 or 0),5) :setForeground(colors.gray) :setText("\025Used") destination:addLabel("_freeLabel") :setPosition("parent.w-8",5) :setForeground(colors.gray) :setText("Free\025") -- Note destination:addLabel("_note") :setPosition(1,"parent.h-1") :setSize("parent.w-10",3) :setForeground(colors.red) :setText("\026Note: The choosen folder will be completely erased during install!") -- Next page destination:addButton("_next") :setPosition("parent.w-8","parent.h-1") :setSize(9,1) :setText("") :setBackground(colors.gray) :setForeground(colors.black) :onClick(function(self) if self:getBackground() ~= colors.gray then destination:hide() title:setText("Installing") page:setText("4/4") install:show() blockInput = true pathToInstall = input:getValue() -- Install script destination:addThread() :start(function() installKristify(install) end) end end) --[[ Install ]] install:addLabel() :setPosition(1,1) :setSize("parent.w",1) :setText("Take a seat and wait until the magic happened!") install:addTextfield("_status") :setPosition(1,2) :setSize("parent.w","parent.h-2") :setForeground(colors.white) :addKeywords(colors.red, {"ERROR!"}) :addRule("[\149\157\141]", colors.lightGray) :addRule("\183", colors.green) :addRule("\019", colors.red) --[[ Done ]] done:addLabel() :setPosition(1,1) :setText("And thats it! You now own a shop! ") -- Checkbox label local _,nH = done:getSize() addFrame(done, 1, nH-2, 1) done:addLabel() :setPosition(4,"parent.h-1") :setText("Enable start on boot") -- Checkbox local checkbox = done:addCheckbox() :setPosition(2,"parent.h-1") :setBackground(colors.white) :setSymbol('x') :setValue(true) -- Exit done:addButton() :setPosition("parent.w-9","parent.h-1") :setSize(10,1) :setText("Exit") :setBackground(colors.green) :setForeground(colors.white) :onClick(function() local script = "shell.openTab( \""..fs.combine(pathToInstall,"src","init.lua").."\" )" local file = "kristify.lua" if checkbox:getValue() then if fs.exists("/startup.lua") then file = fs.open("/startup.lua",'a') else fs.makeDir("startup") file = fs.open(fs.combine("startup","kristify.lua"),'w') end else file = fs.open(file, 'w') end file.write(script) file.close() basalt.stopUpdate() end) basalt.autoUpdate() return pathToInstall