local Y=require("yaru.core") local gfx=require("yaru.gfx") local ui=require("yaru.ui") local fs=require("filesystem") local component=require("component") local computer=require("computer") local unicode=require("unicode") local M={} local SKIP={["/dev"]=true,["/mnt"]=true,["/tmp"]=true,["/proc"]=true,["/home/.local/share/Trash"]=true} local function drives() local r={} local boot=computer.getBootAddress() for addr in component.list("filesystem")do local p=component.proxy(addr) local mounts={} for proxy,path in fs.mounts()do if proxy.address==addr then mounts[#mounts+1]=path end end table.sort(mounts) local tmp=addr==computer.tmpAddress() r[#r+1]={addr=addr,proxy=p,label=p.getLabel()or(tmp and"tmpfs"or addr:sub(1,8)),mounts=mounts, total=p.spaceTotal(),used=p.spaceUsed(),ro=p.isReadOnly(),boot=addr==boot,tmp=tmp} end table.sort(r,function(a,b)return(a.boot and 0 or a.tmp and 2 or 1)<(b.boot and 0 or b.tmp and 2 or 1)end) return r end function M.open() local st={list=drives(),sel=1} local win=Y.newWindow{app="disks",title="Disks",w=88,h=26,minW=60,minH=14} local function refresh()st.list=drives();st.sel=math.min(st.sel,#st.list);Y.scanDrives();Y.dockDirty()end local function install(d) local target=d.mounts[1] if not target then return Y.notify("Disks","Mount the drive first")end Y.dialog{title="Install the system on “"..d.label.."”?",text="Copies OpenOS, the optimisations and the desktop.\nExisting files with the same names are overwritten.", buttons={{"Cancel"},{"Install",function() st.job={done=0,name="counting…"} require("thread").create(function() local n=0 local function copy(src,dst) if SKIP[src]then return end if fs.isDirectory(src)then if fs.get(src)and fs.get(src).address~=computer.getBootAddress()and src~="/"then return end fs.makeDirectory(dst) for name in fs.list(src)do copy(fs.concat(src,name),fs.concat(dst,name))end else fs.copy(src,dst) n=n+1 st.job.done,st.job.name=n,src if n%8==0 then Y.redraw(win);os.sleep(0)end end end local ok,err=pcall(copy,"/",target) pcall(d.proxy.setLabel,"OpenOS Yaru") st.job=nil refresh() Y.redraw(win) Y.notify(ok and"System installed"or"Install failed",ok and(n.." files copied to "..d.label)or tostring(err)) end):detach() end,"suggested"}}} end local function erase(d) if d.boot or d.ro or d.tmp then return end Y.dialog{title="Erase “"..d.label.."”?",text="ALL files on this drive will be deleted. This cannot be undone.", buttons={{"Cancel"},{"Erase",function() for name in d.proxy.list("/")do d.proxy.remove(name)end refresh() Y.notify("Disks",d.label.." erased") end,"destructive"}}} end function win.draw(w,cw,ch) local t=Y.theme local SIDE=26 gfx.fill(1,1,SIDE,ch,t.sidebar) for i,d in ipairs(st.list)do local y=1+(i-1)*3 if y+2>ch then break end if i==st.sel then ui.round(2,y,SIDE-2,2,t.dark and 0x454545 or 0xDEDDDA)end gfx.text(4,y,ui.trunc(d.label,SIDE-6),t.fg) local kind=d.boot and"System"or d.tmp and"RAM disk"or d.ro and"Read-only"or(d.total<=512*1024 and"Floppy"or"Drive") gfx.text(4,y+1,kind.." · "..ui.size(d.total),t.dim) ui.hit(w,1,y,SIDE,2,function()st.sel=i end) end local d=st.list[st.sel] if not d then return end local x,pw=SIDE+3,cw-SIDE-4 Y.drawIcon(x,2,d.total<=512*1024 and"floppy"or"hdd") gfx.text(x+10,2,d.label,t.fg) gfx.text(x+10,3,d.addr,t.dim) gfx.text(x+10,4,(d.boot and"Boot drive · "or"")..(d.ro and"read-only"or"writable"),t.dim) local y=7 gfx.text(x,y,"Usage",t.fg) local us=string.format("%s used of %s (%d%%)",ui.size(d.used),ui.size(d.total),d.total>0 and d.used*100//d.total or 0) gfx.text(x+pw-unicode.len(us),y,us,t.dim) ui.progress(x,y+1,pw,d.total>0 and d.used/d.total or 0,d.used/math.max(1,d.total)>.9 and t.danger or t.accent) y=y+3 gfx.text(x,y,"Mounted at",t.fg) gfx.text(x+12,y,#d.mounts>0 and table.concat(d.mounts,", ")or"not mounted",t.dim) y=y+2 local bx=x if#d.mounts>0 then bx=bx+ui.button(w,bx,y,"Open",function()Y.launch("files",d.mounts[1])end)+1 if not d.boot and not d.tmp then bx=bx+ui.button(w,bx,y,"Eject",function()for _,m in ipairs(d.mounts)do fs.umount(m)end refresh()end)+1 end else bx=bx+ui.button(w,bx,y,"Mount",function() local p="/mnt/"..d.addr:sub(1,3) fs.mount(d.proxy,p) refresh() end,"suggested")+1 end if not d.ro then bx=bx+ui.button(w,bx,y,"Rename…",function() Y.dialog{title="Drive Label",entry=d.label,buttons={{"Cancel"},{"Rename",function(s) if s and s~=""then pcall(d.proxy.setLabel,s:sub(1,16));refresh()end end,"suggested"}}} end)+1 end y=y+2 if not d.boot and not d.ro and not d.tmp then ui.button(w,x,y,"Install System Here…",function()install(d)end,"suggested") ui.button(w,x+26,y,"Erase…",function()erase(d)end,"destructive") end if st.job then gfx.text(x,y+2,ui.trunc("Copying "..st.job.done.." files · "..st.job.name,pw),t.dim) end end function win.key(w,char,code) if code==Y.K.down then st.sel=math.min(#st.list,st.sel+1)elseif code==Y.K.up then st.sel=math.max(1,st.sel-1) elseif code==0x3F then refresh()end end win.onSignal=function(name)if name=="component_added"or name=="component_removed"then refresh()end end return win end return M