Calendar: double-click a Note/attachment card opens it directly #1115

Open
opened 2026-10-05 07:39:52 +00:00 by kayg · 6 comments
Owner

Owner report (2026-10-05, Calendar)

"I would expect double-clicking an attachment would directly take me to it, instead I have to wait for the hover preview."

Double-click (and Enter on keyboard focus; tap on touch) on a Note/attachment card in the Calendar grid opens the item directly (the Note in the editor, a file in its viewer), with no wait for the hover card. Single click keeps selection; hover card behaviour stays for pointer users. Applies to Day, Week, Month and Agenda.

## Owner report (2026-10-05, Calendar) "I would expect double-clicking an attachment would directly take me to it, instead I have to wait for the hover preview." Double-click (and Enter on keyboard focus; tap on touch) on a Note/attachment card in the Calendar grid opens the item directly (the Note in the editor, a file in its viewer), with no wait for the hover card. Single click keeps selection; hover card behaviour stays for pointer users. Applies to Day, Week, Month and Agenda.
Author
Owner

Starting work on branch job/calcards-1115, based on dev at 27644444a9. I will keep click selection, add direct double-click/Enter/tap activation, and preserve the existing hover preview.

Starting work on branch job/calcards-1115, based on dev at 27644444a91dad244073113b0ec8ec7a61f2a212. I will keep click selection, add direct double-click/Enter/tap activation, and preserve the existing hover preview.
Author
Owner

Evidence: Month showed only aggregate Notes/Photos/Files counts; it had no individual saved-item rows where #1115's direct-open behavior could apply. I made the summary open the shared saved-item list and routed stable Note/file identities directly on Enter, touch, or double-click. One pointer click still selects.

Evidence: Month showed only aggregate Notes/Photos/Files counts; it had no individual saved-item rows where #1115's direct-open behavior could apply. I made the summary open the shared saved-item list and routed stable Note/file identities directly on Enter, touch, or double-click. One pointer click still selects.
Author
Owner

Finding and fix: Month cells only exposed static saved-item counts, so there was no item list from which to open a Note or attachment. The activity summary now opens the shared Calendar item list; a single saved item opens on keyboard activation, touch, or double-click while one mouse click still selects it. Day/Week cards use the same stable-identity open path. Focused verification: bunx vitest run src/lib/calendar/model.test.ts src/lib/calendar/TimeGrid.svelte.test.ts src/lib/calendar/MonthGrid.svelte.test.ts --maxWorkers=2 passed (3 files, 72 tests); direct Svelte check found 0 errors and 4 existing warnings. The full bun run check reaches a stale perf-lint exception in apps/web/src/lib/components/app-sidebar.svelte and stops before Svelte diagnostics.

Finding and fix: Month cells only exposed static saved-item counts, so there was no item list from which to open a Note or attachment. The activity summary now opens the shared Calendar item list; a single saved item opens on keyboard activation, touch, or double-click while one mouse click still selects it. Day/Week cards use the same stable-identity open path. Focused verification: `bunx vitest run src/lib/calendar/model.test.ts src/lib/calendar/TimeGrid.svelte.test.ts src/lib/calendar/MonthGrid.svelte.test.ts --maxWorkers=2` passed (3 files, 72 tests); direct Svelte check found 0 errors and 4 existing warnings. The full `bun run check` reaches a stale perf-lint exception in `apps/web/src/lib/components/app-sidebar.svelte` and stops before Svelte diagnostics.
Author
Owner

#1115 browser finding: at 390 px, Week's seven 44 px lanes route standalone items through the existing +N control; the real Note row in the shared overflow sheet supports direct touch activation, while 820/1440 Week retains visible Cards. I updated the production flow to exercise those paths. The standalone PDF fixture must also stay distinct from a Journal-linked PDF because Calendar removes linked paths from the separate activity lane. Evidence is in the current production run under artifacts/calendar-preview-1116/.

