diff --git a/lib/folk-calendar.ts b/lib/folk-calendar.ts
index 95f8b0d..60ed8ae 100644
--- a/lib/folk-calendar.ts
+++ b/lib/folk-calendar.ts
@@ -350,7 +350,8 @@ export class FolkCalendar extends FolkShape {
const prevMonthLastDay = new Date(year, month, 0).getDate();
for (let i = startPadding - 1; i >= 0; i--) {
const day = prevMonthLastDay - i;
- html += `
${dayEvents.length === 0 ? `
No events
` :
dayEvents.sort((a, b) => a.start_time.localeCompare(b.start_time)).map(e => {
@@ -1866,6 +1869,70 @@ class FolkCalendarView extends HTMLElement {
setTimeout(() => document.addEventListener("click", closeHandler), 100);
}
+ private showDayAddForm(dateStr: string) {
+ // Remove any existing add-form
+ this.shadow.querySelector(".dd-add-form")?.remove();
+
+ const friendlyDate = new Date(dateStr + "T12:00:00").toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric" });
+ const form = document.createElement("div");
+ form.className = "dd-add-form";
+ form.innerHTML = `
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ const detail = this.shadow.querySelector(".day-detail");
+ if (!detail) return;
+ detail.appendChild(form);
+
+ const titleInput = form.querySelector(".dd-add-title") as HTMLInputElement;
+ titleInput.focus();
+
+ const createReminder = async (hour: number, minute = 0) => {
+ const title = titleInput.value.trim();
+ if (!title) { titleInput.focus(); titleInput.style.borderColor = "#ef4444"; return; }
+ form.remove();
+ const remindAt = new Date(`${dateStr}T${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}:00`).getTime();
+ const base = this.getScheduleApiBase();
+ try {
+ await fetch(`${base}/api/reminders`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ title, remindAt, allDay: false, syncToCalendar: true }),
+ });
+ if (this.space === "demo") { this.loadDemoData(); } else { this.loadMonth(); }
+ } catch (err) {
+ console.error("[rCal] Failed to create reminder:", err);
+ }
+ };
+
+ // Quick-pick time buttons
+ form.querySelectorAll
(".dd-add-time[data-hour]").forEach((btn) => {
+ btn.addEventListener("click", () => createReminder(parseInt(btn.dataset.hour!)));
+ });
+
+ // Custom time + submit
+ form.querySelector(".dd-add-submit")?.addEventListener("click", () => {
+ const input = form.querySelector(".dd-add-time-input") as HTMLInputElement;
+ const [h, m] = (input.value || "09:00").split(":").map(Number);
+ createReminder(h, m);
+ });
+
+ // Enter key in title → use 9 AM default
+ titleInput.addEventListener("keydown", (e) => {
+ if (e.key === "Enter") createReminder(9);
+ });
+ }
+
startTour() { this._tour.start(); }
// ── Attach Listeners ──
@@ -2178,6 +2245,14 @@ class FolkCalendarView extends HTMLElement {
e.stopPropagation(); this.expandedDay = ""; this.render();
});
+ // Add reminder from day detail
+ $("dd-add")?.addEventListener("click", (e) => {
+ e.stopPropagation();
+ const dateStr = (e.target as HTMLElement).dataset.addDate;
+ if (!dateStr) return;
+ this.showDayAddForm(dateStr);
+ });
+
// Modal close
$("modal-overlay")?.addEventListener("click", (e) => {
if ((e.target as HTMLElement).id === "modal-overlay") { this.selectedEvent = null; this.render(); }
@@ -2644,7 +2719,20 @@ class FolkCalendarView extends HTMLElement {
.day-detail { grid-column: 1 / -1; background: var(--rs-bg-surface); border: 1px solid var(--rs-bg-surface-raised); border-radius: 8px; padding: 12px; }
.dd-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.dd-date { font-size: 14px; font-weight: 600; color: var(--rs-text-primary); }
+ .dd-header-actions { display: flex; gap: 4px; align-items: center; }
+ .dd-add { background: none; border: 1px solid var(--rs-border-strong, #444); color: var(--rs-primary-hover, #818cf8); font-size: 18px; cursor: pointer; padding: 2px 8px; border-radius: 6px; line-height: 1; }
+ .dd-add:hover { background: var(--rs-bg-hover); }
.dd-close { background: none; border: none; color: var(--rs-text-muted); font-size: 18px; cursor: pointer; padding: 4px 8px; }
+ .dd-add-form { margin-top: 8px; padding: 10px; background: var(--rs-bg-surface-raised, #2a2a3e); border-radius: 8px; border: 1px solid var(--rs-border-strong, #444); }
+ .dd-add-title { width: 100%; padding: 8px; border-radius: 6px; border: 1px solid var(--rs-border-strong, #444); background: var(--rs-bg-surface, #1e1e2e); color: var(--rs-text-primary, #e0e0e0); font-size: 13px; margin-bottom: 8px; box-sizing: border-box; }
+ .dd-add-title:focus { outline: none; border-color: var(--rs-primary-hover, #818cf8); }
+ .dd-add-times { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-bottom: 8px; }
+ .dd-add-time { padding: 6px; border-radius: 6px; border: 1px solid var(--rs-border-strong, #444); background: var(--rs-bg-surface, #1e1e2e); color: var(--rs-text-primary, #e0e0e0); cursor: pointer; font-size: 12px; text-align: center; }
+ .dd-add-time:hover { border-color: var(--rs-primary-hover, #818cf8); background: var(--rs-bg-hover); }
+ .dd-add-custom { display: flex; gap: 6px; }
+ .dd-add-time-input { flex: 1; padding: 6px 8px; border-radius: 6px; border: 1px solid var(--rs-border-strong, #444); background: var(--rs-bg-surface, #1e1e2e); color: var(--rs-text-primary, #e0e0e0); font-size: 12px; }
+ .dd-add-submit { padding: 6px 12px; border-radius: 6px; border: 1px solid var(--rs-primary-hover, #818cf8); background: var(--rs-primary-hover, #818cf8); color: #fff; cursor: pointer; font-size: 12px; font-weight: 500; }
+ .dd-add-submit:hover { opacity: 0.9; }
.dd-event { display: flex; gap: 8px; align-items: flex-start; padding: 8px; border-radius: 6px; margin-bottom: 4px; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.dd-event:hover { background: var(--rs-bg-hover); }
.dd-color { width: 4px; border-radius: 2px; align-self: stretch; flex-shrink: 0; }