diff --git a/install.lua b/install.lua index 1de709c..2c9f8e3 100644 --- a/install.lua +++ b/install.lua @@ -1,12 +1,22 @@ -local basalt = require("src") -local url = "https://raw.githubusercontent.com/Pyroxenium/Basalt2/refs/heads/main/src/" +local basalt +local releasePath = "https://raw.githubusercontent.com/Pyroxenium/Basalt2/refs/heads/main/release/basalt.lua" +local devPath = "https://raw.githubusercontent.com/Pyroxenium/Basalt2/refs/heads/main/src/" local configPath = "https://raw.githubusercontent.com/Pyroxenium/Basalt2/refs/heads/main/config.lua" +local luaLSPath = "https://raw.githubusercontent.com/Pyroxenium/Basalt2/refs/heads/main/src/LuaLS.lua" + +local basaltRequest = http.get(releasePath) +if not basaltRequest then + error("Failed to download Basalt") +end +basalt = load(basaltRequest.readAll(), "basalt", "bt", _ENV)() + local coloring = {foreground=colors.black, background=colors.white} local currentScreen = 1 local screens = {} local main = basalt.getMainFrame():setBackground(colors.black) local config +local skipConfig = true local function getConfig() if not config then @@ -25,14 +35,37 @@ end local function getChildrenHeight(container) local height = 0 for _, child in ipairs(container:getChildren()) do - local newHeight = child.get("y") + child.get("height") - if newHeight > height then - height = newHeight + if(child.get("visible"))then + local newHeight = child.get("y") + child.get("height") + if newHeight > height then + height = newHeight + end end end return height end +local function getScreenPosition(index) + if index <= 2 then + return (main:getWidth() * (index - 1)) + 2 + end + + if index == 5 then + if skipConfig then + return (main:getWidth() * 2) + 2 + end + return (main:getWidth() * 4) + 2 + end + + if index == 3 or index == 4 then + if skipConfig then + return main:getWidth() * 10 + else + return (main:getWidth() * (index - 1)) + 2 + end + end +end + local function createScreen(index) local screen = main:addFrame(coloring) :onMouseScroll(function(self, direction) @@ -43,25 +76,53 @@ local function createScreen(index) self:setOffsetY(scrollOffset) end) :setSize("{parent.width - 2}", "{parent.height - 4}") - if(index==1)then - screen:setPosition(2, 2) - else - screen:setPosition("{parent.width * "..(index-1).." + 2}", 2) - end + + screen:setPosition(function() + return getScreenPosition(index) + end, 2) + screens[index] = screen return screen end +local backButton +local nextButton + local function switchScreen(direction) local newScreen = currentScreen + direction + if screens[newScreen] then - --main:setOffsetX((newScreen - 1) * main:getWidth()) + if(skipConfig)then + if(newScreen==4)then + return + end + end main:animate():moveOffset((newScreen - 1) * main:getWidth(), 0, 0.5):start() currentScreen = newScreen end + basalt.schedule(function() + sleep(0.1) + backButton:setVisible(true) + nextButton:setVisible(true) + if(newScreen==1)then + backButton:setVisible(false) + nextButton:setVisible(true) + end + if(newScreen==5)then + nextButton:setVisible(false) + backButton:setVisible(true) + end + if(skipConfig)then + if(newScreen==3)then + nextButton:setVisible(false) + backButton:setVisible(true) + end + end + end) + end -main:addButton() +nextButton = main:addButton() :setBackground("{self.clicked and colors.black or colors.white}") :setForeground("{self.clicked and colors.white or colors.black}") :setSize(8, 1) @@ -70,7 +131,7 @@ main:addButton() :setIgnoreOffset(true) :onMouseClick(function() switchScreen(1) end) -main:addButton() +backButton = main:addButton() :setBackground("{self.clicked and colors.black or colors.white}") :setForeground("{self.clicked and colors.white or colors.black}") :setSize(8, 1) @@ -78,6 +139,7 @@ main:addButton() :setPosition(2, "{parent.height - 1}") :setIgnoreOffset(true) :onMouseClick(function() switchScreen(-1) end) + :setVisible(false) -- Screen 1: Welcome local welcomeScreen = createScreen(1) @@ -124,45 +186,70 @@ local versionDesc = installScreen:addLabel("versionDesc") :setSize("{parent.width - 4}", 3) :setBackground(colors.lightGray) -versionDropdown:onSelect(function(self, index, value) - if(value == "Release") then - versionDesc:setText("The Release version is the most stable and tested version of Basalt. It is recommended for production use.") - elseif(value == "Custom") then - versionDesc:setText("The Custom version allows you to specify which elements or plugins you want to install.") - else - versionDesc:setText("The Dev version is the latest development version of Basalt. It may contain new features and improvements, but it may also have bugs and issues.") - end -end) - installScreen:addLabel(coloring) :setText("Path:") - :setPosition(2, "{versionDesc.y + versionDesc.height + 5}") - -installScreen:addLabel(coloring) - :setText("Additional Components:") :setPosition(2, "{versionDesc.y + versionDesc.height + 1}") - local luaLSCheckbox = installScreen:addCheckbox() +local additionalComponents = installScreen:addLabel(coloring) + :setText("Additional Components:") + :setPosition(2, "{versionDesc.y + versionDesc.height + 3}") + :setVisible(false) + +local luaLSCheckbox = installScreen:addCheckbox(coloring) :setText("[ ] LLS definitions") :setCheckedText("[x] LLS definitions") - :setPosition(2, "{versionDesc.y + versionDesc.height + 2}") - :setBackground(colors.white) - :setForeground(colors.black) + :setPosition(2, "{versionDesc.y + versionDesc.height + 4}") + :setVisible(false) -local luaMinifyCheckbox = installScreen:addCheckbox() +local luaMinifyCheckbox = installScreen:addCheckbox(coloring) :setText("[ ] Minify Project") :setCheckedText("[x] Minify Project") - :setPosition(2, "{versionDesc.y + versionDesc.height + 3}") - :setBackground(colors.white) - :setForeground(colors.black) + :setPosition(2, "{versionDesc.y + versionDesc.height + 5}") + :setVisible(false) + +local singleFileProject = installScreen:addCheckbox(coloring) + :setText("[ ] Single File Project") + :setCheckedText("[x] Single File Project") + :setPosition(2, "{versionDesc.y + versionDesc.height + 6}") + :setVisible(false) local installPathInput = installScreen:addInput() - :setPosition(8, "{versionDesc.y + versionDesc.height + 5}") + :setPosition(8, "{versionDesc.y + versionDesc.height + 1}") :setPlaceholder("basalt") :setSize(12, 1) :setBackground(colors.black) :setForeground(colors.white) +versionDropdown:onSelect(function(self, index, item) + if(item.text == "Release") then + versionDesc:setText("The Release version is the most stable and tested version of Basalt. It is recommended for production use.") + additionalComponents:setVisible(false) + luaLSCheckbox:setVisible(false) + luaMinifyCheckbox:setVisible(false) + singleFileProject:setVisible(false) + elseif(item.text == "Custom") then + versionDesc:setText("The Custom version allows you to specify which elements or plugins you want to install.") + additionalComponents:setVisible(true) + luaLSCheckbox:setVisible(true) + luaMinifyCheckbox:setVisible(true) + singleFileProject:setVisible(true) + else + versionDesc:setText("The Dev version is the latest development version of Basalt. It may contain new features and improvements, but it may also have bugs and issues.") + additionalComponents:setVisible(false) + luaLSCheckbox:setVisible(false) + luaMinifyCheckbox:setVisible(false) + singleFileProject:setVisible(false) + end + + -- skipConfig setzen basierend auf Version + skipConfig = (item.text ~= "Custom") + + -- Screens neu positionieren + for i, screen in pairs(screens) do + screen:setPosition(getScreenPosition(i), 2) + end +end) + -- Screen 3: Elements local elementsScreen = createScreen(3) elementsScreen:addLabel(coloring) @@ -248,14 +335,10 @@ local function addPlugins() end addPlugins() -local function tableGet(t) - local count = 0 - for _ in pairs(t) do count = count + 1 end - return count -end - -- Screen 5 Installation Progress local progressScreen = createScreen(5) +local installButton +local currentlyInstalling = false local progressBar = progressScreen:addProgressBar() :setPosition(2, "{parent.height - 2}") :setSize("{parent.width - 12}", 2) @@ -265,78 +348,281 @@ local log = progressScreen:addList("log") :setSize("{parent.width - 2}", "{parent.height - 6}") :addItem("Starting installation...") -local function install() - local function logMessage(message) - log:addItem(message) - log:scrollToBottom() +local function logMessage(log, message) + log:addItem(message) + log:scrollToBottom() +end + +local function updateProgress(progressBar, current, total) + progressBar:setProgress(math.ceil((current / total) * 100)) +end + +local function installRelease(installPath, log, progressBar) + logMessage(log, "Installing Release version...") + + local request = http.get(releasePath) + if not request then + logMessage(log, "Failed to download release version, aborting installation.") + return end - local fileCount = tableGet(getConfig().categories.core.files) + tableGet(getConfig().categories.libraries.files) - fileCount = fileCount + tableGet(elementsList:getSelectedItems()) + tableGet(pluginList:getSelectedItems()) - progressBar:setProgress(0) - local progressStep = math.ceil(100 / fileCount) + local file = fs.open(installPath, "w") + file.write(request.readAll()) + file.close() + request.close() - local function downloadFile(url, path) - logMessage("Downloading " .. url .. "...") + progressBar:setProgress(100) + logMessage(log, "Release installation complete!") +end + +local function installDev(installPath, log, progressBar) + logMessage(log, "Installing Dev version...") + + local config = getConfig() + if not config then + logMessage(log, "Failed to fetch config") + return + end + + local function downloadFile(url, path, name, size) + logMessage(log, "Downloading " .. name..(size > 0 and " (" .. size/1000 .. " kb)" or "")) local request = http.get(url) if request then local file = fs.open(path, "w") file.write(request.readAll()) file.close() request.close() - logMessage("Downloaded " .. url .. " to " .. path) else - error("Failed to download " .. url) + error("Failed to download " .. name) end end - local path = installPathInput:getText() - if path == "" then - path = "basalt" + local totalFiles = 0 + for _, category in pairs(config.categories) do + totalFiles = totalFiles + #category.files + end + local currentFile = 0 + + for categoryName, category in pairs(config.categories) do + for fileName, fileInfo in pairs(category.files) do + downloadFile(devPath .. fileInfo.path, fs.combine(installPath, fileInfo.path), fileName, fileInfo.size or 0) + currentFile = currentFile + 1 + updateProgress(progressBar, currentFile, totalFiles) + end end - for k, v in pairs(getConfig().categories.core.files) do - logMessage("Installing core: " .. k) - downloadFile(url..v.path, fs.combine(path, v.path)) - progressBar:setProgress(progressBar:getProgress() + progressStep) - end - - for k, v in pairs(getConfig().categories.libraries.files) do - logMessage("Installing library: " .. k) - downloadFile(url..v.path, fs.combine(path, v.path)) - progressBar:setProgress(progressBar:getProgress() + progressStep) - end - - local function installElement(name, item) - logMessage("Installing element: " .. name) - downloadFile(url..item.path, fs.combine(path, item.path)) - progressBar:setProgress(progressBar:getProgress() + progressStep) - end - - local function installPlugin(name, item) - logMessage("Installing plugin: " .. name) - downloadFile(url..item.path, fs.combine(path, item.path)) - progressBar:setProgress(progressBar:getProgress() + progressStep) - end - - for k, element in ipairs(elementsList:getSelectedItems()) do - local item = element.item - installElement(item.text, item.item) - end - - for _, plugin in ipairs(pluginList:getSelectedItems()) do - local item = plugin.item - installPlugin(item.text, item.item) - end - - progressBar:setProgress(100) - logMessage("Installation complete!") + logMessage(log, "Dev installation complete!") end -local installButton = progressScreen:addButton() +local function tableGet(t) + local count = 0 + for _ in pairs(t) do count = count + 1 end + return count +end + +local function installCustom(installPath, log, progressBar, selectedElements, selectedPlugins, includeLuaLS, minify, singleFile) + logMessage(log, "Installing Custom version...") + + local config = getConfig() + if not config then + error("Failed to fetch config") + end + local min + local project = {} + + local function downloadFile(url, name, size) + logMessage(log, "Downloading " .. name..(size > 0 and " (" .. size/1000 .. " kb)" or "")) + local request = http.get(url) + if request then + local content = request.readAll() + request.close() + return content + else + error("Failed to download " .. name) + end + end + + if(minify)then + local request = http.get("https://raw.githubusercontent.com/Pyroxenium/Basalt2/refs/heads/main/tools/minify.lua") + if request then + min = load(request.readAll())() + request.close() + else + logMessage(log, "Failed to download minify.lua") + return + end + end + + local totalFiles = #selectedElements + #selectedPlugins + for _, category in pairs({"core", "libraries"}) do + totalFiles = totalFiles + tableGet(config.categories[category].files) + end + local currentFile = 0 + + for fileName, fileInfo in pairs(config.categories.core.files) do + if fileName ~= "LuaLS" or includeLuaLS then + project[fileInfo.path] = downloadFile(devPath .. fileInfo.path, fileName, fileInfo.size or 0) + currentFile = currentFile + 1 + updateProgress(progressBar, currentFile, totalFiles) + end + end + + for fileName, fileInfo in pairs(config.categories.libraries.files) do + project[fileInfo.path] = downloadFile(devPath .. fileInfo.path, fileName, fileInfo.size or 0) + currentFile = currentFile + 1 + updateProgress(progressBar, currentFile, totalFiles) + end + + for _, element in ipairs(selectedElements) do + local fileInfo = config.categories.elements.files[element.text] + basalt.LOGGER.debug(element.text) + project[fileInfo.path] = downloadFile(devPath .. fileInfo.path, element.text, fileInfo.size or 0) + currentFile = currentFile + 1 + updateProgress(progressBar, currentFile, totalFiles) + end + + for _, plugin in ipairs(selectedPlugins) do + local fileInfo = config.categories.plugins.files[plugin.text] + project[fileInfo.path] = downloadFile(devPath .. fileInfo.path, plugin.text, fileInfo.size or 0) + currentFile = currentFile + 1 + updateProgress(progressBar, currentFile, totalFiles) + end + + if minify then + logMessage(log, "Minifying project...") + for path, content in pairs(project) do + local success, minifiedContent = min(content) + if(success)then + project[path] = minifiedContent + else + logMessage(log, "Failed to minify " .. path) + return + end + end + end + + if(singleFile)then + installPath = installPath:gsub(".lua", "")..".lua" + local output = { + 'local minified = true\n', + 'local minified_elementDirectory = {}\n', + 'local minified_pluginDirectory = {}\n', + 'local project = {}\n', + 'local loadedProject = {}\n', + 'local baseRequire = require\n', + 'require = function(path) if(project[path..".lua"])then if(loadedProject[path]==nil)then loadedProject[path] = project[path..".lua"]() end return loadedProject[path] end baseRequire(path) end\n' + } + + for filePath, content in pairs(project) do + local elementName = filePath:match("^elements/(.+)%.lua$") + if elementName then + table.insert(output, string.format( + 'minified_elementDirectory["%s"] = {}\n', + elementName + )) + end + + local pluginName = filePath:match("^plugins/(.+)%.lua$") + if pluginName then + table.insert(output, string.format( + 'minified_pluginDirectory["%s"] = {}\n', + pluginName + )) + end + end + + for filePath, content in pairs(project) do + table.insert(output, string.format( + 'project["%s"] = function(...) %s end\n', + filePath, content + )) + end + table.insert(output, 'return project["main.lua"]()') + local out = fs.open(installPath, "w") + if(out)then + out.write(table.concat(output)) + out.close() + if(includeLuaLS)then + local luaLS = downloadFile(luaLSPath, "LuaLS", 0) + local luaLSDir = fs.getDir(installPath) + local file = fs.open(fs.combine(luaLSDir, "LuaLS.lua"), "w") + file.write(luaLS) + file.close() + end + else + logMessage(log, "Failed to write to " .. installPath) + return + end + else + for filePath, content in pairs(project) do + local out = fs.open(fs.combine(installPath, filePath), "w") + if(out)then + out.write(content) + out.close() + else + logMessage(log, "Failed to write to " .. fs.combine(installPath, filePath)) + return + end + end + if(includeLuaLS)then + local luaLS = downloadFile(luaLSPath, "LuaLS", 0) + local luaLSDir = fs.combine(installPath, "LuaLS.lua") + local file = fs.open(luaLSDir, "w") + file.write(luaLS) + file.close() + end + end + + logMessage(log, "Custom installation complete!") +end + +local function installBasalt() + currentlyInstalling = true + installButton:setVisible(false) + local selection = versionDropdown:getSelectedItems()[1] + if(selection==nil)then + selection = "Release" + else + selection = selection.text + end + local path = installPathInput:getText() + if(path=="")then + path = "basalt" + else + path = path:gsub(".lua", "") + end + if(selection == "Release")then + installRelease(path..".lua", log, progressBar) + elseif(selection == "Dev")then + installDev(path, log, progressBar) + else + installCustom(path, log, progressBar, elementsList:getSelectedItems(), pluginList:getSelectedItems(), luaLSCheckbox:getChecked(), luaMinifyCheckbox:getChecked(), singleFileProject:getChecked()) + end + currentlyInstalling = false + installButton:setVisible(true) +end + +installButton = progressScreen:addButton() + :setBackground("{self.clicked and colors.lightGray or colors.black}") + :setForeground("{self.clicked and colors.black or colors.lightGray}") :setText("Install") + :setPosition("{parent.width - 9}", "{parent.height - 3}") + :setSize(9, 1) + :onMouseClick(function(self) + if(currentlyInstalling)then + return + end + basalt.schedule(installBasalt) + end) + +local closeButton = progressScreen:addButton() + :setBackground("{self.clicked and colors.lightGray or colors.black}") + :setForeground("{self.clicked and colors.black or colors.lightGray}") + :setText("Close") :setPosition("{parent.width - 9}", "{parent.height - 1}") :setSize(9, 1) - :onMouseClick(install) + :onMouseClick(function(self) + basalt.stop() + end) basalt.run() \ No newline at end of file diff --git a/src/elements/Container.lua b/src/elements/Container.lua index 47554fc..b7bba48 100644 --- a/src/elements/Container.lua +++ b/src/elements/Container.lua @@ -102,6 +102,7 @@ end --- @param child table The child to check --- @return boolean boolean the child is visible function Container:isChildVisible(child) + if(child.get("visible") == false)then return false end local containerW, containerH = self.get("width"), self.get("height") local offsetX, offsetY = self.get("offsetX"), self.get("offsetY") @@ -585,6 +586,7 @@ function Container:destroy() child:destroy() end VisualElement.destroy(self) + return self end return Container \ No newline at end of file diff --git a/src/elements/Dropdown.lua b/src/elements/Dropdown.lua index a20261c..701bd20 100644 --- a/src/elements/Dropdown.lua +++ b/src/elements/Dropdown.lua @@ -106,7 +106,7 @@ function Dropdown:render() if #text == 0 then local selectedItems = self:getSelectedItems() if #selectedItems > 0 then - local selectedItem = selectedItems[1].item + local selectedItem = selectedItems[1] text = selectedItem.text or "" end end diff --git a/src/elements/List.lua b/src/elements/List.lua index 43e15ef..9214e1d 100644 --- a/src/elements/List.lua +++ b/src/elements/List.lua @@ -92,7 +92,9 @@ function List:getSelectedItems() local selected = {} for i, item in ipairs(self.get("items")) do if type(item) == "table" and item.selected then - table.insert(selected, {index = i, item = item}) + local selectedItem = item + selectedItem.index = i + table.insert(selected, selectedItem) end end return selected @@ -211,21 +213,21 @@ function List:render() local bg = item.background or self.get("background") self:textBg(1, i, string.rep(" ", width), bg) - self:textFg(1, i, separatorText, fg) + self:textFg(1, i, separatorText:sub(1, width), fg) else local text = item.text local isSelected = item.selected - local bg = isSelected and + local bg = isSelected and (item.selectedBackground or self.get("selectedBackground")) or (item.background or self.get("background")) - local fg = isSelected and + local fg = isSelected and (item.selectedForeground or self.get("selectedForeground")) or (item.foreground or self.get("foreground")) self:textBg(1, i, string.rep(" ", width), bg) - self:textFg(1, i, text, fg) + self:textFg(1, i, text:sub(1, width), fg) end end end diff --git a/src/elements/Menu.lua b/src/elements/Menu.lua index 9df56ac..a5fabfd 100644 --- a/src/elements/Menu.lua +++ b/src/elements/Menu.lua @@ -107,7 +107,6 @@ function Menu:mouse_click(button, x, y) self.get("items")[i] = item end - -- Wenn kein Multi-Selection, alle anderen deselektieren if not self.get("multiSelection") then for _, otherItem in ipairs(self.get("items")) do if type(otherItem) == "table" then @@ -116,7 +115,6 @@ function Menu:mouse_click(button, x, y) end end - -- Toggle Selection item.selected = not item.selected if item.callback then diff --git a/src/elements/VisualElement.lua b/src/elements/VisualElement.lua index 431c80e..d635fa7 100644 --- a/src/elements/VisualElement.lua +++ b/src/elements/VisualElement.lua @@ -63,6 +63,9 @@ VisualElement.defineProperty(VisualElement, "visible", {default = true, type = " self.parent.set("childrenSorted", false) self.parent.set("childrenEventsSorted", false) end + if(value==false)then + self.set("clicked", false) + end return value end}) diff --git a/src/main.lua b/src/main.lua index faf6122..dc7f6e8 100644 --- a/src/main.lua +++ b/src/main.lua @@ -169,18 +169,17 @@ local function updateEvent(event, ...) if lazyElementsEventHandler(event, ...) then return end if(mainFrame)then - if(mainFrame:dispatchEvent(event, ...))then - return - end + mainFrame:dispatchEvent(event, ...) end - for _, func in ipairs(basalt._schedule) do - if(event==func.filter)then + for k, func in ipairs(basalt._schedule) do + if(event==func.filter)or(func.filter==nil)then local ok, result = coroutine.resume(func.coroutine, event, ...) if(not ok)then errorManager.header = "Basalt Schedule Error" errorManager.error(result) end + func.filter = result end if(coroutine.status(func.coroutine)=="dead")then basalt.removeSchedule(func.coroutine) @@ -213,9 +212,9 @@ end --- @shortDescription Stops the Basalt runtime --- @usage basalt.stop() function basalt.stop() + updaterActive = false term.clear() term.setCursorPos(1,1) - updaterActive = false end --- Starts the Basalt runtime @@ -230,7 +229,9 @@ function basalt.run(isActive) renderFrames() while updaterActive do updateEvent(os.pullEventRaw()) - renderFrames() + if(updaterActive)then + renderFrames() + end end end while updaterActive do