diff --git a/frontend/css/app.css b/frontend/css/app.css
index e3f6e06..403fb09 100644
--- a/frontend/css/app.css
+++ b/frontend/css/app.css
@@ -302,6 +302,10 @@ a { color: var(--primary); text-decoration: none; }
.color-swatch.active { border-color: var(--text-1); }
.ev-color-row { display: flex; align-items: center; gap: 8px; }
+.ev-reminders-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 6px; }
+.ev-reminder-row { display: flex; align-items: center; gap: 8px; }
+.ev-reminder-row select { flex: 1; }
+.ev-reminder-remove { flex-shrink: 0; }
.ev-color-hex {
flex: 1; height: 36px; padding: 0 10px;
background: var(--bg-hover); border: 1px solid var(--border);
@@ -539,6 +543,11 @@ a { color: var(--primary); text-decoration: none; }
width and only truncates while the button is visible. */
.cal-item-remove { display: none; }
.cal-item:hover .cal-item-remove { display: inline-flex; }
+/* Reminder bell: only shown on hover when enabled, but always shown (dimmed)
+ when disabled so the muted state is visible at a glance. */
+.cal-item-bell { display: none; flex-shrink: 0; }
+.cal-item:hover .cal-item-bell { display: inline-flex; }
+.cal-item-bell.off { display: inline-flex; color: var(--text-3); opacity: .75; }
/* ── Month View ─────────────────────────────────────────── */
.month-view { display: flex; flex-direction: column; flex: 1; min-height: 0; }
@@ -574,7 +583,7 @@ a { color: var(--primary); text-decoration: none; }
}
.month-col:last-child { border-right: none; }
.month-col:hover { background: var(--bg-hover); }
-.month-col.today { background: rgba(66,133,244,.08); }
+.month-col.today { background: color-mix(in srgb, var(--today-color) 10%, transparent); }
.month-col.month-selected { background: var(--primary-dim); }
.month-col.month-selected .cell-day { border: 2px solid var(--primary); color: var(--primary); font-weight: 700; }
.month-col.month-selected .cell-day.today { background: var(--today-color); color: #fff; border: none; }
diff --git a/frontend/index.html b/frontend/index.html
index 49f0b92..9a91336 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -364,6 +364,11 @@
+
`
).join('');
@@ -825,6 +836,37 @@ function renderCalendarList() {
});
});
+ // ── Reminder bell handlers (toggle reminders_enabled per calendar) ──
+ container.querySelectorAll('.cal-item-bell').forEach(btn => {
+ btn.addEventListener('click', async e => {
+ e.stopPropagation();
+ const source = btn.dataset.source;
+ const id = parseInt(btn.dataset.calId || btn.dataset.subId);
+ let cal = null;
+ if (source === 'caldav') {
+ for (const acc of state.accounts) { const c = acc.calendars.find(c => c.id === id); if (c) cal = c; }
+ } else if (source === 'local') {
+ cal = state.localCalendars.find(c => c.id === id);
+ } else if (source === 'ical') {
+ cal = state.icalSubscriptions.find(s => s.id === id);
+ } else if (source === 'google') {
+ for (const acc of state.googleAccounts) { const c = acc.calendars.find(c => c.id === id); if (c) cal = c; }
+ } else if (source === 'homeassistant') {
+ for (const acc of state.haAccounts) { const c = acc.calendars.find(c => c.id === id); if (c) cal = c; }
+ }
+ if (!cal) return;
+ const newVal = cal.reminders_enabled === false; // toggle
+ const path = source === 'ical'
+ ? `/ical/subscriptions/${id}`
+ : `/${source}/calendars/${id}`;
+ try {
+ await api.put(path, { reminders_enabled: newVal });
+ cal.reminders_enabled = newVal;
+ renderCalendarList();
+ } catch (err) { showToast(err.message, true); }
+ });
+ });
+
// ── Color dot handlers ─────────────────────────────────
container.querySelectorAll('.cal-item-dot').forEach(dot => {
dot.addEventListener('click', async e => {
@@ -1463,15 +1505,24 @@ function populateCalendarSelect(selectedId) {
sel.appendChild(opt);
});
});
- // Local calendars (group calendars marked with 👥 so group events can be
- // created from anywhere, not just the group view).
- state.localCalendars.filter(c => !c.sidebar_hidden).forEach(cal => {
+ // Local calendars. Group calendars are collected under a "Groups" optgroup
+ // (no emoji — a native