#1115 browser finding: at 390 px, Week's seven 44 px lanes route standalone items through the existing +N control; the real Note row in the shared overflow sheet supports direct touch activation, while 820/1440 Week retains visible Cards. I updated the production flow to exercise those paths. The standalone PDF fixture must also stay distinct from a Journal-linked PDF because Calendar removes linked paths from the separate activity lane. Evidence is in the current production run under `artifacts/calendar-preview-1116/`.
Author
Owner

Production e2e found a Month-only double-click gap: the first pointer click selected a saved File and closed the overflow surface before the second click could open it. The row now keeps its selected state and aria-pressed value during the double-click interval, then the second click opens the stable Files identity. A focused Svelte test covers the two-click sequence; the real production flow passes Day/Week/Month/Agenda keyboard, double-click and touch paths. Evidence: artifacts/calendar-direct-open-1115/ (30 macOS screenshots, 390/820/1440 px, light/dark).

Production e2e found a Month-only double-click gap: the first pointer click selected a saved File and closed the overflow surface before the second click could open it. The row now keeps its selected state and `aria-pressed` value during the double-click interval, then the second click opens the stable Files identity. A focused Svelte test covers the two-click sequence; the real production flow passes Day/Week/Month/Agenda keyboard, double-click and touch paths. Evidence: `artifacts/calendar-direct-open-1115/` (30 macOS screenshots, 390/820/1440 px, light/dark).
Author
Owner

Forgejo #1115 completion

Branch: job/calcards-1115
Head: d079d908315ec3e1f928887588b77b42fb55fee9
Commits: c7ac9f75c, 4f222ac1d, 4a21474c7, f2c063b05, d079d9083. The latest origin/dev sync is merge commit 553f01936.

Built

Calendar Note and standalone File rows now open their stable item identity on Enter, touch tap, or double-click in Day, Week, Month and Agenda. One pointer click keeps selection. Month saved-item rows retain selection and aria-pressed during the double-click window, so their second click reaches the same row. File opens use the shared Quick Look.

The Month direct-open regression test covers first-click selection followed by second-click opening. The production e2e covers keyboard, double-click and touch activation with real Notes and Files data. It captured 30 macOS-rendered production screenshots: Day, Week, Month, Agenda and the Month saved-item list at 390, 820 and 1440 px in light and dark themes.

Files changed by the #1115 commits:

  • packages/ui/src/components/calendar/GridColumn.svelte
  • packages/ui/src/components/calendar/MonthGrid.svelte
  • packages/ui/src/components/calendar/MoreItemsPopover.svelte
  • packages/ui/src/components/calendar/TimeGrid.svelte
  • packages/ui/src/components/calendar/model.ts
  • packages/ui/src/components/ItemCard.svelte
  • apps/web/src/routes/calendar/[view]/[date]/+page.svelte
  • apps/web/src/lib/calendar/MonthGrid.svelte.test.ts
  • apps/web/src/lib/calendar/TimeGrid.svelte.test.ts
  • apps/web/src/lib/calendar/model.test.ts
  • apps/web/e2e/calendar-direct-open-1115.mjs
  • bench/calendar-direct-open-1115.mjs

UX gaps closed

  • Month's first click used to close the saved-item list before the second click. It now keeps the row mounted for 500 ms, keeps selection announced, and opens the stable Files identity on the second click.
  • Keyboard Enter and touch tap open Notes and Files directly. The context-menu path keeps stable Copy link actions.

UX gaps left: none found in the requested activation paths. The screenshot archive remains in the shared worktree at artifacts/calendar-direct-open-1115-macos.tar.gz (30 PNGs plus the profile JSON). The fj issue CLI exposes no file-upload command, so I could not attach it to this comment. The image files are also in artifacts/calendar-direct-open-1115/ for the visual reviewer.

Performance profile

