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 PORT=7777 local function wrap(s,w) local out={} while unicode.len(s)>w do out[#out+1]=unicode.sub(s,1,w);s=unicode.sub(s,w+1)end out[#out+1]=s return out end function M.open() local existing=Y.windowsOf("chat")[1] if existing then Y.focusWin(existing)return existing end local st={msgs={},input={text="",cur=0},nick=os.getenv("HOSTNAME")or("pc-"..require("computer").address():sub(1,4))} local win=Y.newWindow{app="chat",title="Messages",w=70,h=26,minW=40,minH=10} local modem=component.isAvailable("modem")and component.modem local tunnel=component.isAvailable("tunnel")and component.tunnel if modem then modem.open(PORT)end local function add(from,text,mine) st.msgs[#st.msgs+1]={from=from,text=text,mine=mine,time=Y.date("%H:%M")} if#st.msgs>200 then table.remove(st.msgs,1)end end local function send() local s=st.input.text if s==""then return end if s:sub(1,6)=="/nick "then st.nick=s:sub(7);st.input.text,st.input.cur="",0 return end local ok=false if modem then ok=modem.broadcast(PORT,"yaru-chat",st.nick,s)or ok end if tunnel then ok=tunnel.send("yaru-chat",st.nick,s)or ok end add(st.nick,s,true) if not ok then add("system","not sent: no network card",false)end st.input.text,st.input.cur="",0 end function win.onSignal(name,_,remote,port,dist,tag,nick,text) if name~="modem_message"or tag~="yaru-chat"then return end add(tostring(nick),tostring(text),false) if Y.focus~=win then Y.notify(tostring(nick),tostring(text),{action=function()Y.focusWin(win)end,actionLabel="Reply"})end end function win.draw(w,cw,ch) local t=Y.theme gfx.fill(1,1,cw,ch-2,t.view) if not modem and not tunnel then local s="Install a network card (or linked card) to chat" gfx.text((cw-unicode.len(s))//2,ch//2-1,s,t.dim) end local y=ch-3 for i=#st.msgs,1,-1 do local m=st.msgs[i] local bw=math.min(cw-10,math.max(unicode.len(m.text),unicode.len(m.from)+6)+2) local lines=wrap(m.text,bw-2) local h=#lines+1 y=y-h if y<1 then break end local x=m.mine and cw-bw-1 or 2 local bg=m.mine and t.accent or(t.dark and 0x383838 or 0xEBEBEB) local fg=m.mine and 0xFFFFFF or t.fg ui.round(x,y,bw,h,bg) gfx.text(x+1,y,ui.trunc(m.from,bw-8),m.mine and 0xFFFFFF or t.accent,bg) gfx.text(x+bw-6,y,m.time,m.mine and 0xFFFFFF or t.dim,bg) for li,l in ipairs(lines)do gfx.text(x+1,y+li,l,fg,bg)end y=y-1 end gfx.fill(1,ch-1,cw,2,t.bg) ui.entry(w,st.input,2,ch-1,cw-11,Y.focus==w,"Message as "..st.nick.." (/nick NAME)") ui.button(w,cw-8,ch-1,"Send",send,"suggested",7) end function win.key(w,char,code) local r=ui.entryKey(st.input,char,code) if r=="enter"then send()end end function win.paste(w,s)ui.entryPaste(st.input,s)end function win.cleanup()if modem then pcall(modem.close,PORT)end end add("system",modem and("listening on port "..PORT.." as "..st.nick)or tunnel and"linked card ready"or"offline",false) return win end return M