⌘A / Ctrl+A selects all items in the current folder from anywhere in Files (not only when the list has focus) #578

Open
opened 2026-10-01 05:29:13 +00:00 by kayg · 11 comments
Owner

Owner request (2026-10-01): "cmd+a (on Windows, ctrl+a) should select all files in the current folder."

Today: files.selectAll (⌘A / Ctrl+A) exists in the registry, but its scope is "while the list has focus" (FilesBrowser.svelte ~1055). After you open Files or click empty space, focus is on the page, so ⌘A selects page text instead. This is the same focus gap as Escape (#537).
Fix (build on job/esc-537, which already moves focus to the clicked row and handles Escape at window level):

  • ⌘A / Ctrl+A selects every item in the current folder whenever the Files view (Files, Recent, Trash, Shared, smart folders) is active and focus is not in a text field, editor or contenteditable, and no menu, dialog or sheet is open. In a text field it keeps the native select-all text.
  • The same rule applies to Photos (the current grid/album), Mail (the current list) and Tasks (the current list): use the shared collection primitive (one owner, reuse gate).
  • It is in the registry with per-preset keys (#542), announced to screen readers ("5 items selected"), and has no animation for keyboard (#527).
  • With 10,000 items: selection updates in < 16 ms of script (virtualised list).
    Test: an e2e that opens Files, presses ⌘A without clicking (all selected), presses Escape (cleared), focuses the search field, presses ⌘A (only the text is selected). The same in Photos and Mail. Web gates.
## Owner request (2026-10-01): "cmd+a (on Windows, ctrl+a) should select all files in the current folder." **Today:** `files.selectAll` (⌘A / Ctrl+A) exists in the registry, but its scope is "while the list has focus" (`FilesBrowser.svelte` ~1055). After you open Files or click empty space, focus is on the page, so ⌘A selects page text instead. This is the same focus gap as Escape (#537). **Fix (build on job/esc-537, which already moves focus to the clicked row and handles Escape at window level):** - ⌘A / Ctrl+A selects every item in the current folder whenever the Files view (Files, Recent, Trash, Shared, smart folders) is active and focus is **not** in a text field, editor or contenteditable, and no menu, dialog or sheet is open. In a text field it keeps the native select-all text. - The same rule applies to Photos (the current grid/album), Mail (the current list) and Tasks (the current list): use the shared collection primitive (one owner, reuse gate). - It is in the registry with per-preset keys (#542), announced to screen readers ("5 items selected"), and has no animation for keyboard (#527). - With 10,000 items: selection updates in < 16 ms of script (virtualised list). **Test:** an e2e that opens Files, presses ⌘A without clicking (all selected), presses Escape (cleared), focuses the search field, presses ⌘A (only the text is selected). The same in Photos and Mail. Web gates.
Author
Owner

Starting issue #578 on branch job/esc-537.

Base SHA: cc25c441b7a974185622a1dee853cf38686d2b67 (merge base with origin/dev). Current head before work: f8ab419af968c348dc3e30de769cd71a533cac29.

Starting issue #578 on branch `job/esc-537`. Base SHA: `cc25c441b7a974185622a1dee853cf38686d2b67` (merge base with `origin/dev`). Current head before work: `f8ab419af968c348dc3e30de769cd71a533cac29`.
Author
Owner

Finding: the branch already has multi-selection for Files and Photos, but MailView.svelte has no selected-message state or collection selection behavior. There is also no /tasks route or standalone Tasks list in this branch; task rows live in Calendar, with a 20-item undated side-panel list. This means supporting Mail/Tasks select-all needs to stay within their current list UI rather than only adding a shortcut handler.

Finding: the branch already has multi-selection for Files and Photos, but `MailView.svelte` has no selected-message state or collection selection behavior. There is also no `/tasks` route or standalone Tasks list in this branch; task rows live in Calendar, with a 20-item undated side-panel list. This means supporting Mail/Tasks select-all needs to stay within their current list UI rather than only adding a shortcut handler.
Author
Owner

Local performance finding for #578: the 10k nearly-selected path measured p50 1.272 ms, p95 40.217 ms, and max 54.744 ms; the 100-event 10k burst measured p95 4.883 ms. The run recorded load averages of 20.28 / 18.56 / 15.44 on calternal-dev. This is a local, heavily loaded-host sample; docs/perf/baseline.json has no collection select-all scenario for a like-for-like comparison. I added the hot-path profile to bench/run.sh; do not treat this local tail as a perf-host regression.

Local performance finding for #578: the 10k nearly-selected path measured p50 1.272 ms, p95 40.217 ms, and max 54.744 ms; the 100-event 10k burst measured p95 4.883 ms. The run recorded load averages of 20.28 / 18.56 / 15.44 on `calternal-dev`. This is a local, heavily loaded-host sample; `docs/perf/baseline.json` has no collection select-all scenario for a like-for-like comparison. I added the hot-path profile to `bench/run.sh`; do not treat this local tail as a perf-host regression.
Author
Owner

Photos screenshot capture exposed a flaky fixture upload: one --screenshots-only run returned HTTP 412 for its first IMG_1001.JPG upload. The same Photos seed passed in the preceding focused run with 45 groups. The failed run's server log also showed SQLite connection acquisition at 2.65–2.89 s against a 2 s slow threshold. I’m capturing the same evidence through the passing focused e2e path; this is a local SLOW-only test fixture finding, with no application code changed for the response.

Photos screenshot capture exposed a flaky fixture upload: one `--screenshots-only` run returned HTTP 412 for its first `IMG_1001.JPG` upload. The same Photos seed passed in the preceding focused run with 45 groups. The failed run's server log also showed SQLite connection acquisition at 2.65–2.89 s against a 2 s slow threshold. I’m capturing the same evidence through the passing focused e2e path; this is a local SLOW-only test fixture finding, with no application code changed for the response.
Author
Owner

The first Mail e2e run found an ambiguity in its fixture locator after adding a second thread: .mail-row now correctly matches two rows, while the legacy open-thread assertion waited on the whole locator. I’m narrowing that action to the first row; the new selection assertion will continue to require both rows.

The first Mail e2e run found an ambiguity in its fixture locator after adding a second thread: `.mail-row` now correctly matches two rows, while the legacy open-thread assertion waited on the whole locator. I’m narrowing that action to the first row; the new selection assertion will continue to require both rows.
Author
Owner

The first Calendar Tasks proof selected all three fixture rows (the page wait observed three aria-pressed=true rows), but the following count used a child locator even though aria-pressed is on the row itself. I’m correcting that selector; the expected count stays three.

The first Calendar Tasks proof selected all three fixture rows (the page wait observed three `aria-pressed=true` rows), but the following count used a child locator even though `aria-pressed` is on the row itself. I’m correcting that selector; the expected count stays three.
Author
Owner

Post-merge Photos e2e finding: the 390 px Photos context did not install the #555 User storage test seam. bun e2e/photos.mjs --escape-only --screenshots ... failed in storeEvidenceThemePreference with Cannot read properties of undefined (reading 'getItem') before selection assertions. I am adding the seam to that secondary context and rerunning the production proof.

Post-merge Photos e2e finding: the 390 px Photos context did not install the #555 User storage test seam. `bun e2e/photos.mjs --escape-only --screenshots ...` failed in `storeEvidenceThemePreference` with `Cannot read properties of undefined (reading 'getItem')` before selection assertions. I am adding the seam to that secondary context and rerunning the production proof.
Author
Owner

Correction to the Photos e2e finding: the failure occurs while setting the desktop theme first. Photos has a local virtual-authenticator helper that bypasses the shared authenticator() helper, so neither its primary context nor its separately created mobile context had the #555 storage seam. I am installing the seam in both contexts.

Correction to the Photos e2e finding: the failure occurs while setting the desktop theme first. Photos has a local virtual-authenticator helper that bypasses the shared `authenticator()` helper, so neither its primary context nor its separately created mobile context had the #555 storage seam. I am installing the seam in both contexts.
Author
Owner

Post-merge Calendar e2e finding: CALENDAR_E2E_SELECT_ALL_ONLY=1 bun e2e/calendar.mjs timed out waiting for .agenda .line.task[aria-pressed="true"] after Ctrl+A; the proof expects all three Today Task rows selected. The same focused proof passed before merging origin/dev. I am checking the Calendar keyboard handler and merged AgendaList selection state, then will rerun this production proof.

Post-merge Calendar e2e finding: `CALENDAR_E2E_SELECT_ALL_ONLY=1 bun e2e/calendar.mjs` timed out waiting for `.agenda .line.task[aria-pressed="true"]` after Ctrl+A; the proof expects all three Today Task rows selected. The same focused proof passed before merging `origin/dev`. I am checking the Calendar keyboard handler and merged AgendaList selection state, then will rerun this production proof.
Author
Owner

Correction to the Calendar e2e finding: the timeout came from a test timing race. Calendar retained stale Agenda rows during navigation while the current Tasks request and visibility refresh completed. The production selection handler worked once the current Task IDs were loaded. I updated the proof to wait for the current Tasks response and settled Agenda rows; the merged production build now passes select-all and Escape at 390, 820, and 1440 px in both themes.

Correction to the Calendar e2e finding: the timeout came from a test timing race. Calendar retained stale Agenda rows during navigation while the current Tasks request and visibility refresh completed. The production selection handler worked once the current Task IDs were loaded. I updated the proof to wait for the current Tasks response and settled Agenda rows; the merged production build now passes select-all and Escape at 390, 820, and 1440 px in both themes.
Author
Owner

Completed: #578 on job/esc-537.

Head: 20d1e0f0c4a72f4cb5d763e3983e33e26a6bf16f

Built: The existing files.selectAll registry action now works from page focus in Files, Recent, Trash, Shared, Photos, Mail and Calendar Today's Agenda Tasks. The shared collection handler leaves native text selection to inputs/editors and yields to menus, dialogs and sheets. It selects stable collection keys without reading virtualized DOM rows. Selection announces the count, and Escape clears it.

Files: apps/web/src/lib/files/{selection.ts,FilesBrowser.svelte,RecentView.svelte,TrashView.svelte,selection.escape.svelte.test.ts}; apps/web/src/lib/photos/PhotosView.svelte; apps/web/src/lib/mail/MailView.svelte; apps/web/src/routes/calendar/[view]/[date]/+page.svelte; packages/ui/src/components/calendar/AgendaList.svelte; apps/web/src/lib/shortcuts/{registry.ts,registry.test.ts}; apps/web/e2e/{files.mjs,photos.mjs,mail-escape.mjs,calendar.mjs}; bench/{collection-select-all.mjs,record.py,run.sh}.

Commits: 70552d40e adds collection select-all; 9c64ee269 stabilizes the e2e evidence flows; 9b4452b97 merges origin/dev; 132e42480 fixes Photos test storage setup; 20d1e0f0c waits for settled Calendar Tasks before assertions.

Production build and e2e: bun run build passed (✓ built in 56.49s). Files, Photos, Mail and Calendar select-all proofs passed on the production SPA. Files, Photos, Mail and Calendar screenshots cover 390, 820 and 1440 px in light and dark themes; 24 all-selected screenshots are attached below. CSP reports were zero for Files (7 pages), Photos (2 pages) and Mail (1 page).

Web gates (output quoted verbatim):

$ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
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.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/esc-537/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings
$ vitest run

 RUN  v5.0.1 /home/kayg/Developer/calternal-wt/esc-537/apps/web

 Test Files  149 passed (149)
      Tests  1021 passed (1021)
   Start at  17:08:25
   Duration  136.37s (transform 52%, environment 19%, import 15%, tests 11%, setup 4%)

The test run also printed Could not parse CSS stylesheet and repeated jsdom Not implemented: Window's scrollTo() method diagnostics. No Rust source or API contract changed, so no Rust crate gate applied. cargo clean completed: Removed 7169 files, 4.5GiB total. apps/web/build and apps/web/.svelte-kit were removed after validation.

Performance: The local benchmark ran on calternal-dev at load averages 20.28 / 18.56 / 15.44. The 10k near-selected path measured p50 1.272 ms, p95 40.217 ms, max 54.744 ms; the 100-event 10k burst measured p95 4.883 ms. The 10k p95 exceeds the stated 16 ms target in this one loaded-host run. docs/perf/baseline.json has no comparable scenario, so this is a known local performance gap, not a baseline regression.

Decisions: The current Tasks list is Calendar Today's Agenda, so the collection selection applies there; no standalone Tasks route exists. The handler reuses files.selectAll and its existing Cmd/Ctrl host mapping, with collection scope. Files keeps its existing toggle-all behavior (a second shortcut clears an all-selected set); Photos, Mail and Tasks select all. Mail selects rows loaded in its current list page.

Known scope limits: Mail selection does not include cursor pages that are not loaded. No new shortcut preset setting or Tasks route was added.

Attached production screenshots (light/dark, 390/820/1440 px):

**Completed:** #578 on `job/esc-537`. **Head:** `20d1e0f0c4a72f4cb5d763e3983e33e26a6bf16f` **Built:** The existing `files.selectAll` registry action now works from page focus in Files, Recent, Trash, Shared, Photos, Mail and Calendar Today's Agenda Tasks. The shared collection handler leaves native text selection to inputs/editors and yields to menus, dialogs and sheets. It selects stable collection keys without reading virtualized DOM rows. Selection announces the count, and Escape clears it. **Files:** `apps/web/src/lib/files/{selection.ts,FilesBrowser.svelte,RecentView.svelte,TrashView.svelte,selection.escape.svelte.test.ts}`; `apps/web/src/lib/photos/PhotosView.svelte`; `apps/web/src/lib/mail/MailView.svelte`; `apps/web/src/routes/calendar/[view]/[date]/+page.svelte`; `packages/ui/src/components/calendar/AgendaList.svelte`; `apps/web/src/lib/shortcuts/{registry.ts,registry.test.ts}`; `apps/web/e2e/{files.mjs,photos.mjs,mail-escape.mjs,calendar.mjs}`; `bench/{collection-select-all.mjs,record.py,run.sh}`. **Commits:** `70552d40e` adds collection select-all; `9c64ee269` stabilizes the e2e evidence flows; `9b4452b97` merges `origin/dev`; `132e42480` fixes Photos test storage setup; `20d1e0f0c` waits for settled Calendar Tasks before assertions. **Production build and e2e:** `bun run build` passed (`✓ built in 56.49s`). Files, Photos, Mail and Calendar select-all proofs passed on the production SPA. Files, Photos, Mail and Calendar screenshots cover 390, 820 and 1440 px in light and dark themes; 24 all-selected screenshots are attached below. CSP reports were zero for Files (7 pages), Photos (2 pages) and Mail (1 page). **Web gates (output quoted verbatim):** ```text $ node scripts/check-user-storage.mjs && node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json 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. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/esc-537/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` ```text $ vitest run RUN v5.0.1 /home/kayg/Developer/calternal-wt/esc-537/apps/web Test Files 149 passed (149) Tests 1021 passed (1021) Start at 17:08:25 Duration 136.37s (transform 52%, environment 19%, import 15%, tests 11%, setup 4%) ``` The test run also printed `Could not parse CSS stylesheet` and repeated jsdom `Not implemented: Window's scrollTo() method` diagnostics. No Rust source or API contract changed, so no Rust crate gate applied. `cargo clean` completed: `Removed 7169 files, 4.5GiB total`. `apps/web/build` and `apps/web/.svelte-kit` were removed after validation. **Performance:** The local benchmark ran on `calternal-dev` at load averages 20.28 / 18.56 / 15.44. The 10k near-selected path measured p50 1.272 ms, p95 40.217 ms, max 54.744 ms; the 100-event 10k burst measured p95 4.883 ms. The 10k p95 exceeds the stated 16 ms target in this one loaded-host run. `docs/perf/baseline.json` has no comparable scenario, so this is a known local performance gap, not a baseline regression. **Decisions:** The current Tasks list is Calendar Today's Agenda, so the collection selection applies there; no standalone Tasks route exists. The handler reuses `files.selectAll` and its existing Cmd/Ctrl host mapping, with collection scope. Files keeps its existing toggle-all behavior (a second shortcut clears an all-selected set); Photos, Mail and Tasks select all. Mail selects rows loaded in its current list page. **Known scope limits:** Mail selection does not include cursor pages that are not loaded. No new shortcut preset setting or Tasks route was added. **Attached production screenshots** (light/dark, 390/820/1440 px): - **Files:** [1440 light](https://git.kayg.org/attachments/7f93dc10-fcde-446a-8421-5acb58da51ba) · [1440 dark](https://git.kayg.org/attachments/a812a3a8-f9d5-47ec-8e2f-e0f265d9e008) · [820 light](https://git.kayg.org/attachments/32fe1038-46a5-4470-a80d-f74c819b25c6) · [820 dark](https://git.kayg.org/attachments/ed2ae6ae-9f06-4d98-952d-03aa670801e4) · [390 light](https://git.kayg.org/attachments/e5c98f61-c91c-4476-954f-75e0b95d1137) · [390 dark](https://git.kayg.org/attachments/c2a7ee09-5a1d-4b36-bc36-dcb46ea1d84c) - **Photos:** [1440 light](https://git.kayg.org/attachments/12e3f307-92af-45a8-9901-f645bf333bad) · [1440 dark](https://git.kayg.org/attachments/47ce5943-2125-4b78-8842-db66f6b2d196) · [820 light](https://git.kayg.org/attachments/676fd32b-f9d6-4e63-a065-423d59b3df58) · [820 dark](https://git.kayg.org/attachments/45d05904-837b-4641-8361-6c91b7656acf) · [390 light](https://git.kayg.org/attachments/ada300df-63e2-4ac8-a246-b7ff0fa71cb9) · [390 dark](https://git.kayg.org/attachments/6b728e1c-d6f0-4e55-b001-efa3f55593bf) - **Mail:** [1440 light](https://git.kayg.org/attachments/61d9af26-d470-4acd-8b84-e1b34a4c3c8d) · [1440 dark](https://git.kayg.org/attachments/e62a03f3-a69e-484d-8be2-0d9f4d057a0a) · [820 light](https://git.kayg.org/attachments/215d43ab-749f-4af5-924a-27df4b7ac7fe) · [820 dark](https://git.kayg.org/attachments/ee5819f5-c3fa-44d7-9420-c431968e88d5) · [390 light](https://git.kayg.org/attachments/9243f11e-0635-45d6-b26a-9945b5afcdea) · [390 dark](https://git.kayg.org/attachments/18a12740-0318-4fea-9713-c9830def483f) - **Calendar Tasks:** [1440 light](https://git.kayg.org/attachments/183c943d-fe60-4a91-bab3-d81c8ee6d035) · [1440 dark](https://git.kayg.org/attachments/456c92ea-695e-4b56-9b68-8ae3b27f9e03) · [820 light](https://git.kayg.org/attachments/bf07b79d-0ff3-4452-b8ac-4cecc6687bb8) · [820 dark](https://git.kayg.org/attachments/726773ce-9741-4706-8661-803213e36c97) · [390 light](https://git.kayg.org/attachments/364783ee-f42c-41dd-bee0-d77acd6751b9) · [390 dark](https://git.kayg.org/attachments/45eeadef-5933-483c-903b-71d40509d58a)
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#578
No description provided.