Search: remove the bare 'Indexing your data' line; show a spinner before the first toolbar pill #545

Closed
opened 2026-09-30 17:41:07 +00:00 by kayg · 7 comments
Owner

Owner report (2026-09-30, screenshots)

"The indexing line needs to be removed. If things are still getting indexed, there needs to be a loading spinner before the first toolbar pill in the input field. The text also appears in a super weird way."
Today: apps/web/src/lib/components/search-dialog.svelte (~line 783) prints "Indexing your data: 78% complete. Current results may be incomplete." as a bare text line under the field. It has no padding (it touches the card edge) and does not use the type role of the dialog.
Fix:

  • Remove the line.
  • While win.indexingStatus reports indexing, show the shared spinner (reuse the existing spinner/loading primitive; no new one) inside the search field's trailing area, immediately before the first toolbar pill (Save search / Copy link / Expand). Size and colour come from the tokens and match the pill icons.
  • The spinner has a warm tooltip ("Still indexing: 78%. Some results may be missing.") and a polite aria-live status with the same text, announced once when indexing starts and once when it ends, not on every percent.
  • Reduced motion: a static indicator, not a spinning one.
  • When indexing ends, the spinner disappears with no layout shift: the pill group does not move (reserve the slot or use an absolutely positioned slot).
    Test: an e2e with a mocked indexing status that asserts the spinner is before the first pill, there is no text line, and the pills do not move when the spinner goes away (bounding boxes equal). Attach screenshots at 390/1440 in both themes.
    Order: this runs in the search-count-544 worktree after #544, because it is the same file.
## Owner report (2026-09-30, screenshots) "The indexing line needs to be removed. If things are still getting indexed, there needs to be a loading spinner before the first toolbar pill in the input field. The text also appears in a super weird way." **Today:** `apps/web/src/lib/components/search-dialog.svelte` (~line 783) prints "Indexing your data: 78% complete. Current results may be incomplete." as a bare text line under the field. It has no padding (it touches the card edge) and does not use the type role of the dialog. **Fix:** - Remove the line. - While `win.indexingStatus` reports indexing, show the shared spinner (reuse the existing spinner/loading primitive; no new one) inside the search field's trailing area, immediately before the first toolbar pill (Save search / Copy link / Expand). Size and colour come from the tokens and match the pill icons. - The spinner has a warm tooltip ("Still indexing: 78%. Some results may be missing.") and a polite `aria-live` status with the same text, announced once when indexing starts and once when it ends, not on every percent. - Reduced motion: a static indicator, not a spinning one. - When indexing ends, the spinner disappears with no layout shift: the pill group does not move (reserve the slot or use an absolutely positioned slot). **Test:** an e2e with a mocked indexing status that asserts the spinner is before the first pill, there is no text line, and the pills do not move when the spinner goes away (bounding boxes equal). Attach screenshots at 390/1440 in both themes. **Order:** this runs in the search-count-544 worktree after #544, because it is the same file.
Author
Owner

Starting #545 on branch job/search-count-544. Base: origin/dev at 15e17aeafc. Current head includes the approved #544 work at 675f20e59b.

Starting #545 on branch job/search-count-544. Base: origin/dev at 15e17aeafc8ea160c109e62fba57f6961c39d21f. Current head includes the approved #544 work at 675f20e59bbcbeef5aaee4e4b66db96b5f7ca263.
Author
Owner

Finding: search-dialog.svelte renders the indexing notice as a separate <p class="state-hint indexing-status"> in .results-panel (around lines 808–814). SearchField.svelte already owns the trailing action row and has a token-coloured .spinner; its reduced-motion rule still animates that spinner. I will put the indexing state in the field row, remove the results-panel notice, and add an explicit static reduced-motion state for the indexing indicator.

Finding: search-dialog.svelte renders the indexing notice as a separate `<p class="state-hint indexing-status">` in `.results-panel` (around lines 808–814). SearchField.svelte already owns the trailing action row and has a token-coloured `.spinner`; its reduced-motion rule still animates that spinner. I will put the indexing state in the field row, remove the results-panel notice, and add an explicit static reduced-motion state for the indexing indicator.
Author
Owner

