local Y=require("yaru.core") local gfx=require("yaru.gfx") local ui=require("yaru.ui") local unicode=require("unicode") local M={} local MONTHS={"January","February","March","April","May","June","July","August","September","October","November","December"} local function daysIn(y,m) local d={31,28,31,30,31,30,31,31,30,31,30,31} if m==2 and(y%4==0 and(y%100~=0 or y%400==0))then return 29 end return d[m] end local function weekday(y,m,d) local tt={0,3,2,5,0,3,5,1,4,6,2,4} if m<3 then y=y-1 end local w=(y+y//4-y//100+y//400+tt[m]+d)%7 return(w+6)%7 end local function ago(t) local d=Y.now()-t if d<60 then return"now"elseif d<3600 then return(d//60).." min"end return Y.date("%H:%M",t) end function M.open() local now=Y.now() local o={kind="calendar",y=tonumber(Y.date("%Y",now)),m=tonumber(Y.date("%m",now)),off=0} Y.unread=false local pw,ph=74,20 local W=Y.size() local x0,y0=(W-pw)//2,3 function o.draw() local t=Y.theme local mx,my=x0,y0 ui.card(mx,my,pw,ph,t.menu) local nw=42 local list=Y.notifs if#list==0 then gfx.text(mx+(nw-16)//2,my+ph//2-1,"No Notifications",t.dim,t.menu) else local y=my+1 for i=1+o.off,#list do local n=list[i] if y+2>my+ph-3 then break end ui.round(mx+1,y,nw-2,2,t.menuHi) gfx.text(mx+2,y,ui.trunc(n.title,nw-12),t.menuFg,t.menuHi) local a=ago(n.time) gfx.text(mx+nw-3-unicode.len(a),y,a,t.dim,t.menuHi) gfx.text(mx+2,y+1,ui.trunc(n.body:gsub("\n"," "),nw-5),t.dim,t.menuHi) local idx=i o.hits[#o.hits+1]={mx+1,y,nw-2,2,function() local nn=table.remove(Y.notifs,idx) if nn and nn.action then Y.closeOverlay();pcall(nn.action)end end} y=y+3 end end local fy=my+ph-2 gfx.text(mx+2,fy,"Do Not Disturb",t.menuFg,t.menu) ui.switch(o,mx+17,fy,Y.cfg.dnd,function()Y.cfg.dnd=not Y.cfg.dnd;Y.saveConfig()end) if#list>0 then ui.button(o,mx+nw-9,fy,"Clear",function()Y.notifs={};o.off=0 end)end for yy=my+1,my+ph-2 do gfx.text(mx+nw,yy,"│",t.sep,t.menu)end local cx=mx+nw+2 local today=Y.now() gfx.text(cx,my+1,Y.date("%A",today),t.dim,t.menu) gfx.text(cx,my+2,Y.date("%B %d %Y",today),t.menuFg,t.menu) local title=MONTHS[o.m].." "..o.y gfx.text(cx,my+4,"‹",t.menuFg,t.menu) gfx.text(cx+3+(22-unicode.len(title))//2,my+4,title,t.menuFg,t.menu) gfx.text(cx+27,my+4,"›",t.menuFg,t.menu) o.hits[#o.hits+1]={cx-1,my+4,3,1,function()o.m=o.m-1;if o.m<1 then o.m,o.y=12,o.y-1 end end} o.hits[#o.hits+1]={cx+26,my+4,3,1,function()o.m=o.m+1;if o.m>12 then o.m,o.y=1,o.y+1 end end} local hdr={"M","T","W","T","F","S","S"} for i,h in ipairs(hdr)do gfx.text(cx+(i-1)*4+1,my+6,h,t.dim,t.menu)end local first=weekday(o.y,o.m,1) local ty,tm,td=tonumber(Y.date("%Y",today)),tonumber(Y.date("%m",today)),tonumber(Y.date("%d",today)) for d=1,daysIn(o.y,o.m)do local cell=first+d-1 local col,row=cell%7,cell//7 local x,y=cx+col*4,my+7+row*2 local isToday=o.y==ty and o.m==tm and d==td local s=string.format("%2d",d) if isToday then ui.pill(x-1,y,4,t.accent);gfx.text(x,y,s,0xFFFFFF,t.accent) else gfx.text(x,y,s,col>=5 and t.dim or t.menuFg,t.menu)end end o.hits[#o.hits+1]={mx,my,pw,ph,function()end} end function o.touch()Y.closeOverlay()return true end function o.scroll(_,x,y,dir) if x12 then o.m,o.y=1,o.y+1 end end gfx.damage(x0,y0-1,pw+1,ph+3) end function o.key(_,char,code) if code==Y.K.esc then Y.closeOverlay()end end Y.openOverlay(o) return o end return M