One local production profile ran on calternal-dev; no matching baseline exists in docs/perf/baseline.json. Host load average was 32.8 before and 23.3 after.

  • 3-item Month list, 5 warm opens: p50 764.45 ms, p95/max 1331.66 ms; server mean/peak RSS 319/320 MB and mean/peak CPU 8.2/34.97%.
  • 1,000 Notes, 5 opens: p50 757.14 ms, p95/max 908.84 ms; server mean/peak RSS 636/647 MB and mean/peak CPU 78.85/291.89%.
  • Burst of 3 concurrent opens: 1345.87 ms wall time; per-open p50 1104.82 ms and p95/max 1126.86 ms.

The high recorded host load makes this a noisy local result. There is no baseline comparison or regression claim.

Decision not covered by DESIGN: keep a selected Month row mounted for a 500 ms second-click window; Enter and touch open immediately. The 1,000-item profile is opt-in through bench/calendar-direct-open-1115.mjs, so the ordinary e2e stays bounded.

Gates

bun run build passed from apps/web. Vite emitted existing unused-selector and module-directive warnings; the output ended with:

Compressed 885 static variants; saved 20905360 bytes.

Command: bunx vitest run src/lib/calendar/model.test.ts src/lib/calendar/TimeGrid.svelte.test.ts src/lib/calendar/MonthGrid.svelte.test.ts --maxWorkers=2.

Focused Vitest output:

 Test Files  3 passed (3)
      Tests  73 passed (73)

Command: bun e2e/calendar-direct-open-1115.mjs from apps/web, using the merged local production server binary. Production e2e output:

Calendar #1115: 390px light production screens passed
Calendar #1115: 390px dark production screens passed
Calendar #1115: 390px production screens passed
Calendar #1115: 820px light production screens passed
Calendar #1115: 820px dark production screens passed
Calendar #1115: 820px production screens passed
Calendar #1115: 1440px light production screens passed
Calendar #1115: 1440px dark production screens passed
Calendar #1115: 1440px production screens passed
Calendar #1115: Day Enter opens the stable Note identity
Calendar #1115: Day double-click opens the shared File Quick Look
Calendar #1115: Week Enter opens the stable Note identity
Calendar #1115: Week double-click opens the stable File Quick Look
Calendar #1115: Month saved-item Enter opens the stable Note identity
Calendar #1115: Month saved-item double-click opens the stable File Quick Look
Calendar #1115: Agenda attachment Enter opens the stable File Quick Look
Calendar #1115: Day touch tap opens the stable Note identity
Calendar #1115: phone Week overflow tap opens the stable Note identity
Calendar #1115: Month touch tap opens the stable Note identity
Calendar #1115: Agenda attachment touch tap opens the stable File Quick Look
Calendar #1115: 30 production screenshots saved under artifacts/calendar-direct-open-1115

Rust checks were run because restored paused #1116 work in this worktree contains Rust changes; the #1115 commits do not change Rust. cargo fmt --check exited 0 with no output. Command cargo clippy -p calternal-server --all-targets -- -D warnings passed:

Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 00s

Command cargo test -p calternal-server was not clean on the worktree with paused #1116 changes:

test result: FAILED. 254 passed; 2 failed; 10 ignored; 0 measured; 0 filtered out; finished in 68.14s

One failure was the expected migration count after the uncommitted Notes 0034 migration; its expectation is updated in the preserved, uncommitted #1116 test work, and the focused regression passed:

test wire::upgrade_tests::production_7c_upgrades_content_types_once ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 265 filtered out; finished in 5.10s

The other failure was wire::tests::live_apps_run_in_separate_processes; its nested startup/backfill test timed out after 36.21 s under host load. The full crate suite was not repeated.

bun run check from apps/web stopped before Svelte diagnostics at the existing exception ratchet. Verbatim output:

perf-lint: INVALID: exception ratchet: contract.blaze: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.blocked-network: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; contract.dom-bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.model-bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.row-identity: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.snapshot: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; coverage.profile: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; coverage.readiness: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; io.unresolved-call: 16191 exceptions exceed the ratchet ceiling 16190; fix the new violation instead; render.blaze-adapter: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.model_byte_cap: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.model_row_cap: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; total: ledger has 21977 exceptions; ratchet total is 21964

