Manual per-calendar color choice for calendar mode
Build and push server image / build-and-push (push) Successful in 49s

Each linked person can pin one of the panel's four non-black/white
colors (Yellow/Red/Blue/Green) to their own calendar instead of relying
on calendar_render.py's old auto-cycle-by-owner-name order -- owner-only,
like adding a calendar in the first place. Colors resolve against
whichever palette a frame actually renders with (including a custom
Advanced configuration override), so a pinned "Blue" stays this frame's
actual blue. Event color bars/dots are also bigger and rounded now
across agenda/week/month views, easier to tell apart at a glance.
This commit is contained in:
2026-07-22 22:30:55 -04:00
parent ffce798754
commit 27cd6b3703
10 changed files with 190 additions and 41 deletions
+27
View File
@@ -52,6 +52,33 @@ document.querySelectorAll('.calendar-toggle').forEach((el) => {
});
});
// Per-calendar color pin -- owner-only (the server enforces it; these
// buttons only ever render for the viewer's own calendars anyway, see
// frame_calendar.html). Clicking the currently-selected swatch again has
// no special "toggle off" behavior -- use the explicit Auto button.
document.querySelectorAll('.calendar-color-picker').forEach((picker) => {
const key = picker.dataset.key;
picker.querySelectorAll('.color-swatch').forEach((btn) => {
btn.addEventListener('click', async () => {
const colorIndex = btn.dataset.index === '' ? null : Number(btn.dataset.index);
try {
const resp = await fetch(`${window.FRAME_API}/calendar-color`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ calendar_key: key, color_index: colorIndex }),
});
if (!resp.ok) throw new Error(await apiError(resp));
picker.querySelectorAll('.color-swatch').forEach((el) => el.classList.remove('selected'));
btn.classList.add('selected');
showStatus(true, 'Color saved.');
loadCalendarPreview();
} catch (e) {
showStatus(false, e.message);
}
});
});
});
document.getElementById('weather-config-form').addEventListener('submit', async (e) => {
e.preventDefault();
const body = new URLSearchParams({
+13
View File
@@ -265,6 +265,19 @@ input:focus, select:focus {
.calendar-user-list > li { margin-top: 14px; }
.calendar-user-list > li:first-child { margin-top: 0; }
.calendar-user-name { margin: 0; font-size: 13px; font-weight: 600; color: var(--text); }
.calendar-row { flex-wrap: wrap; }
.calendar-color-picker { display: inline-flex; align-items: center; gap: 5px; margin-left: 8px; }
.color-swatch {
width: 20px; height: 20px; padding: 0; margin: 0;
border: 2px solid var(--border); border-radius: 5px;
box-shadow: none; cursor: pointer;
}
.color-swatch.selected { border-color: var(--text); box-shadow: 0 0 0 1.5px var(--text); }
.color-swatch-auto {
width: auto; height: 20px; padding: 0 6px; font-size: 10px; font-weight: 600;
color: var(--text-muted); background: var(--surface-alt);
}
.color-swatch-auto.selected { color: var(--text); }
button {
margin-top: 20px;