Calendar
GPT 5.6 Luna Pro · typescript

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 27,754 bytes · SHA-256 d2f76b95a63a
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Chronicle — Calendar & Event Planner</title>
<style>
/* ── css: base ── */
:root{--ink:#202735;--muted:#8790a2;--line:#e8ebf0;--paper:#fff;--bg:#f6f7fa;--accent:#5967d9;--accent-soft:#eef0ff;--coral:#e97863;--teal:#36a99b;--gold:#e8ad4b}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px Arial,sans-serif}button{font:inherit;color:inherit;border:0;background:none;cursor:pointer}.app-shell{min-height:100vh;display:flex;flex-direction:column}.topbar{height:78px;background:var(--paper);border-bottom:1px solid var(--line);display:flex;align-items:center;padding:0 28px;justify-content:space-between}.brand{display:flex;align-items:center;gap:11px}.brand-mark{width:35px;height:35px;border-radius:11px;background:var(--accent);color:white;display:grid;place-items:center;font-size:20px;box-shadow:0 5px 14px #5967d94a}.brand-name{font:bold 20px Georgia,serif;letter-spacing:.2px}.brand-sub{color:var(--muted);font-size:11px;margin-top:1px}.toolbar{display:flex;align-items:center;gap:8px}.ghost-btn,.date-label{padding:9px 14px;border:1px solid var(--line);border-radius:9px;background:white;font-weight:600}.ghost-btn:hover,.icon-btn:hover,.settings-btn:hover{background:var(--accent-soft)}.icon-btn{font-size:25px;color:var(--muted);width:32px}.date-label{border:0;font-size:17px;margin:0 15px;min-width:145px}.view-switch{background:#f2f3f7;padding:3px;border-radius:9px;display:flex}.view-btn{padding:7px 13px;border-radius:7px;color:var(--muted);font-size:13px}.view-btn.active{background:white;color:var(--accent);box-shadow:0 1px 4px #20273512;font-weight:700}.settings-btn{font-size:18px;color:var(--muted);padding:8px;margin-left:10px}.workspace{display:flex;flex:1;min-height:0}.sidebar{width:245px;padding:28px 21px;background:#fbfcfd;border-right:1px solid var(--line)}.new-event{background:var(--accent);color:#fff;border-radius:11px;padding:12px 18px;width:140px;font-weight:700;box-shadow:0 7px 14px #5967d935;margin-bottom:29px}.new-event span{font-size:19px;vertical-align:-1px;margin-right:4px}.mini-card{padding:0 5px}.mini-head{display:flex;justify-content:space-between;margin-bottom:17px;font-size:13px}.mini-head span{word-spacing:9px;color:var(--muted)}.mini-weekdays,.mini-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:5px;text-align:center}.mini-weekdays{color:#a3aaba;font-size:10px;margin-bottom:9px}.mini-grid button{font-size:11px;height:25px;border-radius:50%}.mini-grid button.dim{color:#c6cbd5}.mini-grid button.today{background:var(--accent);color:white;font-weight:bold}.legend{border-top:1px solid var(--line);margin-top:28px;padding-top:20px;display:grid;gap:12px}.legend-title{font-size:12px;font-weight:bold;color:#6d7585;margin-bottom:4px}.legend label{font-size:12px;color:#687184}.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:9px}.work{background:var(--accent)}.personal{background:var(--coral)}.focus{background:var(--teal)}.tip-card{display:flex;gap:10px;background:#f0f1ff;color:#5b65aa;padding:13px 11px;border-radius:10px;margin-top:30px;font-size:15px}.tip-card b{display:block;font-size:11px}.tip-card small{display:block;font-size:10px;line-height:1.4;margin-top:3px}.calendar-area{flex:1;min-width:0;padding:23px 26px 28px;display:flex;flex-direction:column}.calendar-header{height:47px;display:flex;align-items:flex-start;justify-content:space-between}.calendar-header h1{font:600 27px Georgia,serif;margin:0}.calendar-header p{margin:7px 0 0;color:var(--muted);font-size:12px}.calendar{background:white;border:1px solid var(--line);border-radius:12px;overflow:hidden;flex:1;min-height:560px;box-shadow:0 4px 18px #27314b08}.month-view{height:100%;display:grid;grid-template-rows:37px repeat(6,minmax(80px,1fr))}.weekday-row,.week-row{display:grid;grid-template-columns:repeat(7,1fr)}.weekday-row{background:#fbfcfe;border-bottom:1px solid var(--line);align-items:center;text-align:center;color:#a0a8b7;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px}.day-cell{border-right:1px solid var(--line);border-bottom:1px solid var(--line);padding:8px 8px 5px;min-width:0;position:relative;transition:background .15s}.day-cell:nth-child(7n){border-right:0}.day-cell:hover{background:#fafbff}.day-cell.dim .day-number{color:#c8cdd7}.day-number{display:inline-grid;place-items:center;width:25px;height:25px;font-size:12px;font-weight:600}.day-cell.today .day-number{background:var(--accent);color:white;border-radius:50%;box-shadow:0 3px 6px #5967d94a}.events{margin-top:5px;display:grid;gap:3px}.event-chip{border-left:3px solid var(--accent);background:#eef0ff;border-radius:4px;padding:4px 6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:11px;font-weight:600;cursor:grab}.event-chip:active{cursor:grabbing}.event-chip.coral{border-color:var(--coral);background:#fff0ed}.event-chip.teal{border-color:var(--teal);background:#eaf8f5}.event-chip.gold{border-color:var(--gold);background:#fff7e7}.more{font-size:10px;color:var(--accent);padding:3px 5px;cursor:pointer}.week-view{height:100%;display:flex;flex-direction:column}.week-days{display:grid;grid-template-columns:56px repeat(7,1fr);border-bottom:1px solid var(--line);height:54px}.week-days>div{border-right:1px solid var(--line);text-align:center;padding:9px;color:var(--muted);font-size:11px}.week-days b{display:block;font-size:16px;color:var(--ink);margin-top:3px}.week-days .today b{color:var(--accent)}.all-day{display:grid;grid-template-columns:56px repeat(7,1fr);min-height:34px;border-bottom:1px solid var(--line)}.all-day>div{border-right:1px solid var(--line);padding:4px}.all-label{font-size:10px;color:var(--muted);padding-top:8px!important;text-align:center}.time-grid{display:grid;grid-template-columns:56px repeat(7,1fr);overflow:auto;flex:1}.time-labels{position:relative}.time-label{height:60px;text-align:right;padding:0 9px 0 0;color:#a1a8b7;font-size:10px;transform:translateY(-6px)}.hour-col{position:relative;border-right:1px solid var(--line);background:repeating-linear-gradient(to bottom,transparent 0,transparent 59px,var(--line) 59px,var(--line) 60px)}.hour-col:last-child{border:0}.week-event{position:absolute;left:4px;right:4px;border-radius:5px;padding:5px 6px;font-size:11px;font-weight:600;border-left:3px solid var(--accent);background:#eef0ff;overflow:hidden;cursor:grab;z-index:2}.week-event.coral{border-color:var(--coral);background:#fff0ed}.week-event.teal{border-color:var(--teal);background:#eaf8f5}.week-event.gold{border-color:var(--gold);background:#fff7e7}.now-line{position:absolute;height:2px;background:var(--coral);left:0;right:0;z-index:4}.now-line:before{content:'';width:7px;height:7px;border-radius:50%;background:var(--coral);position:absolute;left:-3px;top:-3px}.agenda{padding:8px 24px;overflow:auto}.agenda-day{padding:18px 0;border-bottom:1px solid var(--line)}.agenda-date{font-weight:700;color:var(--accent);font-size:13px;margin-bottom:10px}.agenda-event{display:flex;gap:15px;align-items:center;padding:11px 4px;cursor:pointer}.agenda-time{color:var(--muted);font-size:11px;width:74px}.agenda-bar{width:4px;height:32px;border-radius:3px;background:var(--accent)}.agenda-info b{display:block}.agenda-info small{color:var(--muted)}.empty{color:var(--muted);text-align:center;padding:70px}.modal-backdrop{position:fixed;inset:0;background:#1d253b55;display:grid;place-items:center;z-index:10;animation:fade .18s}.modal{width:min(475px,calc(100% - 32px));background:white;border-radius:16px;padding:25px;box-shadow:0 20px 60px #1d253b35}.modal h2{font:600 24px Georgia,serif;margin:0 0 20px}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:13px}.field{display:grid;gap:6px}.field.full{grid-column:1/-1}.field label{font-size:11px;color:var(--muted);font-weight:700}.field input,.field textarea,.field select{border:1px solid #dfe3eb;border-radius:7px;padding:10px;font:inherit;outline:none}.field input:focus,.field textarea:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}.field textarea{resize:vertical;min-height:65px}.check{display:flex;gap:8px;align-items:center;color:#687184;font-size:12px}.palette{display:flex;gap:8px}.swatch{width:24px;height:24px;border-radius:50%;border:3px solid white;box-shadow:0 0 0 1px #d4d8e1}.swatch.selected{box-shadow:0 0 0 2px var(--ink)}.error{color:#d5544a;font-size:11px;min-height:15px}.modal-actions{display:flex;justify-content:space-between;align-items:center;margin-top:18px}.primary{background:var(--accent);color:#fff;border-radius:8px;padding:10px 18px;font-weight:bold}.danger{color:#d5544a;font-size:12px}.cancel{padding:10px 14px;color:var(--muted)}.recurrence{border-top:1px solid var(--line);margin-top:5px;padding-top:14px}.recurrence-row{display:flex;gap:8px;align-items:center}.weekday-picks{display:flex;gap:4px;margin-top:8px}.weekday-picks button{width:25px;height:25px;border-radius:50%;background:#f0f1f5;font-size:10px}.weekday-picks button.on{background:var(--accent);color:#fff}@keyframes fade{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%) translateY(20px);opacity:0;background:#252d3e;color:white;padding:11px 16px;border-radius:8px;font-size:12px;transition:.25s;z-index:20}.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}@media(max-width:850px){.sidebar{display:none}.topbar{padding:0 14px}.brand-sub{display:none}.toolbar{gap:2px}.date-label{margin:0 3px;min-width:110px}.calendar-area{padding:16px 10px}.day-cell{padding:5px 4px}.event-chip{font-size:10px;padding:3px}.calendar-header h1{font-size:23px}}@media(max-width:600px){.view-btn{padding:7px 8px}.ghost-btn{padding:8px}.settings-btn{display:none}.calendar-header p{display:none}.month-view{grid-template-rows:32px repeat(6,minmax(65px,1fr))}.day-cell{min-height:65px}.day-number{width:22px;height:22px}.week-days{grid-template-columns:40px repeat(7,1fr)}.all-day,.time-grid{grid-template-columns:40px repeat(7,1fr)}.time-label{font-size:9px;padding-right:4px}.modal{padding:19px}.form-grid{gap:9px}}
</style>
</head>
<body>
<div class="app-shell"><header class="topbar"><div class="brand"><div class="brand-mark">✦</div><div><div class="brand-name">Chronicle</div><div class="brand-sub">Calendar & event planner</div></div></div><div class="toolbar"><button id="todayBtn" class="ghost-btn">Today</button><button id="prevBtn" class="icon-btn" aria-label="Previous">‹</button><button id="nextBtn" class="icon-btn" aria-label="Next">›</button><button id="jumpBtn" class="date-label">March 2025</button><div class="view-switch"><button data-view="month" class="view-btn active">Month</button><button data-view="week" class="view-btn">Week</button><button data-view="agenda" class="view-btn">Agenda</button></div><button id="settingsBtn" class="settings-btn">⚙</button></div></header><main class="workspace"><aside class="sidebar"><button id="newEventBtn" class="new-event"><span>+</span> New event</button><div class="mini-card"><div class="mini-head"><b id="miniMonth">March 2025</b><span id="miniNav">‹ ›</span></div><div class="mini-weekdays"><span>M</span><span>T</span><span>W</span><span>T</span><span>F</span><span>S</span><span>S</span></div><div id="miniGrid" class="mini-grid"></div></div><div class="legend"><div class="legend-title">Calendars</div><label><i class="dot work"></i>Work</label><label><i class="dot personal"></i>Personal</label><label><i class="dot focus"></i>Focus time</label></div><div class="tip-card"><span>✦</span><div><b>Plan with intention</b><small>Drag events to reschedule. Click any day to add.</small></div></div></aside><section class="calendar-area"><div id="calendarHeader" class="calendar-header"></div><div id="calendar" class="calendar"></div></section></main></div><div id="modalRoot"></div><div id="toast" class="toast"></div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/main.ts":{"./date-util":"src/date-util.ts"}};
function __require(id) {
if (__cache[id]) return __cache[id].exports;
var module = __cache[id] = { exports: {} };
var factory = __mods[id];
if (!factory) throw new Error("Module not found: " + id);
factory(module.exports, function (spec) {
var target = (__map[id] && __map[id][spec]) || spec;
return __require(target);
}, module);
return module.exports;
}
// ── module: src/main.ts ──
__mods["src/main.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.app = void 0;
const date_util_1 = require("./date-util");
// State and tunable calendar behavior
const COLORS = ['work', 'coral', 'teal', 'gold'];
const NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const initial = { view: 'month', cursor: (0, date_util_1.today)(), weekStart: 1, events: [] };
let state = (() => { try {
return JSON.parse(localStorage.getItem('chronicle') || 'null') || initial;
}
catch {
return initial;
} })();
const $ = (s) => document.querySelector(s);
const uid = () => String(Date.now() + Math.random());
const save = () => localStorage.setItem('chronicle', JSON.stringify(state));
const evs = (a, b) => state.events.flatMap(e => (0, date_util_1.expand)(e, a, b)).sort((x, y) => (x.date + x.start).localeCompare(y.date + y.start));
function mini(x) { const first = { y: x.y, m: x.m, d: 1 }, lead = (0, date_util_1.weekIndex)(first, state.weekStart); return Array.from({ length: 42 }, (_, i) => { const d = (0, date_util_1.addDays)(first, i - lead); return `<button class="${d.m !== x.m ? 'dim ' : ''}${(0, date_util_1.key)(d) === (0, date_util_1.key)((0, date_util_1.today)()) ? 'today' : ''}" data-mini="${(0, date_util_1.key)(d)}">${d.d}</button>`; }).join(''); }
function chip(e) { return `<div draggable="true" class="event-chip ${e.color}" data-id="${e.id}" data-occ="${e.occurrence}">${e.allDay ? 'All day · ' : e.start + ' · '}${e.title}</div>`; }
function month() { const x = { y: state.cursor.y, m: state.cursor.m, d: 1 }, lead = (0, date_util_1.weekIndex)(x, state.weekStart); let h = '<div class="month-view"><div class="weekday-row">'; for (let i = 0; i < 7; i++)
h += `<div>${NAMES[(i + state.weekStart) % 7]}</div>`; h += '</div>'; for (let r = 0; r < 6; r++) {
h += '<div class="week-row">';
for (let c = 0; c < 7; c++) {
const d = (0, date_util_1.addDays)(x, r * 7 + c - lead), list = evs(d, d);
h += `<div class="day-cell ${d.m !== x.m ? 'dim ' : ''}${(0, date_util_1.key)(d) === (0, date_util_1.key)((0, date_util_1.today)()) ? 'today' : ''}" data-date="${(0, date_util_1.key)(d)}"><span class="day-number">${d.d}</span><div class="events">${list.slice(0, 3).map(chip).join('')}${list.length > 3 ? `<div class="more">+${list.length - 3} more</div>` : ''}</div></div>`;
}
h += '</div>';
} return h + '</div>'; }
function week() { const ws = (0, date_util_1.weekStart)(state.cursor, state.weekStart), ds = Array.from({ length: 7 }, (_, i) => (0, date_util_1.addDays)(ws, i)), list = evs(ds[0], ds[6]); let h = '<div class="week-view"><div class="week-days"><div></div>'; ds.forEach(d => h += `<div class="${(0, date_util_1.key)(d) === (0, date_util_1.key)((0, date_util_1.today)()) ? 'today' : ''}">${NAMES[(0, date_util_1.weekday)(d)]}<b>${d.d}</b></div>`); h += '</div><div class="all-day"><div class="all-label">ALL-DAY</div>'; ds.forEach(d => h += `<div>${list.filter(e => e.date === (0, date_util_1.key)(d) && e.allDay).map(chip).join('')}</div>`); h += '</div><div class="time-grid"><div class="time-labels">'; for (let i = 6; i <= 22; i++)
h += `<div class="time-label">${String(i).padStart(2, '0')}:00</div>`; h += '</div>'; ds.forEach(d => { h += `<div class="hour-col" data-date="${(0, date_util_1.key)(d)}">`; list.filter(e => e.date === (0, date_util_1.key)(d) && !e.allDay).forEach(e => { const a = +e.start.slice(0, 2) * 60 + +e.start.slice(3), b = +e.end.slice(0, 2) * 60 + +e.end.slice(3); h += `<div draggable="true" class="week-event ${e.color}" data-id="${e.id}" data-occ="${e.occurrence}" style="top:${a - 360}px;height:${Math.max(18, b - a)}px">${e.start} ${e.title}</div>`; }); h += '</div>'; }); return h + '</div></div>'; }
function agenda() { const list = evs((0, date_util_1.today)(), (0, date_util_1.addDays)((0, date_util_1.today)(), 90)); if (!list.length)
return '<div class="agenda"><div class="empty">No upcoming events yet.<br>Start with a new plan.</div></div>'; let h = '<div class="agenda">', last = ''; list.forEach(e => { if (e.date !== last) {
if (last)
h += '</div>';
last = e.date;
h += `<div class="agenda-day"><div class="agenda-date">${(0, date_util_1.format)((0, date_util_1.parse)(e.date), { weekday: 'long', month: 'long', day: 'numeric' })}</div>`;
} h += `<div class="agenda-event" data-id="${e.id}"><div class="agenda-time">${e.allDay ? 'All day' : e.start + ' – ' + e.end}</div><i class="agenda-bar ${e.color}"></i><div class="agenda-info"><b>${e.title}</b><small>${e.notes || 'No notes'}</small></div></div>`; }); return h + '</div></div>'; }
function render() { save(); const title = (0, date_util_1.format)(state.cursor, { month: 'long', year: 'numeric' }); $('#miniMonth').textContent = title; $('#miniGrid').innerHTML = mini(state.cursor); $('.date-label').textContent = title; $('#calendarHeader').innerHTML = `<div><h1>${state.view === 'week' ? 'Week of ' : ''}${title}</h1><p>${state.view === 'agenda' ? 'Your upcoming plans, in order' : 'A clear view of your month'}</p></div>`; document.querySelectorAll('.view-btn').forEach(b => b.classList.toggle('active', b.dataset.view === state.view)); $('#calendar').innerHTML = state.view === 'month' ? month() : state.view === 'week' ? week() : agenda(); }
function editor(date, id) { const old = id ? state.events.find(e => e.id === id) : undefined, d = date || old?.date || (0, date_util_1.key)(state.cursor), e = old || { id: '', title: '', date: d, start: '09:00', end: '10:00', allDay: false, color: 'work', notes: '', recur: { type: 'none' }, exceptions: [] }; $('#modalRoot').innerHTML = `<div class="modal-backdrop"><div class="modal"><h2>${old ? 'Edit event' : 'New event'}</h2><form id="eventForm"><div class="form-grid"><div class="field full"><label>EVENT TITLE</label><input name="title" value="${e.title}" autofocus></div><div class="field"><label>DATE</label><input type="date" name="date" value="${d}"></div><div class="field"><label>COLOR</label><div class="palette">${COLORS.map(c => `<button type="button" class="swatch ${c} ${e.color === c ? 'selected' : ''}" data-color="${c}" style="background:var(--${c === 'work' ? 'accent' : c})"></button>`).join('')}</div></div><div class="field"><label>START</label><input type="time" name="start" value="${e.start}"></div><div class="field"><label>END</label><input type="time" name="end" value="${e.end}"></div><label class="check full"><input type="checkbox" name="allDay" ${e.allDay ? 'checked' : ''}> All-day event</label><div class="field full"><label>REPEAT</label><select name="repeat"><option value="none">Does not repeat</option><option value="daily">Daily</option><option value="weekly">Weekly</option><option value="monthly">Monthly</option><option value="yearly">Yearly</option></select></div><div class="field full"><label>NOTES</label><textarea name="notes">${e.notes}</textarea><div class="error" id="formError"></div></div></div><div class="modal-actions">${old ? '<button type="button" class="danger" id="deleteEvent">Delete event</button>' : '<span></span>'}<div><button type="button" class="cancel">Cancel</button><button class="primary">Save event</button></div></div></form></div></div>`; const form = $('#eventForm'); form.elements.namedItem('repeat').value = e.recur.type; let color = e.color; document.querySelectorAll('[data-color]').forEach(b => b.addEventListener('click', () => { color = b.dataset.color; document.querySelectorAll('.swatch').forEach(x => x.classList.remove('selected')); b.classList.add('selected'); })); $('.cancel').onclick = () => $('#modalRoot').innerHTML = ''; form.onsubmit = (ev) => { ev.preventDefault(); const f = new FormData(form), all = f.get('allDay') === 'on', start = String(f.get('start')), end = String(f.get('end')); if (!String(f.get('title')).trim())
return $('#formError').textContent = 'Please add a title.'; if (!all && end <= start)
return $('#formError').textContent = 'End time must be after start time.'; const r = { type: String(f.get('repeat')) }; if (old)
Object.assign(old, { title: String(f.get('title')), date: String(f.get('date')), start, end, allDay: all, notes: String(f.get('notes')), color, recur: r });
else
state.events.push({ id: uid(), title: String(f.get('title')), date: String(f.get('date')), start, end, allDay: all, color, notes: String(f.get('notes')), recur: r, exceptions: [] }); $('#modalRoot').innerHTML = ''; render(); toast('Event saved'); }; if (old)
$('#deleteEvent').onclick = () => { if (confirm('Delete this event?')) {
state.events = state.events.filter(x => x.id !== old.id);
$('#modalRoot').innerHTML = '';
render();
toast('Event deleted');
} }; }
function toast(s) { const t = $('#toast'); t.textContent = s; t.classList.add('show'); setTimeout(() => t.classList.remove('show'), 2000); }
function bind() { $('#newEventBtn').onclick = () => editor(); $('#todayBtn').onclick = () => { state.cursor = (0, date_util_1.today)(); render(); }; $('#prevBtn').onclick = () => { state.cursor = state.view === 'week' ? (0, date_util_1.addDays)(state.cursor, -7) : (0, date_util_1.addMonths)(state.cursor, -1); render(); }; $('#nextBtn').onclick = () => { state.cursor = state.view === 'week' ? (0, date_util_1.addDays)(state.cursor, 7) : (0, date_util_1.addMonths)(state.cursor, 1); render(); }; $('#settingsBtn').onclick = () => { state.weekStart = state.weekStart ? 0 : 1; render(); toast(state.weekStart ? 'Monday start' : 'Sunday start'); }; document.querySelectorAll('.view-btn').forEach(b => b.addEventListener('click', () => { state.view = b.dataset.view; render(); })); $('#miniGrid').onclick = e => { const d = e.target.dataset.mini; if (d) {
state.cursor = (0, date_util_1.parse)(d);
render();
} }; $('#calendar').onclick = e => { const t = e.target, q = t.closest('[data-id]'), c = t.closest('.day-cell'); if (q)
return editor(undefined, q.dataset.id); if (c)
editor(c.dataset.date); }; $('#calendar').ondragover = e => e.preventDefault(); $('#calendar').ondrop = e => { const t = e.target.closest('[data-date]'); if (!t)
return; try {
const q = JSON.parse(e.dataTransfer?.getData('text/plain') || '{}'), x = state.events.find(z => z.id === q.id);
if (x) {
x.date = t.dataset.date;
render();
toast('Event rescheduled');
}
}
catch { } }; $('#calendar').ondragstart = e => { const t = e.target; if (t.dataset.id)
e.dataTransfer?.setData('text/plain', JSON.stringify({ id: t.dataset.id })); }; }
bind();
render();
exports.app = { state, render };
};
// ── module: src/date-util.ts ──
__mods["src/date-util.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.dateUtil = exports.expand = exports.today = exports.format = exports.cmp = exports.weekStart = exports.addMonths = exports.addDays = exports.weekIndex = exports.weekday = exports.daysInMonth = exports.parse = exports.key = exports.pad = void 0;
const pad = (n) => String(n).padStart(2, '0');
exports.pad = pad;
const key = (x) => `${x.y}-${(0, exports.pad)(x.m)}-${(0, exports.pad)(x.d)}`;
exports.key = key;
const parse = (s) => { const [y, m, d] = s.split('-').map(Number); return { y, m, d }; };
exports.parse = parse;
const daysInMonth = (y, m) => m === 2 ? (y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0) ? 29 : 28) : [4, 6, 9, 11].includes(m) ? 30 : 31;
exports.daysInMonth = daysInMonth;
const weekday = (x) => new Date(Date.UTC(x.y, x.m - 1, x.d)).getUTCDay();
exports.weekday = weekday;
const weekIndex = (x, start = 1) => ((0, exports.weekday)(x) - start + 7) % 7;
exports.weekIndex = weekIndex;
const addDays = (x, n) => { let y = x.y, m = x.m, d = x.d; if (n >= 0) {
while (n--) {
d++;
if (d > (0, exports.daysInMonth)(y, m)) {
d = 1;
m++;
if (m > 12) {
m = 1;
y++;
}
}
}
}
else {
while (n++) {
d--;
if (d < 1) {
m--;
if (m < 1) {
m = 12;
y--;
}
d = (0, exports.daysInMonth)(y, m);
}
}
} return { y, m, d }; };
exports.addDays = addDays;
const addMonths = (x, n) => { let z = x.y * 12 + x.m - 1 + n, y = Math.floor(z / 12), m = z % 12 + 1; return { y, m, d: Math.min(x.d, (0, exports.daysInMonth)(y, m)) }; };
exports.addMonths = addMonths;
const weekStart = (x, start = 1) => (0, exports.addDays)(x, -(0, exports.weekIndex)(x, start));
exports.weekStart = weekStart;
const cmp = (a, b) => (0, exports.key)(a).localeCompare((0, exports.key)(b));
exports.cmp = cmp;
const format = (x, opts = { month: 'long', day: 'numeric', year: 'numeric' }) => new Intl.DateTimeFormat('en-US', opts).format(new Date(Date.UTC(x.y, x.m - 1, x.d)));
exports.format = format;
const today = () => { const n = new Date(); return { y: n.getFullYear(), m: n.getMonth() + 1, d: n.getDate() }; };
exports.today = today;
const expand = (e, from, to) => { const out = []; const start = (0, exports.parse)(e.date); let cur = start; const limit = 500; for (let i = 0; i < limit && (0, exports.cmp)(cur, to) <= 0; i++) {
if ((0, exports.cmp)(cur, from) >= 0 && (0, exports.cmp)(cur, to) <= 0 && !(e.exceptions || []).includes((0, exports.key)(cur)))
out.push({ ...e, date: (0, exports.key)(cur), occurrence: (0, exports.key)(cur), masterDate: e.date });
if (!e.recur || e.recur.type === 'none')
break;
if (e.recur.type === 'daily')
cur = (0, exports.addDays)(cur, 1);
else if (e.recur.type === 'weekly') {
let found = false;
for (let j = 1; j <= 7; j++) {
const q = (0, exports.addDays)(cur, j);
if ((e.recur.days || [(0, exports.weekday)(start)]).includes((0, exports.weekday)(q))) {
cur = q;
found = true;
break;
}
}
if (!found)
break;
}
else if (e.recur.type === 'monthly') {
let q = (0, exports.addMonths)(cur, 1);
if (start.d <= (0, exports.daysInMonth)(q.y, q.m))
cur = { y: q.y, m: q.m, d: start.d };
else
cur = { y: q.y, m: q.m, d: 1 };
while (cur.d !== start.d && (0, exports.cmp)(cur, to) <= 0)
cur = (0, exports.addMonths)(cur, 1);
}
else if (e.recur.type === 'yearly') {
cur = { y: cur.y + 1, m: start.m, d: start.d };
}
} return out; };
exports.expand = expand;
exports.dateUtil = { daysInMonth: exports.daysInMonth, weekday: exports.weekday, weekIndex: exports.weekIndex, addDays: exports.addDays, addMonths: exports.addMonths, weekStart: exports.weekStart, expand: exports.expand, key: exports.key, parse: exports.parse };
};
// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"openai/gpt-5.6-luna-pro","provider":"openrouter","persona":"typescript","sessionId":"6665bf3e-4d9b-447d-8a8d-12974c5a6c87","tokensIn":547099,"tokensOut":30463,"tokensTotal":577562,"turns":11,"toolCalls":15,"failedToolCalls":2,"timestamp":"2026-07-31T03:39:05.873Z"} -->