Reading activity grid in Settings: months, weeks, days, hours with drill-down

Active reader time is now kept per user in small UTC buckets (6 minutes for
120 days, then hours, dropped after three years), seeded once from the visits
the clock still remembers. Settings -> Reading activity shows them as a grid
whose cells glow with their total and carry a start-to-end strip; month/week
cells open their days, days open their hours. Finished chapters come from
progress and work without the clock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MaashkinoozheandClaude Opus 5.5 committed 2026-11-03 12:41:44 +01:00
1 parent c760141636
commit fe8956087e
15 files changed
+657 -4

No files matched your search

+44
View File
@@ -575,3 +575,47 @@ kbd { display: inline-block; min-width: 24px; padding: 1px 7px; border: 1px soli
background: var(--card2); font: 13px/1.5 ui-monospace, monospace; text-align: center; }
@media (max-width: 480px) { .moment-dialog { padding: 12px; } .moment-actions .moment-hint { flex-basis: 100%; } .moment-actions .moment-hint:empty { display: none; } }
.moment-extend { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-bottom: 8px; }
/* Reading activity grid (Settings → Reading activity). --g: cell glow 0…1, --a: strip segment glow 0…1. */
.activity-toolbar { display: flex; flex-wrap: wrap; gap: 8px; justify-content: space-between; }
.activity-nav { display: flex; align-items: center; gap: 8px; margin: 12px 0 4px; }
.activity-title { min-width: 11em; text-align: center; }
.activity-summary { margin: 6px 0 12px; }
.activity-grid { display: flex; flex-direction: column; gap: 6px; }
.activity-row { display: grid; grid-template-columns: 5.5em 1fr; gap: 8px; align-items: stretch; border-radius: 10px; }
.activity-row.marked { background: color-mix(in srgb, var(--accent) 12%, transparent); outline: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); }
.activity-row-label { color: var(--muted); font-size: 13px; align-self: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.activity-cells { display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); gap: 6px; }
.activity-head { color: var(--muted); font-size: 12px; text-align: center; }
.activity-cell { position: relative; display: flex; flex-direction: column; justify-content: space-between; gap: 6px; min-height: 58px; padding: 6px 7px 7px;
border: 1px solid var(--line); border-radius: 8px; color: var(--fg); font: inherit; text-align: left; cursor: pointer;
background: color-mix(in srgb, var(--accent) calc(var(--g) * 42%), var(--card2));
box-shadow: 0 0 calc(var(--g) * 12px) color-mix(in srgb, var(--accent) calc(var(--g) * 55%), transparent);
transition: transform .12s, border-color .12s; }
.activity-cell:hover, .activity-cell:focus-visible { border-color: var(--accent); transform: translateY(-1px); outline: none; }
.activity-cell.hot { border-color: color-mix(in srgb, var(--accent) 70%, #fff); }
.activity-cell.now { border-color: var(--fg); }
.activity-cell.outside { opacity: .38; }
.activity-cell.future { cursor: default; background: transparent; border-style: dashed; box-shadow: none; transform: none; }
.view-hour .activity-cell { cursor: default; }
.activity-label { font-size: 13px; font-weight: 600; line-height: 1.1; display: flex; flex-direction: column; }
.activity-label small { font-weight: 400; color: var(--muted); font-size: 11px; }
.activity-value { position: absolute; top: 6px; right: 7px; font-size: 11px; color: color-mix(in srgb, var(--fg) 80%, transparent); }
.view-day .activity-value, .view-month .activity-value { position: static; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.activity-strip { display: flex; height: 9px; border-radius: 3px; overflow: hidden; background: color-mix(in srgb, var(--bg) 70%, transparent); }
.activity-strip i { flex: 1; background: var(--accent); opacity: var(--a); }
.activity-strip i.ahead { background: transparent; }
.activity-strip i + i { box-shadow: inset 1px 0 0 color-mix(in srgb, var(--bg) 35%, transparent); }
.view-month .activity-strip i + i, .view-week .activity-strip i + i { box-shadow: none; }
.activity-tip { position: fixed; z-index: 50; max-width: 300px; padding: 10px 12px; border-radius: 10px; pointer-events: none;
background: var(--card); border: 1px solid var(--line); box-shadow: 0 8px 24px rgba(0, 0, 0, .45); font-size: 13px; line-height: 1.45; }
.activity-tip ul { margin: 4px 0 0; padding-left: 16px; }
.activity-legend { margin-top: 12px; }
@media (max-width: 760px) {
.activity-row { grid-template-columns: 1fr; gap: 3px; }
.activity-row.head .activity-row-label { display: none; }
.view-month .activity-cells { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.activity-cell { min-height: 48px; padding: 5px; }
.activity-value { display: none; }
.activity-cells { gap: 4px; }
}