Known gaps and merge round

  • bun run check needs the shared perf-lint ledger and ceiling reconciled before Svelte diagnostics can run.
  • The full bun run test --maxWorkers=2 suite and adversarial matrix are merge-round work under the verification policy. This issue changes no API route.
  • Paused #1116 changes remain uncommitted in this worktree and were not included in the #1115 commits.

READY FOR MERGE: no — bun run check is blocked by the repository-wide exception ratchet. No push, deploy or merge was performed.

# Forgejo #1115 completion Branch: `job/calcards-1115` Head: `d079d908315ec3e1f928887588b77b42fb55fee9` Commits: `c7ac9f75c`, `4f222ac1d`, `4a21474c7`, `f2c063b05`, `d079d9083`. The latest `origin/dev` sync is merge commit `553f01936`. ## Built Calendar Note and standalone File rows now open their stable item identity on Enter, touch tap, or double-click in Day, Week, Month and Agenda. One pointer click keeps selection. Month saved-item rows retain selection and `aria-pressed` during the double-click window, so their second click reaches the same row. File opens use the shared Quick Look. The Month direct-open regression test covers first-click selection followed by second-click opening. The production e2e covers keyboard, double-click and touch activation with real Notes and Files data. It captured 30 macOS-rendered production screenshots: Day, Week, Month, Agenda and the Month saved-item list at 390, 820 and 1440 px in light and dark themes. Files changed by the #1115 commits: - `packages/ui/src/components/calendar/GridColumn.svelte` - `packages/ui/src/components/calendar/MonthGrid.svelte` - `packages/ui/src/components/calendar/MoreItemsPopover.svelte` - `packages/ui/src/components/calendar/TimeGrid.svelte` - `packages/ui/src/components/calendar/model.ts` - `packages/ui/src/components/ItemCard.svelte` - `apps/web/src/routes/calendar/[view]/[date]/+page.svelte` - `apps/web/src/lib/calendar/MonthGrid.svelte.test.ts` - `apps/web/src/lib/calendar/TimeGrid.svelte.test.ts` - `apps/web/src/lib/calendar/model.test.ts` - `apps/web/e2e/calendar-direct-open-1115.mjs` - `bench/calendar-direct-open-1115.mjs` ## UX gaps closed - Month's first click used to close the saved-item list before the second click. It now keeps the row mounted for 500 ms, keeps selection announced, and opens the stable Files identity on the second click. - Keyboard Enter and touch tap open Notes and Files directly. The context-menu path keeps stable Copy link actions. UX gaps left: none found in the requested activation paths. The screenshot archive remains in the shared worktree at `artifacts/calendar-direct-open-1115-macos.tar.gz` (30 PNGs plus the profile JSON). The `fj issue` CLI exposes no file-upload command, so I could not attach it to this comment. The image files are also in `artifacts/calendar-direct-open-1115/` for the visual reviewer. ## Performance profile One local production profile ran on `calternal-dev`; no matching baseline exists in `docs/perf/baseline.json`. Host load average was 32.8 before and 23.3 after. - 3-item Month list, 5 warm opens: p50 764.45 ms, p95/max 1331.66 ms; server mean/peak RSS 319/320 MB and mean/peak CPU 8.2/34.97%. - 1,000 Notes, 5 opens: p50 757.14 ms, p95/max 908.84 ms; server mean/peak RSS 636/647 MB and mean/peak CPU 78.85/291.89%. - Burst of 3 concurrent opens: 1345.87 ms wall time; per-open p50 1104.82 ms and p95/max 1126.86 ms. The high recorded host load makes this a noisy local result. There is no baseline comparison or regression claim. Decision not covered by DESIGN: keep a selected Month row mounted for a 500 ms second-click window; Enter and touch open immediately. The 1,000-item profile is opt-in through `bench/calendar-direct-open-1115.mjs`, so the ordinary e2e stays bounded. ## Gates `bun run build` passed from `apps/web`. Vite emitted existing unused-selector and module-directive warnings; the output ended with: ```text Compressed 885 static variants; saved 20905360 bytes. ``` Command: `bunx vitest run src/lib/calendar/model.test.ts src/lib/calendar/TimeGrid.svelte.test.ts src/lib/calendar/MonthGrid.svelte.test.ts --maxWorkers=2`. Focused Vitest output: ```text Test Files 3 passed (3) Tests 73 passed (73) ``` Command: `bun e2e/calendar-direct-open-1115.mjs` from `apps/web`, using the merged local production server binary. Production e2e output: ```text Calendar #1115: 390px light production screens passed Calendar #1115: 390px dark production screens passed Calendar #1115: 390px production screens passed Calendar #1115: 820px light production screens passed Calendar #1115: 820px dark production screens passed Calendar #1115: 820px production screens passed Calendar #1115: 1440px light production screens passed Calendar #1115: 1440px dark production screens passed Calendar #1115: 1440px production screens passed Calendar #1115: Day Enter opens the stable Note identity Calendar #1115: Day double-click opens the shared File Quick Look Calendar #1115: Week Enter opens the stable Note identity Calendar #1115: Week double-click opens the stable File Quick Look Calendar #1115: Month saved-item Enter opens the stable Note identity Calendar #1115: Month saved-item double-click opens the stable File Quick Look Calendar #1115: Agenda attachment Enter opens the stable File Quick Look Calendar #1115: Day touch tap opens the stable Note identity Calendar #1115: phone Week overflow tap opens the stable Note identity Calendar #1115: Month touch tap opens the stable Note identity Calendar #1115: Agenda attachment touch tap opens the stable File Quick Look Calendar #1115: 30 production screenshots saved under artifacts/calendar-direct-open-1115 ``` Rust checks were run because restored paused #1116 work in this worktree contains Rust changes; the #1115 commits do not change Rust. `cargo fmt --check` exited 0 with no output. Command `cargo clippy -p calternal-server --all-targets -- -D warnings` passed: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 00s ``` Command `cargo test -p calternal-server` was not clean on the worktree with paused #1116 changes: ```text test result: FAILED. 254 passed; 2 failed; 10 ignored; 0 measured; 0 filtered out; finished in 68.14s ``` One failure was the expected migration count after the uncommitted Notes 0034 migration; its expectation is updated in the preserved, uncommitted #1116 test work, and the focused regression passed: ```text test wire::upgrade_tests::production_7c_upgrades_content_types_once ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 265 filtered out; finished in 5.10s ``` The other failure was `wire::tests::live_apps_run_in_separate_processes`; its nested startup/backfill test timed out after 36.21 s under host load. The full crate suite was not repeated. `bun run check` from `apps/web` stopped before Svelte diagnostics at the existing exception ratchet. Verbatim output: ```text perf-lint: INVALID: exception ratchet: contract.blaze: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.blocked-network: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; contract.dom-bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.model-bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.row-identity: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.snapshot: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; coverage.profile: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; coverage.readiness: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; io.unresolved-call: 16191 exceptions exceed the ratchet ceiling 16190; fix the new violation instead; render.blaze-adapter: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.model_byte_cap: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.model_row_cap: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; total: ledger has 21977 exceptions; ratchet total is 21964 ``` ## Known gaps and merge round - `bun run check` needs the shared perf-lint ledger and ceiling reconciled before Svelte diagnostics can run. - The full `bun run test --maxWorkers=2` suite and adversarial matrix are merge-round work under the verification policy. This issue changes no API route. - Paused #1116 changes remain uncommitted in this worktree and were not included in the #1115 commits. READY FOR MERGE: no — `bun run check` is blocked by the repository-wide exception ratchet. No push, deploy or merge was performed.
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#1115
No description provided.