first commit
This commit is contained in:
commit
d533cad870
12 changed files with 2726 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
u2c.py
|
||||
push.sh
|
||||
version
|
||||
6
.luarc.json
Normal file
6
.luarc.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
||||
"runtime.version": "Lua 5.2",
|
||||
"format.enable": true,
|
||||
"workspace.library": ["~/lua-ls-cc-tweaked/library"]
|
||||
}
|
||||
7
.zed/settings.json
Normal file
7
.zed/settings.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
// Folder-specific settings
|
||||
//
|
||||
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
||||
{
|
||||
"tab_size": 2
|
||||
}
|
||||
34
README.md
Normal file
34
README.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# (S)ophie (I)ncoroporated - (S)torage (S)olution
|
||||
<h1><p align=center>SISS</p></h1>
|
||||
|
||||
## How to use
|
||||
1. First, in your /config.lua file, add the following:
|
||||
```lua
|
||||
return {
|
||||
["inventories"] = { -- REQUIRED!! Please set at least one inventory pattern.
|
||||
".*barrel.*" -- Lua patterns to match all barrels
|
||||
},
|
||||
["import"] = { -- Not required, just don't set!
|
||||
"ender_storage_156" -- A inventory I want to move all items from into our storage
|
||||
},
|
||||
["chatbox"] = { -- Not required, just don't set!
|
||||
["hartbreix"] = "manipulator_42" -- Chatbox support
|
||||
}
|
||||
}
|
||||
```
|
||||
2. Run the following command:
|
||||
```
|
||||
wget run https://git.sad.ovh/sophie/storage-solution/raw/branch/main/src/.main-installer.lua
|
||||
```
|
||||
|
||||
This will install storage-solution to /storage-solution.
|
||||
To make it start every time you turn on the computer, add
|
||||
|
||||
```
|
||||
shell.run("wget run https://git.sad.ovh/sophie/storage-solution/raw/branch/main/src/.main-installer.lua")
|
||||
```
|
||||
to startup.lua
|
||||
|
||||
### [EXTRA!]
|
||||
|
||||
You can also run `wget run https://files.sad.ovh/public/storage-solution/.beta-installer.lua`, which is quite unstable. You may experience issues while running the beta version, but also new juicy features :)
|
||||
81
src/.beta-installer.lua
Normal file
81
src/.beta-installer.lua
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
local base_url = "https://files.sad.ovh/public/storage-solution"
|
||||
local api_url = base_url .. "?ls"
|
||||
local download_root = "storage-solution"
|
||||
|
||||
local function fetch_folder_list()
|
||||
local response = http.get(api_url)
|
||||
if not response then
|
||||
error("Failed to get folder list from Copyparty API")
|
||||
end
|
||||
local body = response.readAll()
|
||||
response.close()
|
||||
local data = textutils.unserializeJSON(body)
|
||||
return data
|
||||
end
|
||||
|
||||
local function download_file(path)
|
||||
local file_url = base_url .. "/" .. path
|
||||
local local_path = download_root .. "/" .. path
|
||||
local response = http.get(file_url)
|
||||
if response then
|
||||
local file = fs.open(local_path, "wb")
|
||||
if file then
|
||||
file.write(response.readAll())
|
||||
file.close()
|
||||
end
|
||||
response.close()
|
||||
else
|
||||
print("Failed to download: " .. file_url)
|
||||
end
|
||||
end
|
||||
|
||||
local function traverse_and_download(folder_data, prefix)
|
||||
prefix = prefix or ""
|
||||
|
||||
for _, file in ipairs(folder_data.files or {}) do
|
||||
print("Downloading: " .. prefix .. file.href)
|
||||
download_file(prefix .. file.href)
|
||||
end
|
||||
|
||||
for _, dir in ipairs(folder_data.dirs or {}) do
|
||||
fs.makeDir(download_root .. "/" .. prefix .. dir.href)
|
||||
local subdir_url = base_url .. "/" .. dir.href .. "?ls"
|
||||
local response = http.get(subdir_url)
|
||||
if response then
|
||||
local body = response.readAll()
|
||||
response.close()
|
||||
local subdir_data = textutils.unserializeJSON(body)
|
||||
traverse_and_download(subdir_data, prefix .. dir.href)
|
||||
else
|
||||
print("Failed to get subdirectory: " .. subdir_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if fs.exists(download_root) then
|
||||
if fs.exists(download_root .. "/version") then
|
||||
local previousVersion = fs.open(download_root .. "/version", "r").readAll()
|
||||
local currentVersion = http.get(base_url .. "/version").readAll();
|
||||
|
||||
if previousVersion == currentVersion then
|
||||
print("Previous version " .. previousVersion .. " is already installed, we're on " .. currentVersion .. " aswell, so skipping installation.")
|
||||
shell.run("storage-solution/main.lua")
|
||||
return
|
||||
else
|
||||
print("Version " .. previousVersion .. " was already installed. Uninstalling.")
|
||||
fs.delete(download_root)
|
||||
end
|
||||
else
|
||||
print("Version marker does not exist. Cannot install.")
|
||||
shell.run("storage-solution/main.lua")
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
fs.makeDir(download_root)
|
||||
|
||||
local folder_list = fetch_folder_list()
|
||||
traverse_and_download(folder_list, "")
|
||||
|
||||
print("Done :), installed Sophie's Storage Solution version " .. (http.get(base_url .. "/version").readAll()))
|
||||
shell.run("storage-solution/main.lua")
|
||||
101
src/.main-installer.lua
Normal file
101
src/.main-installer.lua
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
-- Forgejo-based installer for Sophie's Storage Solution
|
||||
local repo_api = "https://git.sad.ovh/api/v1/repos/sophie/storage-solution"
|
||||
local raw_base = "https://git.sad.ovh/sophie/storage-solution/raw/branch/main"
|
||||
local branch = "main"
|
||||
local download_root = "storage-solution"
|
||||
|
||||
-- Get latest commit hash for branch
|
||||
local function fetch_commit_hash()
|
||||
local url = repo_api .. "/branches/" .. branch
|
||||
local response = http.get(url)
|
||||
if not response then
|
||||
error("Failed to fetch branch info: " .. url)
|
||||
end
|
||||
local body = response.readAll()
|
||||
response.close()
|
||||
local data = textutils.unserializeJSON(body)
|
||||
return data.commit.id
|
||||
end
|
||||
|
||||
-- Fetch file/folder listing from Forgejo API
|
||||
local function fetch_repo_tree(path)
|
||||
local url = repo_api .. "/contents" .. (path and ("/" .. path) or "")
|
||||
local response = http.get(url)
|
||||
if not response then
|
||||
error("Failed to fetch repo tree: " .. url)
|
||||
end
|
||||
local body = response.readAll()
|
||||
response.close()
|
||||
return textutils.unserializeJSON(body)
|
||||
end
|
||||
|
||||
-- Download a single file
|
||||
local function download_file(path)
|
||||
local url = raw_base .. "/" .. path
|
||||
local local_path = download_root .. "/" .. path
|
||||
fs.makeDir(fs.getDir(local_path))
|
||||
local response = http.get(url)
|
||||
if response then
|
||||
local file = fs.open(local_path, "wb")
|
||||
if file then
|
||||
file.write(response.readAll())
|
||||
file.close()
|
||||
end
|
||||
response.close()
|
||||
print("Downloaded: " .. path)
|
||||
else
|
||||
print("Failed to download: " .. url)
|
||||
end
|
||||
end
|
||||
|
||||
-- Recursively traverse Forgejo folders
|
||||
local function traverse_and_download(path)
|
||||
local tree = fetch_repo_tree(path)
|
||||
for _, entry in ipairs(tree) do
|
||||
if entry.type == "file" then
|
||||
download_file(entry.path)
|
||||
elseif entry.type == "dir" then
|
||||
traverse_and_download(entry.path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Get current remote commit hash
|
||||
local remote_hash = fetch_commit_hash()
|
||||
print("Latest commit: " .. remote_hash)
|
||||
|
||||
-- Check for existing version
|
||||
if fs.exists(download_root) then
|
||||
if fs.exists(download_root .. "/version") then
|
||||
local f = fs.open(download_root .. "/version", "r")
|
||||
local local_hash = f.readAll()
|
||||
f.close()
|
||||
|
||||
if local_hash == remote_hash then
|
||||
print("Already up to date (commit " .. remote_hash:sub(1, 7) .. ").")
|
||||
shell.run(download_root .. "/main.lua")
|
||||
return
|
||||
else
|
||||
print("Outdated (" .. local_hash:sub(1, 7) .. " -> " .. remote_hash:sub(1, 7) .. "), reinstalling...")
|
||||
fs.delete(download_root)
|
||||
end
|
||||
else
|
||||
print("Version marker missing, reinstalling.")
|
||||
fs.delete(download_root)
|
||||
end
|
||||
end
|
||||
|
||||
-- Create root directory
|
||||
fs.makeDir(download_root)
|
||||
|
||||
-- Download everything
|
||||
print("Fetching repository tree from Forgejo...")
|
||||
traverse_and_download("")
|
||||
|
||||
-- Save commit hash as version
|
||||
local f = fs.open(download_root .. "/version", "w")
|
||||
f.write(remote_hash)
|
||||
f.close()
|
||||
|
||||
print("Installed Sophie's Storage Solution (commit " .. remote_hash:sub(1, 7) .. ")")
|
||||
shell.run(download_root .. "/main.lua")
|
||||
1504
src/lib/abstractInventoryLib.lua
Normal file
1504
src/lib/abstractInventoryLib.lua
Normal file
File diff suppressed because it is too large
Load diff
466
src/lib/primeui.lua
Normal file
466
src/lib/primeui.lua
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
-- PrimeUI by JackMacWindows
|
||||
-- Public domain/CC0
|
||||
|
||||
local expect = require "cc.expect".expect
|
||||
|
||||
-- Initialization code
|
||||
local PrimeUI = {}
|
||||
do
|
||||
local coros = {}
|
||||
local restoreCursor
|
||||
|
||||
--- Adds a task to run in the main loop.
|
||||
---@param func function The function to run, usually an `os.pullEvent` loop
|
||||
function PrimeUI.addTask(func)
|
||||
expect(1, func, "function")
|
||||
local t = {coro = coroutine.create(func)}
|
||||
coros[#coros+1] = t
|
||||
_, t.filter = coroutine.resume(t.coro)
|
||||
end
|
||||
|
||||
--- Sends the provided arguments to the run loop, where they will be returned.
|
||||
---@param ... any The parameters to send
|
||||
function PrimeUI.resolve(...)
|
||||
coroutine.yield(coros, ...)
|
||||
end
|
||||
|
||||
--- Clears the screen and resets all components. Do not use any previously
|
||||
--- created components after calling this function.
|
||||
function PrimeUI.clear()
|
||||
-- Reset the screen.
|
||||
term.setCursorPos(1, 1)
|
||||
term.setCursorBlink(false)
|
||||
term.setBackgroundColor(colors.black)
|
||||
term.setTextColor(colors.white)
|
||||
term.clear()
|
||||
-- Reset the task list and cursor restore function.
|
||||
coros = {}
|
||||
restoreCursor = nil
|
||||
end
|
||||
|
||||
--- Sets or clears the window that holds where the cursor should be.
|
||||
---@param win window|nil The window to set as the active window
|
||||
function PrimeUI.setCursorWindow(win)
|
||||
expect(1, win, "table", "nil")
|
||||
restoreCursor = win and win.restoreCursor
|
||||
end
|
||||
|
||||
--- Gets the absolute position of a coordinate relative to a window.
|
||||
---@param win window The window to check
|
||||
---@param x number The relative X position of the point
|
||||
---@param y number The relative Y position of the point
|
||||
---@return number x The absolute X position of the window
|
||||
---@return number y The absolute Y position of the window
|
||||
function PrimeUI.getWindowPos(win, x, y)
|
||||
if win == term then return x, y end
|
||||
while win ~= term.native() and win ~= term.current() do
|
||||
if not win.getPosition then return x, y end
|
||||
local wx, wy = win.getPosition()
|
||||
x, y = x + wx - 1, y + wy - 1
|
||||
_, win = debug.getupvalue(select(2, debug.getupvalue(win.isColor, 1)), 1) -- gets the parent window through an upvalue
|
||||
end
|
||||
return x, y
|
||||
end
|
||||
|
||||
--- Runs the main loop, returning information on an action.
|
||||
---@return any ... The result of the coroutine that exited
|
||||
function PrimeUI.run()
|
||||
while true do
|
||||
-- Restore the cursor and wait for the next event.
|
||||
if restoreCursor then restoreCursor() end
|
||||
local ev = table.pack(os.pullEvent())
|
||||
-- Run all coroutines.
|
||||
for _, v in ipairs(coros) do
|
||||
if v.filter == nil or v.filter == ev[1] then
|
||||
-- Resume the coroutine, passing the current event.
|
||||
local res = table.pack(coroutine.resume(v.coro, table.unpack(ev, 1, ev.n)))
|
||||
-- If the call failed, bail out. Coroutines should never exit.
|
||||
if not res[1] then error(res[2], 2) end
|
||||
-- If the coroutine resolved, return its values.
|
||||
if res[2] == coros then return table.unpack(res, 3, res.n) end
|
||||
-- Set the next event filter.
|
||||
v.filter = res[2]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Creates a text input box.
|
||||
---@param win window The window to draw on
|
||||
---@param x number The X position of the left side of the box
|
||||
---@param y number The Y position of the box
|
||||
---@param width number The width/length of the box
|
||||
---@param action function|string A function or `run` event to call when a key is pressed
|
||||
---@param fgColor color|nil The color of the text (defaults to white)
|
||||
---@param bgColor color|nil The color of the background (defaults to black)
|
||||
---@param replacement string|nil A character to replace typed characters with
|
||||
---@param history string[]|nil A list of previous entries to provide
|
||||
---@param completion function|nil A function to call to provide completion
|
||||
function PrimeUI.inputBox(win, x, y, width, action, fgColor, bgColor, replacement, history, completion, default)
|
||||
expect(1, win, "table")
|
||||
expect(2, x, "number")
|
||||
expect(3, y, "number")
|
||||
expect(4, width, "number")
|
||||
expect(5, action, "function", "string")
|
||||
fgColor = expect(6, fgColor, "number", "nil") or colors.white
|
||||
bgColor = expect(7, bgColor, "number", "nil") or colors.black
|
||||
expect(8, replacement, "string", "nil")
|
||||
expect(9, history, "table", "nil")
|
||||
expect(10, completion, "function", "nil")
|
||||
expect(11, default, "string", "nil")
|
||||
|
||||
local box = window.create(win, x, y, width, 1)
|
||||
box.setTextColor(fgColor)
|
||||
box.setBackgroundColor(bgColor)
|
||||
box.clear()
|
||||
|
||||
PrimeUI.addTask(function()
|
||||
local text = default or ""
|
||||
local cursor = #text + 1
|
||||
local histIndex = nil
|
||||
|
||||
local function runAction()
|
||||
if type(action) == "string" then
|
||||
PrimeUI.resolve("inputBox", action, text)
|
||||
else
|
||||
action(text)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function redraw()
|
||||
box.clear()
|
||||
box.setCursorPos(1, 1)
|
||||
if replacement then
|
||||
box.write(string.rep(replacement, #text))
|
||||
else
|
||||
box.write(text)
|
||||
end
|
||||
box.setCursorPos(cursor, 1)
|
||||
|
||||
runAction()
|
||||
end
|
||||
|
||||
redraw()
|
||||
|
||||
while true do
|
||||
local ev, p1 = os.pullEvent()
|
||||
if ev == "char" then
|
||||
text = text:sub(1, cursor - 1) .. p1 .. text:sub(cursor)
|
||||
cursor = cursor + 1
|
||||
redraw()
|
||||
elseif ev == "key" then
|
||||
if p1 == keys.enter then
|
||||
runAction()
|
||||
elseif p1 == keys.left then
|
||||
cursor = math.max(1, cursor - 1)
|
||||
box.setCursorPos(cursor, 1)
|
||||
elseif p1 == keys.right then
|
||||
cursor = math.min(#text + 1, cursor + 1)
|
||||
box.setCursorPos(cursor, 1)
|
||||
elseif p1 == keys.backspace then
|
||||
if cursor > 1 then
|
||||
text = text:sub(1, cursor - 2) .. text:sub(cursor)
|
||||
cursor = cursor - 1
|
||||
redraw()
|
||||
end
|
||||
elseif p1 == keys.delete then
|
||||
text = text:sub(1, cursor - 1) .. text:sub(cursor + 1)
|
||||
redraw()
|
||||
elseif p1 == keys.up and history then
|
||||
if not histIndex then histIndex = #history + 1 end
|
||||
histIndex = math.max(1, histIndex - 1)
|
||||
text = history[histIndex] or ""
|
||||
cursor = #text + 1
|
||||
redraw()
|
||||
elseif p1 == keys.down and history then
|
||||
if histIndex then
|
||||
histIndex = math.min(#history + 1, histIndex + 1)
|
||||
text = history[histIndex] or ""
|
||||
cursor = #text + 1
|
||||
redraw()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- Draws a line of text at a position.
|
||||
---@param win window The window to draw on
|
||||
---@param x number The X position of the left side of the text
|
||||
---@param y number The Y position of the text
|
||||
---@param text string The text to draw
|
||||
---@param fgColor color|nil The color of the text (defaults to white)
|
||||
---@param bgColor color|nil The color of the background (defaults to black)
|
||||
function PrimeUI.label(win, x, y, text, fgColor, bgColor)
|
||||
expect(1, win, "table")
|
||||
expect(2, x, "number")
|
||||
expect(3, y, "number")
|
||||
expect(4, text, "string")
|
||||
fgColor = expect(5, fgColor, "number", "nil") or colors.white
|
||||
bgColor = expect(6, bgColor, "number", "nil") or colors.black
|
||||
win.setCursorPos(x, y)
|
||||
win.setTextColor(fgColor)
|
||||
win.setBackgroundColor(bgColor)
|
||||
win.write(text)
|
||||
end
|
||||
|
||||
--- Creates a scrollable window, which allows drawing large content in a small area.
|
||||
---@param win window The parent window of the scroll box
|
||||
---@param x number The X position of the box
|
||||
---@param y number The Y position of the box
|
||||
---@param width number The width of the box
|
||||
---@param height number The height of the outer box
|
||||
---@param innerHeight number The height of the inner scroll area
|
||||
---@param allowArrowKeys boolean|nil Whether to allow arrow keys to scroll the box (defaults to true)
|
||||
---@param showScrollIndicators boolean|nil Whether to show arrow indicators on the right side when scrolling is available, which reduces the inner width by 1 (defaults to false)
|
||||
---@param fgColor number|nil The color of scroll indicators (defaults to white)
|
||||
---@param bgColor color|nil The color of the background (defaults to black)
|
||||
---@return window inner The inner window to draw inside
|
||||
---@return fun(pos:number) scroll A function to manually set the scroll position of the window
|
||||
function PrimeUI.scrollBox(win, x, y, width, height, innerHeight, allowArrowKeys, showScrollIndicators, fgColor, bgColor)
|
||||
expect(1, win, "table")
|
||||
expect(2, x, "number")
|
||||
expect(3, y, "number")
|
||||
expect(4, width, "number")
|
||||
expect(5, height, "number")
|
||||
expect(6, innerHeight, "number")
|
||||
expect(7, allowArrowKeys, "boolean", "nil")
|
||||
expect(8, showScrollIndicators, "boolean", "nil")
|
||||
fgColor = expect(9, fgColor, "number", "nil") or colors.white
|
||||
bgColor = expect(10, bgColor, "number", "nil") or colors.black
|
||||
if allowArrowKeys == nil then allowArrowKeys = true end
|
||||
-- Create the outer container box.
|
||||
local outer = window.create(win == term and term.current() or win, x, y, width, height)
|
||||
outer.setBackgroundColor(bgColor)
|
||||
outer.clear()
|
||||
-- Create the inner scrolling box.
|
||||
local inner = window.create(outer, 1, 1, width - (showScrollIndicators and 1 or 0), innerHeight)
|
||||
inner.setBackgroundColor(bgColor)
|
||||
inner.clear()
|
||||
-- Draw scroll indicators if desired.
|
||||
if showScrollIndicators then
|
||||
outer.setBackgroundColor(bgColor)
|
||||
outer.setTextColor(fgColor)
|
||||
outer.setCursorPos(width, height)
|
||||
outer.write(innerHeight > height and "\31" or " ")
|
||||
end
|
||||
-- Get the absolute position of the window.
|
||||
x, y = PrimeUI.getWindowPos(win, x, y)
|
||||
-- Add the scroll handler.
|
||||
local scrollPos = 1
|
||||
PrimeUI.addTask(function()
|
||||
while true do
|
||||
-- Wait for next event.
|
||||
local ev = table.pack(os.pullEvent())
|
||||
-- Update inner height in case it changed.
|
||||
innerHeight = select(2, inner.getSize())
|
||||
-- Check for scroll events and set direction.
|
||||
local dir
|
||||
if ev[1] == "key" and allowArrowKeys then
|
||||
if ev[2] == keys.up then dir = -1
|
||||
elseif ev[2] == keys.down then dir = 1 end
|
||||
elseif ev[1] == "mouse_scroll" and ev[3] >= x and ev[3] < x + width and ev[4] >= y and ev[4] < y + height then
|
||||
dir = ev[2]
|
||||
end
|
||||
-- If there's a scroll event, move the window vertically.
|
||||
if dir and (scrollPos + dir >= 1 and scrollPos + dir <= innerHeight - height) then
|
||||
scrollPos = scrollPos + dir
|
||||
inner.reposition(1, 2 - scrollPos)
|
||||
end
|
||||
-- Redraw scroll indicators if desired.
|
||||
if showScrollIndicators then
|
||||
outer.setBackgroundColor(bgColor)
|
||||
outer.setTextColor(fgColor)
|
||||
outer.setCursorPos(width, 1)
|
||||
outer.write(scrollPos > 1 and "\30" or " ")
|
||||
outer.setCursorPos(width, height)
|
||||
outer.write(scrollPos < innerHeight - height and "\31" or " ")
|
||||
end
|
||||
end
|
||||
end)
|
||||
-- Make a function to allow external scrolling.
|
||||
local function scroll(pos)
|
||||
expect(1, pos, "number")
|
||||
pos = math.floor(pos)
|
||||
expect.range(pos, 1, innerHeight - height)
|
||||
-- Scroll the window.
|
||||
scrollPos = pos
|
||||
inner.reposition(1, 2 - scrollPos)
|
||||
-- Redraw scroll indicators if desired.
|
||||
if showScrollIndicators then
|
||||
outer.setBackgroundColor(bgColor)
|
||||
outer.setTextColor(fgColor)
|
||||
outer.setCursorPos(width, 1)
|
||||
outer.write(scrollPos > 1 and "\30" or " ")
|
||||
outer.setCursorPos(width, height)
|
||||
outer.write(scrollPos < innerHeight - height and "\31" or " ")
|
||||
end
|
||||
end
|
||||
return inner, scroll
|
||||
end
|
||||
|
||||
|
||||
--- Creates a list of entries that can each be selected.
|
||||
---@param win window The window to draw on
|
||||
---@param x number The X coordinate of the inside of the box
|
||||
---@param y number The Y coordinate of the inside of the box
|
||||
---@param width number The width of the inner box
|
||||
---@param height number The height of the inner box
|
||||
---@param entries function A function that returns a list of entries to show, where the value is whether the item is pre-selected (or `"R"` for required/forced selected)
|
||||
---@param action function|string A function or `run` event that's called when a selection is made
|
||||
---@param selectChangeAction function|string|nil A function or `run` event that's called when the current selection is changed
|
||||
---@param fgColor color|nil The color of the text (defaults to white)
|
||||
---@param bgColor color|nil The color of the background (defaults to black)
|
||||
function PrimeUI.selectionBox(win, x, y, width, height, entries, action, selectChangeAction, fgColor, bgColor)
|
||||
expect(1, win, "table")
|
||||
expect(2, x, "number")
|
||||
expect(3, y, "number")
|
||||
expect(4, width, "number")
|
||||
expect(5, height, "number")
|
||||
expect(6, entries, "function")
|
||||
expect(7, action, "function", "string")
|
||||
expect(8, selectChangeAction, "function", "string", "nil")
|
||||
fgColor = expect(9, fgColor, "number", "nil") or colors.white
|
||||
bgColor = expect(10, bgColor, "number", "nil") or colors.black
|
||||
|
||||
local entrywin = window.create(win, x, y, width, height)
|
||||
local selection, scroll = 1, 1
|
||||
-- Create a function to redraw the entries on screen.
|
||||
local function drawEntries()
|
||||
-- Clear and set invisible for performance.
|
||||
entrywin.setVisible(false)
|
||||
entrywin.setBackgroundColor(bgColor)
|
||||
entrywin.clear()
|
||||
-- Draw each entry in the scrolled region.
|
||||
for i = scroll, scroll + height - 1 do
|
||||
-- Get the entry; stop if there's no more.
|
||||
local e = entries()[i]
|
||||
if not e then break end
|
||||
-- Set the colors: invert if selected.
|
||||
entrywin.setCursorPos(2, i - scroll + 1)
|
||||
if i == selection then
|
||||
entrywin.setBackgroundColor(fgColor)
|
||||
entrywin.setTextColor(bgColor)
|
||||
else
|
||||
entrywin.setBackgroundColor(bgColor)
|
||||
entrywin.setTextColor(fgColor)
|
||||
end
|
||||
-- Draw the selection.
|
||||
entrywin.clearLine()
|
||||
entrywin.write(#e > width - 1 and e:sub(1, width - 4) .. "..." or e)
|
||||
end
|
||||
-- Draw scroll arrows.
|
||||
entrywin.setBackgroundColor(bgColor)
|
||||
entrywin.setTextColor(fgColor)
|
||||
entrywin.setCursorPos(width, 1)
|
||||
entrywin.write("\30")
|
||||
entrywin.setCursorPos(width, height)
|
||||
entrywin.write("\31")
|
||||
-- Send updates to the screen.
|
||||
entrywin.setVisible(true)
|
||||
end
|
||||
-- Draw first screen.
|
||||
drawEntries()
|
||||
-- Add a task for selection keys.
|
||||
PrimeUI.addTask(function()
|
||||
while true do
|
||||
local event, key, cx, cy = os.pullEvent()
|
||||
if event == "key" then
|
||||
if key == keys.down and selection < #entries() then
|
||||
-- Move selection down.
|
||||
selection = selection + 1
|
||||
if selection > scroll + height - 1 then scroll = scroll + 1 end
|
||||
-- Send action if necessary.
|
||||
if type(selectChangeAction) == "string" then PrimeUI.resolve("selectionBox", selectChangeAction, selection)
|
||||
elseif selectChangeAction then selectChangeAction(selection) end
|
||||
-- Redraw screen.
|
||||
drawEntries()
|
||||
elseif key == keys.up and selection > 1 then
|
||||
-- Move selection up.
|
||||
selection = selection - 1
|
||||
if selection < scroll then scroll = scroll - 1 end
|
||||
-- Send action if necessary.
|
||||
if type(selectChangeAction) == "string" then PrimeUI.resolve("selectionBox", selectChangeAction, selection)
|
||||
elseif selectChangeAction then selectChangeAction(selection) end
|
||||
-- Redraw screen.
|
||||
drawEntries()
|
||||
elseif key == keys.enter then
|
||||
-- Select the entry: send the action.
|
||||
if type(action) == "string" then PrimeUI.resolve("selectionBox", action, entries()[selection])
|
||||
else action(entries()[selection]) end
|
||||
end
|
||||
elseif event == "mouse_click" and key == 1 then
|
||||
-- Handle clicking the scroll arrows.
|
||||
local wx, wy = PrimeUI.getWindowPos(entrywin, 1, 1)
|
||||
if cx == wx + width - 1 then
|
||||
if cy == wy and selection > 1 then
|
||||
-- Move selection up.
|
||||
selection = selection - 1
|
||||
if selection < scroll then scroll = scroll - 1 end
|
||||
-- Send action if necessary.
|
||||
if type(selectChangeAction) == "string" then PrimeUI.resolve("selectionBox", selectChangeAction, selection)
|
||||
elseif selectChangeAction then selectChangeAction(selection) end
|
||||
-- Redraw screen.
|
||||
drawEntries()
|
||||
elseif cy == wy + height - 1 and selection < #entries() then
|
||||
-- Move selection down.
|
||||
selection = selection + 1
|
||||
if selection > scroll + height - 1 then scroll = scroll + 1 end
|
||||
-- Send action if necessary.
|
||||
if type(selectChangeAction) == "string" then PrimeUI.resolve("selectionBox", selectChangeAction, selection)
|
||||
elseif selectChangeAction then selectChangeAction(selection) end
|
||||
-- Redraw screen.
|
||||
drawEntries()
|
||||
end
|
||||
elseif cx >= wx and cx < wx + width - 1 and cy >= wy and cy < wy + height then
|
||||
local sel = scroll + (cy - wy)
|
||||
if sel == selection then
|
||||
-- Select the entry: send the action.
|
||||
if type(action) == "string" then PrimeUI.resolve("selectionBox", action, entries()[selection])
|
||||
else action(entries()[selection]) end
|
||||
else
|
||||
selection = sel
|
||||
-- Send action if necessary.
|
||||
if type(selectChangeAction) == "string" then PrimeUI.resolve("selectionBox", selectChangeAction, selection)
|
||||
elseif selectChangeAction then selectChangeAction(selection) end
|
||||
-- Redraw screen.
|
||||
drawEntries()
|
||||
end
|
||||
end
|
||||
elseif event == "mouse_scroll" then
|
||||
-- Handle mouse scrolling.
|
||||
local wx, wy = PrimeUI.getWindowPos(entrywin, 1, 1)
|
||||
if cx >= wx and cx < wx + width and cy >= wy and cy < wy + height then
|
||||
if key < 0 and selection > 1 then
|
||||
-- Move selection up.
|
||||
selection = selection - 1
|
||||
if selection < scroll then scroll = scroll - 1 end
|
||||
-- Send action if necessary.
|
||||
if type(selectChangeAction) == "string" then PrimeUI.resolve("selectionBox", selectChangeAction, selection)
|
||||
elseif selectChangeAction then selectChangeAction(selection) end
|
||||
-- Redraw screen.
|
||||
drawEntries()
|
||||
elseif key > 0 and selection < #entries() then
|
||||
-- Move selection down.
|
||||
selection = selection + 1
|
||||
if selection > scroll + height - 1 then scroll = scroll + 1 end
|
||||
-- Send action if necessary.
|
||||
if type(selectChangeAction) == "string" then PrimeUI.resolve("selectionBox", selectChangeAction, selection)
|
||||
elseif selectChangeAction then selectChangeAction(selection) end
|
||||
-- Redraw screen.
|
||||
drawEntries()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
return drawEntries
|
||||
end
|
||||
|
||||
|
||||
return {
|
||||
PrimeUI = PrimeUI
|
||||
}
|
||||
42
src/main.lua
Normal file
42
src/main.lua
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
---@class Config
|
||||
---@field inventories string[]
|
||||
---@field import string[]|nil
|
||||
---@field chatbox table<string, string>|nil
|
||||
|
||||
local inv = require("modules.inv")
|
||||
local ui = require("modules.ui")
|
||||
local chatbox = require("modules.chatbox")
|
||||
|
||||
local config = require("../config") ---@type Config
|
||||
|
||||
local function importMechanism()
|
||||
if config.import == nil then
|
||||
return
|
||||
end
|
||||
if #config.import == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
while true do
|
||||
for _, import_from_inv in ipairs(config.import) do
|
||||
---@type ccTweaked.peripheral.Inventory
|
||||
local perip = peripheral.wrap(import_from_inv)
|
||||
if perip and perip.size then
|
||||
local slotsToSend = {}
|
||||
for slot = 1, perip.size() do
|
||||
local item = perip.getItemDetail(slot)
|
||||
if item then
|
||||
table.insert(slotsToSend, slot)
|
||||
end
|
||||
end
|
||||
if #slotsToSend > 0 then
|
||||
inv.sendItemAwayMultiple(slotsToSend, perip, import_from_inv)
|
||||
end
|
||||
end
|
||||
end
|
||||
sleep(0.1)
|
||||
end
|
||||
end
|
||||
|
||||
inv.sync()
|
||||
parallel.waitForAll(chatbox.run, inv.getAIL().run, importMechanism, ui.runUi, inv.detectPlayerInsert)
|
||||
212
src/modules/chatbox.lua
Normal file
212
src/modules/chatbox.lua
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
local config = require("../../config") ---@type Config
|
||||
local inv = require("modules.inv")
|
||||
|
||||
local function levDist(s, t)
|
||||
local n = #s
|
||||
local m = #t
|
||||
|
||||
if n == 0 then return m end
|
||||
if m == 0 then return n end
|
||||
|
||||
-- create matrix
|
||||
local d = {}
|
||||
for i = 0, n do
|
||||
d[i] = {}
|
||||
end
|
||||
|
||||
-- initialize
|
||||
for i = 0, n do d[i][0] = i end
|
||||
for j = 0, m do d[0][j] = j end
|
||||
|
||||
-- main loop
|
||||
for i = 1, n do
|
||||
local s_i = s:sub(i, i)
|
||||
|
||||
for j = 1, m do
|
||||
-- safeguard shortcut
|
||||
if i == j and d[i][j] and d[i][j] > 4 then
|
||||
return n
|
||||
end
|
||||
|
||||
local t_j = t:sub(j, j)
|
||||
local cost = (s_i == t_j) and 0 or 1
|
||||
|
||||
local mi = math.min(
|
||||
d[i - 1][j] + 1,
|
||||
d[i][j - 1] + 1,
|
||||
d[i - 1][j - 1] + cost
|
||||
)
|
||||
|
||||
d[i][j] = mi
|
||||
|
||||
-- Damerau transposition
|
||||
if i > 1 and j > 1 and s_i == t:sub(j - 1, j - 1) and s:sub(i - 1, i - 1) == t_j then
|
||||
d[i][j] = math.min(d[i][j], d[i - 2][j - 2] + cost)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return d[n][m]
|
||||
end
|
||||
|
||||
local function findBest(data, item)
|
||||
|
||||
local sorted_data = {}
|
||||
|
||||
for _, z in ipairs(data) do
|
||||
local parts = {}
|
||||
for part in string.gmatch(z, "([^:]+)") do
|
||||
table.insert(parts, part)
|
||||
end
|
||||
|
||||
local key = parts[1] .. ":" .. (parts[2] or "")
|
||||
local dist = levDist(item, parts[2] or "")
|
||||
table.insert(sorted_data, { key, dist })
|
||||
end
|
||||
|
||||
table.sort(sorted_data, function(a, b)
|
||||
return a[2] < b[2]
|
||||
end)
|
||||
|
||||
local best = {}
|
||||
local best_dist = sorted_data[1][2]
|
||||
for _, z in ipairs(sorted_data) do
|
||||
if z[2] == best_dist then
|
||||
table.insert(best, z)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return best
|
||||
end
|
||||
|
||||
local BOT_NAME = "&cS &eI&an&3c &5S&cI&6S"
|
||||
|
||||
function auth(user)
|
||||
local manip = config.chatbox[user]
|
||||
if manip then
|
||||
return true
|
||||
else
|
||||
chatbox.tell(user, "You are not authorized to use this command.", BOT_NAME)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function run()
|
||||
if config.chatbox == nil then return end
|
||||
if #config.chatbox == 0 then return end
|
||||
|
||||
while true do
|
||||
local _, user, command, args = os.pullEvent("command")
|
||||
|
||||
if command == "sis" then
|
||||
if args[1] == "whoami" then
|
||||
local manip = config.chatbox[user]
|
||||
if manip then
|
||||
chatbox.tell(user, "You are " .. user .. ", linked with `" .. manip .."`.", BOT_NAME)
|
||||
else
|
||||
chatbox.tell(user, "You are not registered. Ask the creator of this SIS to be registered.", BOT_NAME)
|
||||
end
|
||||
elseif args[1] == "deposit" then
|
||||
if not auth(user) then goto continue end
|
||||
|
||||
if not args[2] then
|
||||
chatbox.tell(user, "Supply a item (minecraft:`diamond` part) to deposit it!", BOT_NAME)
|
||||
goto continue
|
||||
end
|
||||
|
||||
|
||||
local perip = peripheral.wrap(config.chatbox[user])
|
||||
|
||||
---@type ccTweaked.peripheral.Inventory
|
||||
local peripInventory = perip.getInventory()
|
||||
local invList = peripInventory.list()
|
||||
|
||||
local allItems = {}
|
||||
local seen = {}
|
||||
|
||||
for _, stack in pairs(invList) do
|
||||
local name = stack and stack.name
|
||||
if name and not seen[name] then
|
||||
seen[name] = true
|
||||
allItems[#allItems + 1] = name
|
||||
end
|
||||
end
|
||||
|
||||
local best = findBest(allItems, args[2])
|
||||
|
||||
local itemName = ""
|
||||
|
||||
if #best > 1 then
|
||||
itemName = best[1][1]
|
||||
if string.find(":", args[2]) then
|
||||
itemName = args[2]
|
||||
end
|
||||
chatbox.tell(user, "`WARNING`: Item most likely inaccurate. Assuming " .. itemName, BOT_NAME)
|
||||
else
|
||||
itemName = best[1][1]
|
||||
end
|
||||
|
||||
local slots = {}
|
||||
|
||||
for slot, item in pairs(peripInventory.list()) do
|
||||
if item.name == itemName then
|
||||
table.insert(slots, slot)
|
||||
end
|
||||
end
|
||||
|
||||
local amount = nil
|
||||
|
||||
if args[3] then
|
||||
amount = tonumber(args[3], 10)
|
||||
end
|
||||
|
||||
local moved = inv.sendItemAwayMultiple(slots, peripInventory, peripInventory, amount)
|
||||
|
||||
chatbox.tell(user, "Moved `" .. tostring(moved) .. "` items.", BOT_NAME)
|
||||
elseif args[1] == "withdraw" then
|
||||
if not auth(user) then goto continue end
|
||||
|
||||
if not args[2] then
|
||||
chatbox.tell(user, "Supply a item (minecraft:`diamond` part) to withdraw it!", BOT_NAME)
|
||||
goto continue
|
||||
end
|
||||
|
||||
local perip = peripheral.wrap(config.chatbox[user])
|
||||
|
||||
---@type ccTweaked.peripheral.Inventory
|
||||
local peripInventory = perip.getInventory()
|
||||
|
||||
local best = findBest(inv.getAIL().listNames(), args[2])
|
||||
|
||||
local itemName = ""
|
||||
|
||||
if #best > 1 then
|
||||
itemName = best[1][1]
|
||||
if string.find(":", args[2]) then
|
||||
itemName = args[2]
|
||||
end
|
||||
chatbox.tell(user, "`WARNING`: Item most likely inaccurate. Assuming " .. itemName, BOT_NAME)
|
||||
else
|
||||
itemName = best[1][1]
|
||||
end
|
||||
|
||||
local amount = nil
|
||||
|
||||
if args[3] then
|
||||
amount = tonumber(args[3], 10)
|
||||
end
|
||||
|
||||
local moved = inv.sendItemToSelf(itemName, peripInventory, amount)
|
||||
|
||||
chatbox.tell(user, "Moved `" .. tostring(moved) .. "` items.", BOT_NAME)
|
||||
end
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
run = run
|
||||
}
|
||||
199
src/modules/inv.lua
Normal file
199
src/modules/inv.lua
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
local previousInventory = {}
|
||||
---@type ccTweaked.peripheral.WiredModem
|
||||
local modem = peripheral.find("modem");
|
||||
local turtleId = modem.getNameLocal()
|
||||
local turtleMoveAllowed = true
|
||||
local config = require("../../config") ---@type Config
|
||||
|
||||
local abstractInventoryLib = require("lib.abstractInventoryLib")
|
||||
|
||||
local peripherals = peripheral.getNames();
|
||||
|
||||
---@type AbstractInventory
|
||||
local ail = nil
|
||||
|
||||
local function sync()
|
||||
print("Syncing..")
|
||||
---@type string[]
|
||||
local foundInventories = {}
|
||||
|
||||
for _, invPattern in ipairs(config.inventories) do
|
||||
for _, inv in ipairs(peripherals) do
|
||||
if string.find(inv, invPattern) then
|
||||
table.insert(foundInventories, inv)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ail = abstractInventoryLib(foundInventories, true)
|
||||
|
||||
print("Synced.");
|
||||
end
|
||||
---@param itemName string
|
||||
---@param perip ccTweaked.peripheral.Inventory|nil
|
||||
---@param maxAmount number|nil
|
||||
local function sendItemToSelf(itemName, perip, maxAmount)
|
||||
if perip == nil then
|
||||
perip = turtleId
|
||||
end
|
||||
turtleMoveAllowed = false
|
||||
|
||||
local remaining = maxAmount or 64
|
||||
local total = 0
|
||||
|
||||
if remaining <= 0 then
|
||||
turtleMoveAllowed = true
|
||||
return
|
||||
end
|
||||
|
||||
if ail.getItem(itemName) then
|
||||
local nbtList = ail.listNBT(itemName)
|
||||
local chosenNBT = nil
|
||||
if nbtList and #nbtList > 0 then
|
||||
chosenNBT = nbtList[math.random(1, #nbtList)]
|
||||
end
|
||||
|
||||
if chosenNBT == "NONE" then
|
||||
chosenNBT = nil
|
||||
end
|
||||
|
||||
while remaining > 0 do
|
||||
local toSend = math.min(64, remaining)
|
||||
|
||||
local ok, moved = pcall(function()
|
||||
ail.performTransfer()
|
||||
--chatbox.tell("hartbreix", tostring(perip) .. " <> " .. tostring(itemName) .. " <> " .. tostring(toSend) .. " <> " .. tostring(chosenNBT), "debug")
|
||||
local amount = ail.pushItems(perip, itemName, toSend, nil, chosenNBT, {
|
||||
["allowBadTransfers"] = true,
|
||||
["optimal"] = true
|
||||
})
|
||||
|
||||
ail.performTransfer()
|
||||
|
||||
return amount
|
||||
end)
|
||||
total = total + moved
|
||||
if ok and moved and moved > 0 then
|
||||
remaining = remaining - moved
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- sleep(0.1)
|
||||
turtleMoveAllowed = true
|
||||
|
||||
return total
|
||||
end
|
||||
|
||||
---@param slots ccTweaked.turtle.turtleSlot[]
|
||||
---@param perip ccTweaked.peripheral.Inventory|nil
|
||||
---@param id string|nil
|
||||
---@param maxAmount number|nil
|
||||
local function sendItemAwayMultiple(slots, perip, id, maxAmount)
|
||||
if perip == nil then
|
||||
perip = turtle
|
||||
end
|
||||
local srcId = id or turtleId
|
||||
|
||||
local itemsInSlots = {}
|
||||
for _, slot in ipairs(slots) do
|
||||
local item = perip.getItemDetail(slot)
|
||||
if item then
|
||||
itemsInSlots[slot] = item
|
||||
end
|
||||
end
|
||||
|
||||
local totalMoved = 0
|
||||
local remaining = maxAmount or math.huge
|
||||
|
||||
if remaining <= 0 then
|
||||
return 0
|
||||
end
|
||||
|
||||
for _, slot in ipairs(slots) do
|
||||
if remaining <= 0 then break end
|
||||
|
||||
local toSend = math.min(64, remaining)
|
||||
|
||||
local ok, pulled = pcall(function()
|
||||
ail.performTransfer()
|
||||
|
||||
local moved = ail.pullItems(srcId, slot, toSend, nil, nil, {
|
||||
["allowBadTransfers"] = true,
|
||||
["optimal"] = false
|
||||
})
|
||||
|
||||
ail.performTransfer()
|
||||
return moved
|
||||
end)
|
||||
|
||||
if ok and pulled and pulled > 0 then
|
||||
totalMoved = totalMoved + pulled
|
||||
remaining = remaining - pulled
|
||||
end
|
||||
end
|
||||
|
||||
local numItems = 0
|
||||
for _, _ in pairs(itemsInSlots) do
|
||||
numItems = numItems + 1
|
||||
end
|
||||
|
||||
if numItems > 0 then
|
||||
os.queueEvent("update_ui")
|
||||
end
|
||||
|
||||
return totalMoved
|
||||
end
|
||||
|
||||
local function getTurtleInventory()
|
||||
local inventory = {}
|
||||
for slot = 1, 16 do
|
||||
local item = turtle.getItemDetail(slot, false)
|
||||
inventory[slot] = item
|
||||
end
|
||||
return inventory
|
||||
end
|
||||
|
||||
local function detectPlayerInsert()
|
||||
previousInventory = getTurtleInventory()
|
||||
|
||||
while true do
|
||||
os.pullEvent("turtle_inventory")
|
||||
|
||||
local currentInventory = getTurtleInventory()
|
||||
|
||||
if turtleMoveAllowed then
|
||||
local newlyAddedSlots = {}
|
||||
|
||||
for slot = 1, 16 do
|
||||
local prev = previousInventory[slot]
|
||||
local curr = currentInventory[slot]
|
||||
|
||||
if not prev and curr then
|
||||
table.insert(newlyAddedSlots, slot)
|
||||
end
|
||||
end
|
||||
|
||||
if #newlyAddedSlots > 0 then
|
||||
sendItemAwayMultiple(newlyAddedSlots)
|
||||
end
|
||||
end
|
||||
|
||||
previousInventory = currentInventory
|
||||
end
|
||||
end
|
||||
|
||||
local function getAIL()
|
||||
return ail
|
||||
end
|
||||
|
||||
|
||||
return {
|
||||
detectPlayerInsert = detectPlayerInsert,
|
||||
sendItemAwayMultiple = sendItemAwayMultiple,
|
||||
sendItemToSelf = sendItemToSelf,
|
||||
getTurtleInventory = getTurtleInventory,
|
||||
sync = sync,
|
||||
getAIL = getAIL,
|
||||
}
|
||||
71
src/modules/ui.lua
Normal file
71
src/modules/ui.lua
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
local inv = require("modules.inv");
|
||||
local PrimeUI = require("lib.primeui").PrimeUI;
|
||||
|
||||
local function runUi()
|
||||
local search = ""
|
||||
|
||||
local function getFiltered()
|
||||
local filtered = {}
|
||||
for name, count in pairs(inv.getAIL().listItemAmounts()) do
|
||||
if search == "" or string.find(name:lower(), search:lower(), 1, true) then
|
||||
table.insert(filtered, { name = name, count = count })
|
||||
end
|
||||
end
|
||||
table.sort(filtered, function(a, b) return a.count > b.count end)
|
||||
|
||||
local refiltered = {}
|
||||
|
||||
for i, filter in ipairs(filtered) do
|
||||
refiltered[i] = filter.name .. " -> " .. tostring(filter.count)
|
||||
end
|
||||
return refiltered
|
||||
end
|
||||
|
||||
local com = getFiltered("")
|
||||
|
||||
local win = term.current();
|
||||
local w , h= term.getSize()
|
||||
|
||||
PrimeUI.clear()
|
||||
local updateEntries = PrimeUI.selectionBox(
|
||||
win, 1, 4, w, h-7,
|
||||
function ()
|
||||
return com
|
||||
end,
|
||||
function(option)
|
||||
local z = option:match("^([^\\s]*)")
|
||||
|
||||
if inv.getAIL().getItem(z) then
|
||||
inv.sendItemToSelf(z)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
PrimeUI.inputBox(win, 2, 2, w-2, function (data)
|
||||
search = data
|
||||
com = getFiltered()
|
||||
updateEntries()
|
||||
end)
|
||||
|
||||
PrimeUI.label(win, 2, h-1, "primeui ui refucked")
|
||||
PrimeUI.label(win, 2, h-2, "rev. " .. fs.open("storage-solution/version", "r").readAll())
|
||||
|
||||
local timer = os.startTimer(1)
|
||||
PrimeUI.addTask(function()
|
||||
while true do
|
||||
local _, osTimer = os.pullEvent("timer")
|
||||
if osTimer == timer then
|
||||
com = getFiltered()
|
||||
updateEntries()
|
||||
timer = os.startTimer(1)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
PrimeUI.run()
|
||||
end
|
||||
|
||||
|
||||
return {
|
||||
runUi = runUi
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue