Add optional time ranges and a calendar day view

Tasks can now have a start/end time on their deadline date. The new
Calendar view shows a day as a timeline (with a week strip and projected
repeats of recurring tasks), and the task form lists what is already
planned that day and flags overlaps. Existing databases are migrated to
schema v2 automatically, with a backup copy taken first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
goyban
2026-10-07 12:08:09 +02:00
parent c5b049edba
commit 509b1a1320
8 changed files with 447 additions and 19 deletions
+65
View File
@@ -235,3 +235,68 @@ dialog::backdrop { background: rgba(10, 12, 20, 0.5); }
.row .field.narrow { flex: 0 0 80px; }
.dialog-body { padding: 16px; }
}
/* Times in the task dialog */
input:disabled { opacity: 0.5; }
.day-preview { background: var(--panel-2); border-radius: 8px; padding: 10px 12px; margin-top: -4px; font-size: 14px; }
.day-head { font-weight: 600; margin-bottom: 4px; color: var(--muted); }
.day-head.clash { color: var(--today); }
.day-preview ul { list-style: none; margin: 0; padding: 0; }
.day-preview li { padding: 2px 0; }
.day-preview li.clash { color: var(--today); font-weight: 600; }
.day-time { font-variant-numeric: tabular-nums; color: var(--muted); }
.day-preview li.clash .day-time { color: inherit; }
/* Calendar */
.cal-nav { display: flex; gap: 8px; padding: 0 24px 8px; align-items: center; }
.cal-nav input[type="date"] { max-width: 180px; }
.week-strip { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; margin-top: 6px; }
.week-day {
display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 6px 0;
background: var(--panel); border: 1px solid var(--border); border-radius: 10px; cursor: pointer;
}
.week-day:hover { border-color: var(--accent); }
.week-day .wd { font-size: 12px; color: var(--muted); }
.week-day .dn { font-size: 17px; font-weight: 600; }
.week-day .dots { font-size: 12px; line-height: 1; min-height: 12px; color: var(--accent); letter-spacing: 1px; }
.week-day.today .dn { color: var(--accent); }
.week-day.active { background: var(--accent); border-color: var(--accent); }
.week-day.active .wd, .week-day.active .dn, .week-day.active .dots { color: var(--accent-text); }
.timeline {
position: relative; background: var(--panel); border: 1px solid var(--border);
border-radius: var(--radius); overflow: hidden;
}
.hour { position: absolute; left: 0; right: 0; border-top: 1px solid var(--border); cursor: copy; }
.hour:first-child { border-top: none; }
.hour:hover { background: var(--panel-2); }
.hour-label {
position: absolute; left: 8px; top: 3px; font-size: 11px; color: var(--muted);
font-variant-numeric: tabular-nums;
}
.block {
position: absolute; display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
overflow: hidden; text-align: left; padding: 3px 7px; margin-top: 1px; cursor: pointer;
background: var(--accent-weak); border: 1px solid var(--border); border-left: 4px solid var(--accent);
border-radius: 6px; font-size: 13px; line-height: 1.25;
}
.block:hover { filter: brightness(0.97); border-color: var(--accent); }
.block.short { flex-direction: row; align-items: center; gap: 6px; white-space: nowrap; }
.block-time { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; flex: none; }
.block-title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.block.short .block-title { white-space: nowrap; }
.block.done { opacity: 0.55; }
.block.done .block-title { text-decoration: line-through; }
.block.projected { background: transparent; border-style: dashed; border-left-style: solid; }
.now-line { position: absolute; left: 48px; right: 0; height: 2px; background: var(--overdue); pointer-events: none; }
.now-line::before {
content: ""; position: absolute; left: -4px; top: -3px; width: 8px; height: 8px;
border-radius: 50%; background: var(--overdue);
}
@media (max-width: 800px) {
.cal-nav { padding: 0 16px 8px; }
.cal-nav input[type="date"] { max-width: none; flex: 1; }
.week-strip { gap: 4px; }
}
.block.short .block-time { order: 2; }