Calendar
GPT 5.6 terra Pro · full

Browser check: Artifact runtime error. This is a technical check, not a quality review.
Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 22,369 bytes · SHA-256 f21e843b512f
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Daymark Calendar</title><style>
:root{--blue:#4285f4;--ink:#202124;--muted:#70757a;--line:#e8eaed;--bg:#f8fafd;--panel:#fff;--shadow:0 5px 18px #20212418}*{box-sizing:border-box}body{margin:0;background:var(--bg);font:14px/1.35 Arial,sans-serif;color:var(--ink)}button,input,textarea,select{font:inherit}button{cursor:pointer;border:0;background:none;color:inherit}.app{max-width:1500px;margin:auto;padding:18px}.top{height:58px;display:flex;align-items:center;gap:12px;background:#fff;border:1px solid var(--line);border-radius:14px;padding:8px 14px;box-shadow:0 1px 2px #00000008}.brand{font-size:20px;font-weight:700;letter-spacing:-.5px;margin-right:10px}.brand i{font-style:normal;color:var(--blue)}.btn{padding:8px 12px;border-radius:8px;border:1px solid var(--line);font-weight:600}.btn:hover{background:#f1f3f4}.primary{background:var(--blue);color:#fff;border-color:var(--blue)}.title{font-size:20px;font-weight:600;min-width:190px;text-align:center}.spacer{flex:1}.seg{display:flex;border:1px solid var(--line);border-radius:8px;overflow:hidden}.seg button{padding:7px 11px;border-right:1px solid var(--line)}.seg button:last-child{border:0}.seg .on{background:#e8f0fe;color:#1967d2;font-weight:bold}.calendar{margin-top:14px;background:var(--panel);border:1px solid var(--line);border-radius:14px;overflow:hidden;box-shadow:0 1px 2px #00000008;animation:in .22s ease}@keyframes in{from{opacity:.35;transform:translateY(5px)}to{opacity:1;transform:none}}.dow,.monthgrid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr))}.dow{border-bottom:1px solid var(--line)}.dow div{text-align:center;padding:11px 2px;color:var(--muted);font-size:11px;font-weight:bold;text-transform:uppercase}.monthgrid{grid-template-rows:repeat(6,112px)}.day{position:relative;border-right:1px solid var(--line);border-bottom:1px solid var(--line);padding:6px;min-width:0}.day:nth-child(7n){border-right:0}.day:hover{background:#f8fbff}.day.out{background:#fafafa;color:#a7aaae}.num{width:25px;height:25px;display:flex;align-items:center;justify-content:center;margin-bottom:2px}.today .num{color:#fff;background:var(--blue);border-radius:50%;font-weight:bold}.chip{display:block;width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left;padding:3px 5px 3px 8px;margin:2px 0;border-radius:4px;border-left:4px solid var(--c);background:color-mix(in srgb,var(--c) 13%,white);font-size:12px;cursor:grab}.more{font-size:12px;color:#1967d2;font-weight:bold;padding:2px;cursor:pointer}.week{display:grid;grid-template-columns:60px repeat(7,minmax(0,1fr));min-height:720px}.weekhead{display:contents}.corner,.wday{height:58px;border-bottom:1px solid var(--line);padding:9px;text-align:center}.wday b{display:block;font-size:12px;text-transform:uppercase;color:var(--muted)}.wday span{display:inline-flex;justify-content:center;align-items:center;margin-top:2px;width:25px;height:25px}.wday.is-today span{background:var(--blue);color:#fff;border-radius:50%}.times{grid-column:1;border-right:1px solid var(--line);position:relative}.time{height:54px;text-align:right;padding:0 8px;color:var(--muted);font-size:11px;transform:translateY(-7px)}.allday-label{position:absolute;top:61px;left:5px;color:var(--muted);font-size:10px}.weekdays{grid-column:2/9;display:grid;grid-template-columns:repeat(7,1fr);grid-template-rows:34px 648px}.allcol{border-right:1px solid var(--line);border-bottom:1px solid var(--line);padding:3px;min-width:0}.allcol:last-child{border:0}.weekcanvas{grid-column:1/8;display:grid;grid-template-columns:repeat(7,1fr);position:relative;background:repeating-linear-gradient(to bottom,transparent 0,transparent 53px,var(--line) 54px)}.weekcol{border-right:1px solid var(--line);position:relative}.weekcol:last-child{border:0}.wevent{position:absolute;left:3px;right:3px;border-left:4px solid var(--c);background:color-mix(in srgb,var(--c) 15%,white);border-radius:4px;padding:3px 5px;overflow:hidden;font-size:11px;z-index:2;cursor:grab}.now{position:absolute;left:0;right:0;border-top:2px solid #ea4335;z-index:3;pointer-events:none}.agenda{padding:10px 20px 30px}.agday{display:grid;grid-template-columns:100px 1fr;border-bottom:1px solid var(--line);padding:14px 0}.agdate{font-weight:bold;color:#5f6368}.agitem{padding:8px 10px;border-left:5px solid var(--c);background:color-mix(in srgb,var(--c) 10%,white);border-radius:5px;margin-bottom:6px}.empty{color:var(--muted);padding:40px;text-align:center}.modalback,.popover{position:fixed;inset:0;background:#20212455;z-index:20;display:flex;align-items:center;justify-content:center}.modal{background:#fff;width:min(500px,calc(100% - 30px));border-radius:13px;box-shadow:var(--shadow);padding:22px}.modal h2{margin:0 0 16px}.field{margin:10px 0}.field label{display:block;font-size:12px;font-weight:bold;color:var(--muted);margin-bottom:4px}.field input,.field textarea,.field select{width:100%;border:1px solid #dadce0;border-radius:6px;padding:8px}.row{display:flex;gap:10px}.row>*{flex:1}.colors{display:flex;gap:8px}.swatch{width:25px;height:25px;border-radius:50%;background:var(--c);border:3px solid transparent}.swatch.sel{border-color:#202124}.actions{display:flex;justify-content:flex-end;gap:8px;margin-top:18px}.error{color:#d93025;font-size:12px;min-height:16px}.choice{display:flex;gap:7px;flex-wrap:wrap}.choice label{font-weight:normal;color:var(--ink)}.pop{background:#fff;width:330px;max-height:75vh;overflow:auto;border-radius:12px;padding:16px;box-shadow:var(--shadow)}.toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%);background:#323232;color:#fff;border-radius:6px;padding:12px 16px;z-index:50;box-shadow:var(--shadow)}.toast button{color:#8ab4f8;font-weight:bold;margin-left:18px}@media(max-width:780px){.app{padding:8px}.brand{display:none}.top{gap:5px}.title{min-width:120px;font-size:16px}.btn{padding:7px 8px}.monthgrid{grid-template-rows:repeat(6,94px)}.day{padding:3px}.chip{font-size:10px}.week{grid-template-columns:42px repeat(7,minmax(75px,1fr));overflow:auto}.calendar{overflow:auto}.seg button{padding:7px}.hide-sm{display:none}}
</style></head><body><div class="app"><header class="top"><div class="brand">day<i>mark</i></div><button class="btn primary" id="newBtn">+ Create</button><button class="btn" id="todayBtn">Today</button><button class="btn" id="prev">‹</button><button class="btn" id="next">›</button><div class="title" id="title"></div><div class="spacer"></div><button class="btn hide-sm" id="weekStart">Mon start</button><div class="seg"><button data-view="month">Month</button><button data-view="week">Week</button><button data-view="agenda">Agenda</button></div></header><main id="cal" class="calendar"></main></div><div id="layer"></div><script>
'use strict';
/* ===== Tunable constants ===== */
const CONFIG={colors:['#4285f4','#34a853','#fbbc04','#ea4335','#a142f4','#00acc1'],hourStart:6,hourEnd:18,snap:15,weekStart:1};
/* ===== Date utilities: pure Y/M/D component arithmetic ===== */
const DateUtil=(()=>{const pad=n=>String(n).padStart(2,'0');const key=d=>`${d.y}-${pad(d.m)}-${pad(d.d)}`;const parse=s=>{let[a,b,c]=s.split('-').map(Number);return{y:a,m:b,d:c}};const leap=y=>y%4===0&&(y%100!==0||y%400===0);const dim=(y,m)=>[31,leap(y)?29:28,31,30,31,30,31,31,30,31,30,31][m-1];const cmp=(a,b)=>key(a).localeCompare(key(b));const addDays=(d,n)=>{let{y,m,d:day}=d;while(n>0){let x=dim(y,m)-day;if(n<=x){day+=n;n=0}else{n-=x+1;day=1;if(++m>12){m=1;y++}}}while(n<0){if(day+n>0){day+=n;n=0}else{n+=day;if(--m<1){m=12;y--}day=dim(y,m)}}return{y,m,d:day}};const addMonths=(d,n)=>{let x=d.y*12+d.m-1+n,y=Math.floor(x/12),m=x%12+1;return{y,m,d:Math.min(d.d,dim(y,m))}};const dow=d=>{let y=d.y,m=d.m,day=d.d,t=[0,3,2,5,0,3,5,1,4,6,2,4];if(m<3)y--;return(y+Math.floor(y/4)-Math.floor(y/100)+Math.floor(y/400)+t[m-1]+day)%7};const start=d=>addDays(d,-((dow(d)-state.settings.weekStart+7)%7));const weekNum=d=>{let jan={y:d.y,m:1,d:1},offset=(dow(jan)-state.settings.weekStart+7)%7;return Math.floor((daysBetween(jan,d)+offset)/7)+1};const daysBetween=(a,b)=>{let sign=cmp(a,b)<=0?1:-1,n=0,x={...a};while(key(x)!==key(b)){x=addDays(x,sign);n+=sign}return n};const range=(a,b)=>{let r=[],x={...a};while(cmp(x,b)<=0){r.push(x);x=addDays(x,1)}return r};return{pad,key,parse,leap,daysInMonth:dim,addDays,addMonths,dow,weekStart:start,weekNum,daysBetween,range,cmp}})();
/* ===== State & persistence ===== */
const today=(()=>{let x=new Date;return{y:x.getFullYear(),m:x.getMonth()+1,d:x.getDate()}})();
let state=JSON.parse(localStorage.getItem('daymark-v2')||'null')||{events:[],settings:{weekStart:CONFIG.weekStart},view:'month',cursor:{...today}};state.settings??={weekStart:CONFIG.weekStart};const save=()=>localStorage.setItem('daymark-v2',JSON.stringify(state));const id=()=>crypto.randomUUID?crypto.randomUUID():Math.random().toString(36).slice(2);
/* recurrence: expand only visible range; exceptions map occurrence key -> replacement event or null */
function occurs(master,d){let base=DateUtil.parse(master.date), rule=master.recur;if(DateUtil.cmp(d,base)<0)return false;if(!rule)return DateUtil.key(d)===master.date;if(rule.until&&DateUtil.key(d)>rule.until)return false;if(rule.type==='daily')return true;if(rule.type==='weekly')return (rule.days||[DateUtil.dow(base)]).includes(DateUtil.dow(d));if(rule.type==='monthly')return d.d===base.d;if(rule.type==='yearly')return d.m===base.m&&d.d===base.d;return false}
function expand(from,to){let out=[];for(const e of state.events){if(!e.recur){let d=DateUtil.parse(e.date);if(DateUtil.cmp(d,from)>=0&&DateUtil.cmp(d,to)<=0)out.push({...e,occ:e.date,master:e.id})}else for(const d of DateUtil.range(from,to))if(occurs(e,d)){let k=DateUtil.key(d),ex=e.exceptions?.[k];if(ex===null)continue;out.push(ex?{...ex,occ:k,master:e.id,exception:true}:{...e,date:k,occ:k,master:e.id})}}return out.sort((a,b)=>a.date.localeCompare(b.date)||(a.start||'').localeCompare(b.start||''))}
/* ===== Rendering ===== */
const cal=document.querySelector('#cal'),title=document.querySelector('#title');
function render(){save();document.querySelectorAll('[data-view]').forEach(b=>b.classList.toggle('on',b.dataset.view===state.view));document.querySelector('#weekStart').textContent=state.settings.weekStart?'Mon start':'Sun start';cal.style.animation='none';requestAnimationFrame(()=>cal.style.animation='');if(state.view==='month')month();else if(state.view==='week')week();else agenda()}
function labels(){return state.settings.weekStart?['Mon','Tue','Wed','Thu','Fri','Sat','Sun']:['Sun','Mon','Tue','Wed','Thu','Fri','Sat']}
function month(){let c=state.cursor,first={y:c.y,m:c.m,d:1},grid=DateUtil.weekStart(first),end=DateUtil.addDays(grid,41);title.textContent=new Intl.DateTimeFormat('en',{month:'long',year:'numeric'}).format(new Date(c.y,c.m-1,1));let es=expand(grid,end),html=`<div class="dow">${labels().map(x=>`<div>${x}</div>`).join('')}</div><div class="monthgrid">`;DateUtil.range(grid,end).forEach(d=>{let k=DateUtil.key(d),items=es.filter(e=>e.date===k),inm=d.m===c.m;html+=`<div class="day ${inm?'':'out'} ${k===DateUtil.key(today)?'today':''}" data-date="${k}"><div class="num">${d.d}</div>${items.slice(0,3).map(chip).join('')}${items.length>3?`<button class="more" data-pop="${k}">+${items.length-3} more</button>`:''}</div>`});cal.innerHTML=html+'</div>'}
function chip(e){return`<button draggable="true" class="chip" style="--c:${e.color}" data-eid="${e.id}" data-occ="${e.occ}">${e.allDay?'':e.start+' '}${esc(e.title)}</button>`}
function week(){let s=DateUtil.weekStart(state.cursor),days=DateUtil.range(s,DateUtil.addDays(s,6)),es=expand(s,days[6]);title.textContent=`${new Intl.DateTimeFormat('en',{month:'short',day:'numeric'}).format(new Date(s.y,s.m-1,s.d))} – ${new Intl.DateTimeFormat('en',{month:'short',day:'numeric',year:'numeric'}).format(new Date(days[6].y,days[6].m-1,days[6].d))}`;let hrs='';for(let h=CONFIG.hourStart;h<CONFIG.hourEnd;h++)hrs+=`<div class="time">${h}:00</div>`;let heads=days.map((d,i)=>`<div class="wday ${DateUtil.key(d)===DateUtil.key(today)?'is-today':''}"><b>${labels()[i]}</b><span>${d.d}</span></div>`).join('');let all=days.map(d=>`<div class="allcol" data-date="${DateUtil.key(d)}">${es.filter(e=>e.date===DateUtil.key(d)&&e.allDay).map(chip).join('')}</div>`).join('');let cols=days.map(d=>{let k=DateUtil.key(d);return`<div class="weekcol" data-date="${k}">${es.filter(e=>e.date===k&&!e.allDay).map(weekEvent).join('')}</div>`}).join('');let now='';if(DateUtil.cmp(today,s)>=0&&DateUtil.cmp(today,days[6])<=0){let n=new Date,mins=n.getHours()*60+n.getMinutes(),top=(mins-CONFIG.hourStart*60)/60*54;if(top>=0&&top<=648)now=`<div class="now" style="top:${top}px"></div>`}cal.innerHTML=`<div class="week"><div class="weekhead"><div class="corner">Week ${DateUtil.weekNum(s)}</div>${heads}</div><div class="times"><span class="allday-label">all-day</span>${hrs}</div><div class="weekdays">${all}<div class="weekcanvas">${cols}${now}</div></div></div>`}
function weekEvent(e){let [h,m]=e.start.split(':').map(Number), [eh,em]=e.end.split(':').map(Number), top=((h*60+m)-CONFIG.hourStart*60)/60*54,ht=Math.max(18,((eh*60+em)-(h*60+m))/60*54);return`<button draggable="true" class="wevent" style="--c:${e.color};top:${top}px;height:${ht}px" data-eid="${e.id}" data-occ="${e.occ}">${esc(e.title)}<br><small>${e.start}–${e.end}</small></button>`}
function agenda(){let a=state.cursor,b=DateUtil.addDays(a,60),es=expand(a,b);title.textContent='Upcoming agenda';let groups={};es.forEach(e=>(groups[e.date]??=[]).push(e));cal.innerHTML='<div class="agenda">'+(Object.keys(groups).length?Object.entries(groups).map(([k,x])=>`<section class="agday"><div class="agdate">${new Intl.DateTimeFormat('en',{weekday:'short',month:'short',day:'numeric'}).format(new Date(...k.split('-').map((v,i)=>i===1?+v-1:+v)))}</div><div>${x.map(e=>`<button class="agitem" style="--c:${e.color}" data-eid="${e.id}" data-occ="${e.occ}">${e.allDay?'All day':e.start+' – '+e.end} · <b>${esc(e.title)}</b>${e.notes?'<br><small>'+esc(e.notes)+'</small>':''}</button>`).join('')}</div></section>`).join(''):'<div class="empty">No events in the next 60 days.</div>')+'</div>'}
const esc=s=>String(s||'').replace(/[&<>]/g,x=>({'&':'&','<':'<','>':'>'}[x]));
/* ===== Editor & recurrence mutations ===== */
const layer=document.querySelector('#layer');let editing=null,selectedColor=CONFIG.colors[0];
function editor(date=DateUtil.key(state.cursor),ev=null){editing=ev;selectedColor=ev?.color||CONFIG.colors[0];let r=ev?.recur;layer.innerHTML=`<div class="modalback"><form class="modal" id="form"><h2>${ev?'Edit event':'New event'}</h2><div class="field"><label>Title</label><input required name="title" value="${esc(ev?.title||'')}" placeholder="Add title"></div><div class="row"><div class="field"><label>Date</label><input required type="date" name="date" value="${ev?.date||date}"></div><div class="field"><label>Color</label><div class="colors">${CONFIG.colors.map(c=>`<button type="button" class="swatch ${c===selectedColor?'sel':''}" style="--c:${c}" data-color="${c}"></button>`).join('')}</div></div></div><div class="field"><label><input type="checkbox" name="allDay" ${ev?.allDay?'checked':''}> All day</label></div><div class="row timeset"><div class="field"><label>Start</label><input type="time" name="start" value="${ev?.start||'09:00'}"></div><div class="field"><label>End</label><input type="time" name="end" value="${ev?.end||'10:00'}"></div></div><div class="field"><label>Repeat</label><select name="rtype"><option value="">Does not repeat</option>${['daily','weekly','monthly','yearly'].map(x=>`<option ${r?.type===x?'selected':''} value="${x}">${x[0].toUpperCase()+x.slice(1)}</option>`).join('')}</select></div><div class="field weekdays" style="display:${r?.type==='weekly'?'block':'none'}"><label>Repeat on</label><div class="choice">${['Sun','Mon','Tue','Wed','Thu','Fri','Sat'].map((x,i)=>`<label><input type="checkbox" name="wd" value="${i}" ${(r?.days||[DateUtil.dow(DateUtil.parse(ev?.date||date))]).includes(i)?'checked':''}>${x}</label>`).join('')}</div></div><div class="field"><label>Notes</label><textarea name="notes" rows="2">${esc(ev?.notes||'')}</textarea></div><div class="error" id="err"></div><div class="actions">${ev?'<button type="button" class="btn" id="delete">Delete</button>':''}<button type="button" class="btn" id="cancel">Cancel</button><button class="btn primary">Save</button></div></form></div>`}
function scopePrompt(cb){layer.innerHTML=`<div class="popover"><div class="pop"><h3>Apply changes to</h3><p>This is a recurring event.</p><div class="actions"><button class="btn" data-scope="one">This event only</button><button class="btn" data-scope="following">This and following</button><button class="btn primary" data-scope="all">Entire series</button></div></div></div>`;layer.onclick=e=>{let s=e.target.dataset.scope;if(s){layer.innerHTML='';layer.onclick=null;cb(s)}}}
function modify(ev,data,scope){let master=state.events.find(x=>x.id===ev.master);if(!ev.recur||scope==='all'){Object.assign(master,data);return}if(scope==='one'){master.exceptions??={};master.exceptions[ev.occ]={...master,...data,id:master.id,date:ev.occ,recur:null};return} // split: truncate prior and start a cloned master
let old={...master,recur:{...master.recur,until:DateUtil.key(DateUtil.addDays(DateUtil.parse(ev.occ),-1))}};Object.assign(master,old);let next={...master,...data,id:id(),date:ev.occ,exceptions:{},recur:{...master.recur}};state.events.push(next)}
function deleteEvent(ev,scope){let master=state.events.find(x=>x.id===ev.master),backup=JSON.stringify(state.events);if(!ev.recur||scope==='all')state.events=state.events.filter(x=>x.id!==master.id);else if(scope==='one'){master.exceptions??={};master.exceptions[ev.occ]=null}else{master.recur.until=DateUtil.key(DateUtil.addDays(DateUtil.parse(ev.occ),-1))}render();toast('Event deleted',()=>{state.events=JSON.parse(backup);render()})}
/* ===== Interactions ===== */
document.addEventListener('click',e=>{let b=e.target.closest('[data-view]');if(b){state.view=b.dataset.view;render();return}if(e.target.closest('#newBtn')){editor();return}if(e.target.closest('#todayBtn')){state.cursor={...today};render();return}if(e.target.closest('#prev')){state.cursor=state.view==='month'?DateUtil.addMonths(state.cursor,-1):DateUtil.addDays(state.cursor,state.view==='week'?-7:-30);render();return}if(e.target.closest('#next')){state.cursor=state.view==='month'?DateUtil.addMonths(state.cursor,1):DateUtil.addDays(state.cursor,state.view==='week'?7:30);render();return}if(e.target.closest('#weekStart')){state.settings.weekStart=state.settings.weekStart?0:1;render();return}let p=e.target.closest('[data-pop]');if(p){dayPop(p.dataset.pop);return}let ce=e.target.closest('[data-eid]');if(ce){let ev=expand(DateUtil.parse(ce.dataset.occ),DateUtil.parse(ce.dataset.occ)).find(x=>x.id===ce.dataset.eid&&x.occ===ce.dataset.occ);if(ev)editor(ev.date,ev);return}let cell=e.target.closest('.day');if(cell)editor(cell.dataset.date);if(e.target.id==='cancel'||e.target.classList.contains('modalback'))layer.innerHTML=''});
layer.addEventListener('click',e=>{if(e.target.dataset.color){selectedColor=e.target.dataset.color;document.querySelectorAll('.swatch').forEach(x=>x.classList.toggle('sel',x.dataset.color===selectedColor))}if(e.target.id==='delete'&&editing){let go=s=>deleteEvent(editing,s);editing.recur?scopePrompt(go):go('all')}});layer.addEventListener('change',e=>{if(e.target.name==='allDay')document.querySelector('.timeset').style.opacity=e.target.checked?.45:1;if(e.target.name==='rtype')document.querySelector('.weekdays').style.display=e.target.value==='weekly'?'block':'none'});layer.addEventListener('submit',e=>{e.preventDefault();let f=new FormData(e.target),all=f.get('allDay')==='on',start=f.get('start'),end=f.get('end');if(!all&&end<=start){document.querySelector('#err').textContent='End time must be after start time.';return}let rt=f.get('rtype'),data={title:f.get('title').trim(),date:f.get('date'),allDay,start,end,notes:f.get('notes').trim(),color:selectedColor,recur:rt?{type:rt,days:rt==='weekly'?f.getAll('wd').map(Number):undefined}:null};if(!data.title)return;if(editing){let go=s=>{modify(editing,data,s);layer.innerHTML='';render()};editing.recur?scopePrompt(go):go('all')}else{data.id=id();data.exceptions={};state.events.push(data);layer.innerHTML='';render()}});
let drag=null;document.addEventListener('dragstart',e=>{let x=e.target.closest('[data-eid]');if(x)drag={id:x.dataset.eid,occ:x.dataset.occ,week:!!x.classList.contains('wevent')};});document.addEventListener('dragover',e=>{if(drag)e.preventDefault()});document.addEventListener('drop',e=>{if(!drag)return;let target=e.target.closest('[data-date]');if(!target)return;let ev=expand(DateUtil.parse(drag.occ),DateUtil.parse(drag.occ)).find(x=>x.id===drag.id&&x.occ===drag.occ);if(!ev)return;let data={date:target.dataset.date};if(drag.week&&!ev.allDay){let rect=target.getBoundingClientRect(),mins=(e.clientY-rect.top)/54*60+CONFIG.hourStart*60,snap=Math.max(CONFIG.hourStart*60,Math.round(mins/CONFIG.snap)*CONFIG.snap),dur=(+ev.end.slice(0,2)*60+ +ev.end.slice(3))-(+ev.start.slice(0,2)*60+ +ev.start.slice(3));data.start=`${DateUtil.pad(Math.floor(snap/60))}:${DateUtil.pad(snap%60)}`;let ee=snap+dur;data.end=`${DateUtil.pad(Math.floor(ee/60))}:${DateUtil.pad(ee%60)}`}modify(ev,data,ev.recur?'one':'all');drag=null;render()});
function dayPop(k){let es=expand(DateUtil.parse(k),DateUtil.parse(k));layer.innerHTML=`<div class="popover"><div class="pop"><div class="actions"><b style="margin-right:auto">${k}</b><button class="btn" id="cancel">×</button></div>${es.map(chip).join('')||'<p>No events</p>'}</div></div>`}function toast(msg,undo){let x=document.createElement('div');x.className='toast';x.innerHTML=`${msg}<button>UNDO</button>`;document.body.append(x);let t=setTimeout(()=>x.remove(),5000);x.querySelector('button').onclick=()=>{clearTimeout(t);x.remove();undo()}}
render();
</script></body></html>
<!-- agent-meta {"model":"openai/gpt-5.6-terra-pro","provider":"openrouter","persona":"full","sessionId":"973ba93b-b740-4bf5-b95f-02ef96ddff7e","tokensIn":785276,"tokensOut":23892,"tokensTotal":809168,"cost":0.4372465,"turns":14,"toolCalls":13,"failedToolCalls":1,"timestamp":"2026-08-01T05:34:11.443Z"} -->