Calendar Week: Tab shifts day headers but events stay (state desync); sidebar Today does nothing; title says September on 1 Oct #609

Closed
opened 2026-10-01 09:38:56 +00:00 by kayg · 5 comments
Owner

Owner report (2026-10-01, Week view screenshot on 1af8ead26)

  1. "In the week view, I can hit Tab Tab Tab to select days in the future while the events don't change. They show the same events on the wrong days; basically state does not sync." The screenshot shows the column headers "Mon 5 … Sat 10" (October), with September's entries (Getting ready, On and off working…) under them.
  2. "When I hit 'Today' from that point on the left sidebar, nothing happens! Today on the top row does vertically scroll to the current time, but the top bar is still the (Tab Tab Tab) scrolled days!"
  3. "It still says September 2026 even though today is 1 October 2026!"
    Cause to confirm: keyboard focus moves (Tab) shift the visible day window (the header labels), but the event data and the title come from a different state (the route date/anchor), so they drift apart. "Today" resets one of them but not the other.
    Fix: one source of truth for the visible range:
  • The visible window (anchor date + span) lives in one store, synced to the URL (/calendar/week/<date>, DESIGN §33).
  • The header labels, the event layout, the title ("September 2026" vs "Sep – Oct 2026" for a week spanning months) and the mini month all read from it.
  • Tab only moves focus between focusable items/days and never shifts the window. Moving focus to a day outside the window scrolls the window by the same store update. Arrow-key day navigation updates the store.
  • Today (sidebar and top row) sets the store to today: the window jumps to the week containing today, scrolls to now, and focuses today.
  • The title spans months correctly ("Sep 27 – Oct 3, 2026"); on 1 Oct the default week view shows the week containing 1 Oct.
    Tests: e2e:
  • Tab ×N across days → the headers and events stay consistent (each event's date matches its column header);
  • sidebar Today and top-row Today both land on today's week and scroll to now;
  • the title is correct for a week spanning months;
  • a deep link to /calendar/week/2026-10-05 shows Oct 5–11 with October's events.
    A unit test for the store.
## Owner report (2026-10-01, Week view screenshot on 1af8ead26) 1. "In the week view, I can hit Tab Tab Tab to select days in the future while the events don't change. They show the same events on the wrong days; basically state does not sync." The screenshot shows the column headers "Mon 5 … Sat 10" (October), with September's entries (Getting ready, On and off working…) under them. 2. "When I hit 'Today' from that point on the left sidebar, nothing happens! Today on the top row does vertically scroll to the current time, but the top bar is still the (Tab Tab Tab) scrolled days!" 3. "It still says September 2026 even though today is 1 October 2026!" **Cause to confirm:** keyboard focus moves (Tab) shift the visible day window (the header labels), but the event data and the title come from a different state (the route date/anchor), so they drift apart. "Today" resets one of them but not the other. **Fix: one source of truth for the visible range:** - The visible window (anchor date + span) lives in one store, synced to the URL (`/calendar/week/<date>`, DESIGN §33). - The header labels, the event layout, the title ("September 2026" vs "Sep – Oct 2026" for a week spanning months) and the mini month all read from it. - Tab only moves focus between focusable items/days and never shifts the window. Moving focus to a day outside the window scrolls the window by the same store update. Arrow-key day navigation updates the store. - **Today** (sidebar and top row) sets the store to today: the window jumps to the week containing today, scrolls to now, and focuses today. - The title spans months correctly ("Sep 27 – Oct 3, 2026"); on 1 Oct the default week view shows the week containing 1 Oct. **Tests:** e2e: - Tab ×N across days → the headers and events stay consistent (each event's date matches its column header); - sidebar Today and top-row Today both land on today's week and scroll to now; - the title is correct for a week spanning months; - a deep link to `/calendar/week/2026-10-05` shows Oct 5–11 with October's events. A unit test for the store.
Author
Owner

Starting #609 on branch job/weekstate-609 at base SHA 1af8ead262daf0a4ad22a60564dc75bab0005cf4. The branch is currently three commits behind origin/dev; I will merge origin/dev once before final gates. I am tracing Calendar route state, Week grid navigation, Today actions, and existing browser/unit test and benchmark coverage.

Starting #609 on branch `job/weekstate-609` at base SHA `1af8ead262daf0a4ad22a60564dc75bab0005cf4`. The branch is currently three commits behind `origin/dev`; I will merge `origin/dev` once before final gates. I am tracing Calendar route state, Week grid navigation, Today actions, and existing browser/unit test and benchmark coverage.
Author
Owner

Inspection finding: Week state is split between the route date, the page's visible state, and TimeGrid's virtual scroll index (apps/web/src/routes/calendar/[view]/[date]/+page.svelte and packages/ui/src/components/calendar/TimeGrid.svelte). The page title calls viewTitle('week', shown), which currently formats only the anchor month (packages/ui/src/components/calendar/model.ts), so a week spanning September and October still reads “September 2026”. I am validating focus-driven scrolling and both Today actions with real Calendar data before finalizing the state fix.

Inspection finding: Week state is split between the route date, the page's `visible` state, and TimeGrid's virtual scroll index (`apps/web/src/routes/calendar/[view]/[date]/+page.svelte` and `packages/ui/src/components/calendar/TimeGrid.svelte`). The page title calls `viewTitle('week', shown)`, which currently formats only the anchor month (`packages/ui/src/components/calendar/model.ts`), so a week spanning September and October still reads “September 2026”. I am validating focus-driven scrolling and both Today actions with real Calendar data before finalizing the state fix.
Author
Owner

Finding on 2026-10-01: the real-server production E2E showed Tab focus on 2026-10-05 with Week headers 2026-10-09–15 above body columns 2026-10-05–11. The header clip used overflow: hidden, so focusing a buffered day scrolled the header's own overflow area while the body followed the shared grid scroll. Changing the header clip to overflow: clip removed that second scroll offset. The production E2E now passes the Tab range/data checks and captures the 12 required Week/Day screenshots at 390, 820, and 1440 px in light and dark themes.

Finding on 2026-10-01: the real-server production E2E showed Tab focus on 2026-10-05 with Week headers 2026-10-09–15 above body columns 2026-10-05–11. The header clip used `overflow: hidden`, so focusing a buffered day scrolled the header's own overflow area while the body followed the shared grid scroll. Changing the header clip to `overflow: clip` removed that second scroll offset. The production E2E now passes the Tab range/data checks and captures the 12 required Week/Day screenshots at 390, 820, and 1440 px in light and dark themes.
Author
Owner

#609 is complete. The visible Calendar window now has one anchor and span, shared by the Week/Day grid, title, MiniMonth, data loading, and /calendar/week/ URL. Today uses that window, scrolls to the current time, and focuses today. Cross-month Week titles include both endpoints. Tab focus no longer moves the header track independently of Events and Logs.

The production E2E confirmed the original failure: focusing 2026-10-05 showed headers 2026-10-09–15 over columns 2026-10-05–11. The header's overflow: hidden created a second scroll offset. overflow: clip fixed it, and the regression test now passes.

Files

  • apps/web/src/routes/calendar/[view]/[date]/+page.svelte
  • apps/web/src/lib/calendar/sidebarState.svelte.ts
  • apps/web/src/lib/calendar/window.test.ts
  • apps/web/src/lib/calendar/model.test.ts
  • apps/web/src/lib/components/SidebarLinks.svelte
  • apps/web/src/lib/components/app-sidebar.svelte
  • apps/web/e2e/weekstate-609.mjs
  • apps/web/package.json
  • packages/ui/src/components/calendar/window.ts
  • packages/ui/src/components/calendar/TimeGrid.svelte
  • packages/ui/src/components/calendar/MiniMonth.svelte
  • packages/ui/src/components/calendar/model.ts
  • packages/ui/src/time.ts
  • packages/ui/src/date.ts
  • packages/ui/src/index.ts
  • bench/calendar-weekstate-609.mjs
  • docs/perf/baseline.json

Verification

cargo fmt --check
(no output; exit 0)

cargo clippy -p calternal-plugin-mail --all-targets -- -D warnings
Finished dev profile [unoptimized + debuginfo] target(s) in 2m 48s

cargo test -p calternal-plugin-mail
39 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out.

bun run check
User browser caches use userStorage; only documented device/public-link exceptions remain.
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
svelte-check found 0 errors and 0 warnings

bun run test
Test Files  149 passed (149)
Tests  1013 passed (1013)
Duration  139.50s (transform 50%, environment 18%, import 17%, tests 11%, setup 3%)

bun run build
Wrote site to "build"
✔ done

bun run test:e2e:weekstate-609
weekstate: initial Week range, entries, and title passed
weekstate: ArrowRight range and URL passed
weekstate: Tab focus and range passed
weekstate: deep link range and entry passed
weekstate: sidebar Today passed
weekstate: top-row Today passed
calendar Week state e2e: focus, URL, data, both Today actions, cross-month title, and screenshot matrix passed

The merged Mail sync adversarial check also passed: test sync::tests::real_tls_provider_backfill ... ok and 1 passed; 0 failed; 0 ignored; 0 measured; 40 filtered out; finished in 25.32s.

Performance

bench/calendar-weekstate-609.mjs measured the 7-day window at p50 2.20 µs / p95 5.54 µs, 0.0097 CPU seconds, and 20,926,464 → 31,117,312 RSS bytes; the 2,000-update burst used 0.0027 CPU seconds. The 31-day window at p50 0.93 µs / p95 1.76 µs, 0.0261 CPU seconds, and 31,117,312 → 44,883,968 RSS bytes; the 10,000-update burst used 0.0253 CPU seconds. This was a local run because the perf-test lock was held. Local load average was 26.35, 29.40, 26.08. The new profile has no matching feature-specific baseline; the record is in docs/perf/baseline.json.

Screenshot attachments

All 12 Week/Day screenshots are attached to this issue. Light and dark captures cover 390, 820, and 1440 px.

Surface 390 px 820 px 1440 px
Week light / dark light / dark light / dark
Day light / dark light / dark light / dark

Decisions

  • A Week URL date is its first visible day; the window span is seven inclusive days. The Week title keeps the existing month/year form within one month and names both short month/day endpoints when the range crosses a month or year.
  • Today uses the User's configured week start. It places the now line about three hours below the grid top, clamped at the start of the day, then focuses today.
  • Focus on a day outside the active window updates the anchor and URL to that day, then smoothly scrolls the grid. This follows the owner keyboard-animation override.

Gaps

No known implementation gaps. Visual judgment is left to the orchestrator. The performance result is local and noisy, with no feature-specific comparison baseline.

Final head: 3c865174ee7d4d5b188b5ed0dedc5cabfd0a1315.

#609 is complete. The visible Calendar window now has one anchor and span, shared by the Week/Day grid, title, MiniMonth, data loading, and /calendar/week/<date> URL. Today uses that window, scrolls to the current time, and focuses today. Cross-month Week titles include both endpoints. Tab focus no longer moves the header track independently of Events and Logs. The production E2E confirmed the original failure: focusing 2026-10-05 showed headers 2026-10-09–15 over columns 2026-10-05–11. The header's `overflow: hidden` created a second scroll offset. `overflow: clip` fixed it, and the regression test now passes. ## Files - `apps/web/src/routes/calendar/[view]/[date]/+page.svelte` - `apps/web/src/lib/calendar/sidebarState.svelte.ts` - `apps/web/src/lib/calendar/window.test.ts` - `apps/web/src/lib/calendar/model.test.ts` - `apps/web/src/lib/components/SidebarLinks.svelte` - `apps/web/src/lib/components/app-sidebar.svelte` - `apps/web/e2e/weekstate-609.mjs` - `apps/web/package.json` - `packages/ui/src/components/calendar/window.ts` - `packages/ui/src/components/calendar/TimeGrid.svelte` - `packages/ui/src/components/calendar/MiniMonth.svelte` - `packages/ui/src/components/calendar/model.ts` - `packages/ui/src/time.ts` - `packages/ui/src/date.ts` - `packages/ui/src/index.ts` - `bench/calendar-weekstate-609.mjs` - `docs/perf/baseline.json` ## Verification ~~~~text cargo fmt --check (no output; exit 0) cargo clippy -p calternal-plugin-mail --all-targets -- -D warnings Finished dev profile [unoptimized + debuginfo] target(s) in 2m 48s cargo test -p calternal-plugin-mail 39 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out. bun run check User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. svelte-check found 0 errors and 0 warnings bun run test Test Files 149 passed (149) Tests 1013 passed (1013) Duration 139.50s (transform 50%, environment 18%, import 17%, tests 11%, setup 3%) bun run build Wrote site to "build" ✔ done bun run test:e2e:weekstate-609 weekstate: initial Week range, entries, and title passed weekstate: ArrowRight range and URL passed weekstate: Tab focus and range passed weekstate: deep link range and entry passed weekstate: sidebar Today passed weekstate: top-row Today passed calendar Week state e2e: focus, URL, data, both Today actions, cross-month title, and screenshot matrix passed ~~~~ The merged Mail sync adversarial check also passed: `test sync::tests::real_tls_provider_backfill ... ok` and `1 passed; 0 failed; 0 ignored; 0 measured; 40 filtered out; finished in 25.32s`. ## Performance `bench/calendar-weekstate-609.mjs` measured the 7-day window at p50 2.20 µs / p95 5.54 µs, 0.0097 CPU seconds, and 20,926,464 → 31,117,312 RSS bytes; the 2,000-update burst used 0.0027 CPU seconds. The 31-day window at p50 0.93 µs / p95 1.76 µs, 0.0261 CPU seconds, and 31,117,312 → 44,883,968 RSS bytes; the 10,000-update burst used 0.0253 CPU seconds. This was a local run because the perf-test lock was held. Local load average was 26.35, 29.40, 26.08. The new profile has no matching feature-specific baseline; the record is in `docs/perf/baseline.json`. ## Screenshot attachments All 12 Week/Day screenshots are attached to this issue. Light and dark captures cover 390, 820, and 1440 px. | Surface | 390 px | 820 px | 1440 px | |---|---|---|---| | Week | [light](https://git.kayg.org/attachments/a3a12c6a-6f19-47b6-83e1-fa2cbb37db7d) / [dark](https://git.kayg.org/attachments/641d750e-9934-4469-80ae-a9579b3d2931) | [light](https://git.kayg.org/attachments/e0590f84-28bc-49df-bbd2-093c454daae5) / [dark](https://git.kayg.org/attachments/aaa92594-e656-4bee-9721-ad3140424035) | [light](https://git.kayg.org/attachments/6a0e65f8-f1c3-44e3-9303-7f839f7564ed) / [dark](https://git.kayg.org/attachments/d3d9bac4-656a-430a-a76d-2476820c34e3) | | Day | [light](https://git.kayg.org/attachments/c1032845-3334-4de7-aff5-fbb13c51dd90) / [dark](https://git.kayg.org/attachments/8d95aead-1d17-4314-9325-88aa430775b6) | [light](https://git.kayg.org/attachments/03943c58-d33c-45af-89d7-9fbdead8d1c3) / [dark](https://git.kayg.org/attachments/b3346a57-568c-4b91-8aac-d6fb51cc3231) | [light](https://git.kayg.org/attachments/90d8a90e-f003-474c-b832-5bfb71a43438) / [dark](https://git.kayg.org/attachments/4f434a6c-dcd7-4fe0-b7dd-f27268398b32) | ## Decisions - A Week URL date is its first visible day; the window span is seven inclusive days. The Week title keeps the existing month/year form within one month and names both short month/day endpoints when the range crosses a month or year. - Today uses the User's configured week start. It places the now line about three hours below the grid top, clamped at the start of the day, then focuses today. - Focus on a day outside the active window updates the anchor and URL to that day, then smoothly scrolls the grid. This follows the owner keyboard-animation override. ## Gaps No known implementation gaps. Visual judgment is left to the orchestrator. The performance result is local and noisy, with no feature-specific comparison baseline. Final head: `3c865174ee7d4d5b188b5ed0dedc5cabfd0a1315`.
Author
Owner

Fixed in 58f5c8bed (merge-round-6, now in origin/dev); covered by apps/web/e2e/weekstate-609.mjs, the Calendar window/model tests, and the attached production screenshot matrix.

Fixed in 58f5c8bed (merge-round-6, now in origin/dev); covered by apps/web/e2e/weekstate-609.mjs, the Calendar window/model tests, and the attached production screenshot matrix.
kayg closed this issue 2026-10-03 12:48:25 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#609
No description provided.