Finding: after the new six viewport/theme reloads, the existing focused Search e2e first read the Navigate group at 2 rows while the app refreshed its enabled Tabs. The unchanged expected count is 7; waiting for that existing state removed the race. The mocked Index-progress assertions now pass at 390, 820 and 1440 px in both themes, including start/end announcements, the warm tooltip, static reduced motion and equal action bounds.

Finding: after the new six viewport/theme reloads, the existing focused Search e2e first read the Navigate group at 2 rows while the app refreshed its enabled Tabs. The unchanged expected count is 7; waiting for that existing state removed the race. The mocked Index-progress assertions now pass at 390, 820 and 1440 px in both themes, including start/end announcements, the warm tooltip, static reduced motion and equal action bounds.
Author
Owner

Completed

Removed the old Indexing text line. Search now shows the shared spinner in a reserved slot immediately before the first action pill while indexing is active. It uses the icon size and accent token, the shared warm tooltip, a polite status announcement on start and end, and a static ring under reduced motion. The reserved slot keeps the action pills in place when indexing ends.

Branch: job/search-count-544
Commit: 89f77bb69fbf1c4a0330932ac137923ec48f0ede

Files

  • apps/web/src/lib/components/search-dialog.svelte
  • apps/web/src/lib/search/SearchField.svelte
  • apps/web/e2e/search.mjs
  • bench/record.py
  • bench/test_record.py
  • docs/perf/README.md

Production screenshots

The mocked Index status runs on real Search results. All six screenshots are from the production build; the phone, tablet and desktop variants are attached here.

Verification

bun run build completed:

✓ built in 43.79s
Run npm run preview to preview your production build locally.
> Using @sveltejs/adapter-static
  Wrote site to "build"
  ✔ done

bun run check output:

$ node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
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/search-count-544/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bun run test summary:

 Test Files  141 passed (141)
      Tests  917 passed (917)
   Start at  22:54:21
   Duration  73.70s (transform 50%, environment 18%, import 18%, tests 10%, setup 4%)

python3 -m unittest bench.test_record:

......
----------------------------------------------------------------------
Ran 6 tests in 0.098s

OK

Focused production e2e output:

PASS mocked Index progress indicator, announcements and stable action bounds at phone, tablet and desktop widths in both themes
PASS complete Navigate rows and keyboard-accessible Files Show all
search count e2e: ok
search performance JSON /home/kayg/Developer/calternal-wt/search-count-544/target/tmp/search-count-545-perf.json
CSP REPORTS search: 0 across 1 pages

The full Search e2e also passed the new Index checks, then stopped at an existing centering assertion. Its measured centers differed by 5.89 px; the assertion still requires 2 px and was not changed:

AssertionError [ERR_ASSERTION]: result growth stays centred: {"fewCentre":450.5,"manyCentre":444.609375}
    at /home/kayg/Developer/calternal-wt/search-count-544/apps/web/e2e/search.mjs:905:9
CSP REPORTS search: 0 across 2 pages

git diff --check passed with no output. The required git fetch origin && git merge origin/dev reported Already up to date.

Performance

Local Search UI sample: 6 cycles, 16 real results per cycle, and 18 mocked status updates. Spinner start latency was p50 415.3 ms / p95 5918.2 ms; end latency was p50 323.3 ms / p95 791.0 ms. The e2e harness used 2677.323 ms CPU; sampled RSS was 109,772,800 bytes before, 136,613,888 bytes peak, and 131,457,024 bytes after.

docs/perf/baseline.json has no Index-indicator scenario yet. Its nearest Search UI measure is open_palette.visible_ms at p50 191 ms / p95 405 ms; this is not the same transition. One cold local sample is the 5.9 s start p95 outlier. The perf VM lock was free, but this branch's browser runner was not staged there; the sample is labeled local. The indicator sample uses the focused 16-result fixture; it does not measure indicator behavior over the 100k/1M Search corpora.

