local Y=require("yaru.core") local gfx=require("yaru.gfx") local ui=require("yaru.ui") local component=require("component") local unicode=require("unicode") local M={} local SIDES={[0]="Bottom","Top","North","South","West","East"} function M.open() local st={side=nil,items={},sel=1,top=0,q={text="",cur=0},inv={}} local win=Y.newWindow{app="storage",title="Storage",w=96,h=30,minW=64,minH=14} local tp=component.isAvailable("transposer")and component.transposer local function scanSides() st.inv={} if not tp then return end for s=0,5 do local ok,size=pcall(tp.getInventorySize,s) if ok and size then local _,name=pcall(tp.getInventoryName,s) st.inv[#st.inv+1]={side=s,size=size,name=type(name)=="string"and name:gsub("^.-:","")or"inventory"} end end if not st.side and st.inv[1]then st.side=st.inv[1].side end end local function scan() if not tp or not st.side then return end local side=st.side st.busy,st.items=true,{} require("thread").create(function() local ok,size=pcall(tp.getInventorySize,side) local byKey={} for slot=1,ok and size or 0 do local ok2,stack=pcall(tp.getStackInSlot,side,slot) if ok2 and stack then local key=stack.name..":"..(stack.damage or 0) local it=byKey[key] if not it then it={label=stack.label or stack.name,name=stack.name,count=0,slots={}} byKey[key]=it st.items[#st.items+1]=it end it.count=it.count+stack.size it.slots[#it.slots+1]={slot,stack.size} end st.progress={slot,size} if slot%6==0 then Y.redraw(win)end end table.sort(st.items,function(a,b)return a.count>b.count end) st.busy,st.progress=false,nil Y.redraw(win) end):detach() end local function move(it,count,to) local left=count require("thread").create(function() for _,sl in ipairs(it.slots)do if left<=0 then break end local ok,n=pcall(tp.transferItem,st.side,to,math.min(left,sl[2]),sl[1]) if ok and n then left=left-n end end Y.notify("Storage",string.format("Moved %d × %s to %s",count-left,it.label,SIDES[to])) scan() end):detach() end scanSides();scan() function win.draw(w,cw,ch) local t=Y.theme if not tp then Y.drawIcon((cw-8)//2,ch//2-5,"storage") local s="Place a transposer next to the computer (or an adapter-connected one)" gfx.text((cw-unicode.len(s))//2,ch//2,s,t.dim) return end local SIDE=24 gfx.fill(1,1,SIDE,ch,t.sidebar) gfx.text(2,1,"Inventories",t.dim) for i,inv in ipairs(st.inv)do local y=i*2 if inv.side==st.side then ui.round(2,y,SIDE-2,1,t.dark and 0x454545 or 0xDEDDDA)end gfx.text(3,y,ui.trunc(SIDES[inv.side].." · "..inv.name,SIDE-4),t.fg) gfx.text(3,y+1,inv.size.." slots",t.dim) ui.hit(w,1,y,SIDE,2,function()st.side=inv.side;st.sel=1;st.top=0;scan()end) end if#st.inv==0 then gfx.text(2,3,"no inventories found",t.dim)end ui.button(w,2,ch,"Refresh",function()scanSides();scan()end,"flat",9) local x=SIDE+2 ui.entry(w,st.q,x,1,cw-SIDE-3,Y.focus==w,"Filter items") local list={} local q=unicode.lower(st.q.text) for _,it in ipairs(st.items)do if q==""or unicode.lower(it.label):find(q,1,true)then list[#list+1]=it end end if st.busy and st.progress then gfx.text(x,3,string.format("Reading slots… %d / %d",st.progress[1],st.progress[2]),t.dim) else gfx.text(x,3,#list.." item types",t.dim)end local rows=ch-4 for i=1,rows do local it=list[st.top+i] if not it then break end local y=3+i local sel=st.top+i==st.sel if sel then ui.select(x-1,y,cw-SIDE-1,t.sel)end local fg=sel and t.selFg or t.fg gfx.text(x+1,y,ui.trunc(it.label,cw-SIDE-22),fg) local c=tostring(it.count) gfx.text(cw-12-#c,y,c,fg) gfx.text(cw-10,y,#it.slots.." slot"..(#it.slots>1 and"s"or""),sel and fg or t.dim) local idx=st.top+i ui.hit(w,x-1,y,cw-SIDE,1,function(_,px,py) st.sel=idx local items={} for _,inv in ipairs(st.inv)do if inv.side~=st.side then items[#items+1]={"Move all to "..SIDES[inv.side].." ("..inv.name..")",function()move(it,it.count,inv.side)end} items[#items+1]={"Move 1 stack to "..SIDES[inv.side],function()move(it,64,inv.side)end} end end if#items==0 then items[1]={"Only one inventory attached",function()end,disabled=true}end Y.menu(w.x+px,w.y+py+1,items) end) end end function win.key(w,char,code) if code==Y.K.down then st.sel=st.sel+1 elseif code==Y.K.up then st.sel=math.max(1,st.sel-1) else ui.entryKey(st.q,char,code)end end function win.scroll(w,x,y,dir)st.top=math.max(0,st.top-dir*3)end return win end return M