Decisions

  • Use a reserved --icon slot whenever the content Search field has its toolbar actions.
  • Announce the start percentage once, keep the tooltip current, and use “Indexing complete. Search results are up to date.” for the end announcement.
  • Reuse the field's shared spinner ring and make the Index ring static under reduced motion.
## Completed Removed the old Indexing text line. Search now shows the shared spinner in a reserved slot immediately before the first action pill while indexing is active. It uses the icon size and accent token, the shared warm tooltip, a polite status announcement on start and end, and a static ring under reduced motion. The reserved slot keeps the action pills in place when indexing ends. **Branch:** `job/search-count-544` **Commit:** `89f77bb69fbf1c4a0330932ac137923ec48f0ede` ### Files - `apps/web/src/lib/components/search-dialog.svelte` - `apps/web/src/lib/search/SearchField.svelte` - `apps/web/e2e/search.mjs` - `bench/record.py` - `bench/test_record.py` - `docs/perf/README.md` ### Production screenshots The mocked Index status runs on real Search results. All six screenshots are from the production build; the phone, tablet and desktop variants are attached here. - [390 px, Paper White](https://git.kayg.org/attachments/fe180ca8-801a-4fe0-82c9-bf664a6f394c) - [390 px, Tokyo Night](https://git.kayg.org/attachments/8497ca4b-d281-43c8-a785-058a1f2ba820) - [820 px, Paper White](https://git.kayg.org/attachments/d2192a6b-fd8b-4485-b93b-dc353c147853) - [820 px, Tokyo Night](https://git.kayg.org/attachments/b7060592-80f4-464a-915b-c2a657aa84e3) - [1440 px, Paper White](https://git.kayg.org/attachments/e24a49a2-33a8-485d-b1ba-c37a47820e39) - [1440 px, Tokyo Night](https://git.kayg.org/attachments/33a6715e-d8e7-451c-b814-02f7f105c265) ### Verification `bun run build` completed: ```text ✓ built in 43.79s Run npm run preview to preview your production build locally. > Using @sveltejs/adapter-static Wrote site to "build" ✔ done ``` `bun run check` output: ```text $ node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json 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/search-count-544/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run test` summary: ```text Test Files 141 passed (141) Tests 917 passed (917) Start at 22:54:21 Duration 73.70s (transform 50%, environment 18%, import 18%, tests 10%, setup 4%) ``` `python3 -m unittest bench.test_record`: ```text ...... ---------------------------------------------------------------------- Ran 6 tests in 0.098s OK ``` Focused production e2e output: ```text PASS mocked Index progress indicator, announcements and stable action bounds at phone, tablet and desktop widths in both themes PASS complete Navigate rows and keyboard-accessible Files Show all search count e2e: ok search performance JSON /home/kayg/Developer/calternal-wt/search-count-544/target/tmp/search-count-545-perf.json CSP REPORTS search: 0 across 1 pages ``` The full Search e2e also passed the new Index checks, then stopped at an existing centering assertion. Its measured centers differed by 5.89 px; the assertion still requires 2 px and was not changed: ```text AssertionError [ERR_ASSERTION]: result growth stays centred: {"fewCentre":450.5,"manyCentre":444.609375} at /home/kayg/Developer/calternal-wt/search-count-544/apps/web/e2e/search.mjs:905:9 CSP REPORTS search: 0 across 2 pages ``` `git diff --check` passed with no output. The required `git fetch origin && git merge origin/dev` reported `Already up to date.` ### Performance Local Search UI sample: 6 cycles, 16 real results per cycle, and 18 mocked status updates. Spinner start latency was p50 415.3 ms / p95 5918.2 ms; end latency was p50 323.3 ms / p95 791.0 ms. The e2e harness used 2677.323 ms CPU; sampled RSS was 109,772,800 bytes before, 136,613,888 bytes peak, and 131,457,024 bytes after. `docs/perf/baseline.json` has no Index-indicator scenario yet. Its nearest Search UI measure is `open_palette.visible_ms` at p50 191 ms / p95 405 ms; this is not the same transition. One cold local sample is the 5.9 s start p95 outlier. The perf VM lock was free, but this branch's browser runner was not staged there; the sample is labeled local. The indicator sample uses the focused 16-result fixture; it does not measure indicator behavior over the 100k/1M Search corpora. ### Decisions - Use a reserved `--icon` slot whenever the content Search field has its toolbar actions. - Announce the start percentage once, keep the tooltip current, and use “Indexing complete. Search results are up to date.” for the end announcement. - Reuse the field's shared spinner ring and make the Index ring static under reduced motion.
Author
Owner

Correction: the 5,918 ms start p95 is the maximum of six local samples. The harness did not isolate its cause, so ‘cold’ was an inference, not a measured finding.

Correction: the 5,918 ms start p95 is the maximum of six local samples. The harness did not isolate its cause, so ‘cold’ was an inference, not a measured finding.
Author
Owner

Performance update: locked perf VM sample

The earlier local sample is superseded by this run on root@10.69.69.63. I built the release server on the build host with the current production SPA, staged it with the e2e runner, and held /root/perf.lock for the complete measurement. No compilation ran on the VM.

  • Load average inside the lock, before: 2.68, 2.36, 1.53 (1/5/15 minute).
  • Load average inside the lock, after: 3.24, 2.50, 1.59.
  • Six cycles; 16 real Search results per cycle; 18 mocked Index status updates.
  • Spinner start: p50 99.7 ms, p95 115.0 ms.
  • Spinner end: p50 61.8 ms, p95 81.7 ms.
  • E2E harness CPU: 1366.158 ms.
  • Harness RSS: 105779200 bytes before, 144916480 bytes sampled peak and after.

The existing docs/perf/baseline.json has no Index-indicator scenario. Its nearest Search UI value is open_palette.visible_ms p50 191 ms / p95 405 ms, which measures a different interaction.

Locked VM run output:

PASS mocked Index progress indicator, announcements and stable action bounds at phone, tablet and desktop widths in both themes
PASS complete Navigate rows and keyboard-accessible Files Show all
search count e2e: ok
search performance JSON /root/search-count-545-run/target/tmp/search-count-545-perf-vm.json
CSP REPORTS search: 0 across 1 pages
### Performance update: locked perf VM sample The earlier local sample is superseded by this run on `root@10.69.69.63`. I built the release server on the build host with the current production SPA, staged it with the e2e runner, and held `/root/perf.lock` for the complete measurement. No compilation ran on the VM. - Load average inside the lock, before: `2.68, 2.36, 1.53` (1/5/15 minute). - Load average inside the lock, after: `3.24, 2.50, 1.59`. - Six cycles; 16 real Search results per cycle; 18 mocked Index status updates. - Spinner start: p50 `99.7 ms`, p95 `115.0 ms`. - Spinner end: p50 `61.8 ms`, p95 `81.7 ms`. - E2E harness CPU: `1366.158 ms`. - Harness RSS: `105779200` bytes before, `144916480` bytes sampled peak and after. The existing `docs/perf/baseline.json` has no Index-indicator scenario. Its nearest Search UI value is `open_palette.visible_ms` p50 `191 ms` / p95 `405 ms`, which measures a different interaction. Locked VM run output: ```text PASS mocked Index progress indicator, announcements and stable action bounds at phone, tablet and desktop widths in both themes PASS complete Navigate rows and keyboard-accessible Files Show all search count e2e: ok search performance JSON /root/search-count-545-run/target/tmp/search-count-545-perf-vm.json CSP REPORTS search: 0 across 1 pages ```
Author
Owner

Shipped in merge round 4, deployed to calternal.cloud in 1af8ead26 (healthy).

Shipped in merge round 4, deployed to calternal.cloud in 1af8ead26 (healthy).
kayg closed this issue 2026-10-01 09:17:43 +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#545
No description provided.