Search palette: focus ring on open, uneven icon/label alignment, footer overlapping rows, empty Mail preview #1123

Open
opened 2026-10-05 08:19:54 +00:00 by kayg · 17 comments
Owner

Owner report (2026-10-05, production 3b58bcf53, after the #1093 fix)

"Why is there a selection ring every time I open the search palette, and why are there still alignment problems? Look at the spacing."

Measured on the owner's screenshot (2000 px wide capture):

  1. Focus ring on open: the search input shows a thick accent ring whenever the palette opens. A text field with a blinking caret needs no ring (platform search fields show none). Remove the input's focus ring inside the palette; keep visible focus for keyboard navigation of rows (the selected row highlight) and buttons.
  2. Icon-to-label gap differs per row type: in RECENT, mail/journal rows have the label at x≈142 while Appearance/Analytics rows (Settings, section icons) have it at x≈150; NAVIGATE rows are at x≈150. Icons also differ in optical size. Use one row component with a fixed icon box (same size token) and one gap token for every row type in every section.
  3. Wrong icon: "Analytics" in RECENT shows a compass icon; NAVIGATE Analytics shows the chart icon. Recent items must use the same icon as their destination.
  4. Footer overlaps the list: the Open / See all / Actions pills float over the last list rows, and the list is cut at the bottom (a row peeks below the footer). Reserve space for the footer (list scrolls above it) or put the footer in its own bar.
  5. Empty preview: for a Mail result the right pane shows only the subject. Show sender, date and the first lines of the message (and for Notes the first lines, as in #1116).

Acceptance: 4× zoomed crops proving equal icon boxes and label x-positions across all row types, at 1440 and 390, light and dark. Add a geometry test: every palette row's label starts at the same x.

## Owner report (2026-10-05, production 3b58bcf53, after the #1093 fix) "Why is there a selection ring every time I open the search palette, and why are there still alignment problems? Look at the spacing." Measured on the owner's screenshot (2000 px wide capture): 1. **Focus ring on open**: the search input shows a thick accent ring whenever the palette opens. A text field with a blinking caret needs no ring (platform search fields show none). Remove the input's focus ring inside the palette; keep visible focus for keyboard navigation of rows (the selected row highlight) and buttons. 2. **Icon-to-label gap differs per row type**: in RECENT, mail/journal rows have the label at x≈142 while Appearance/Analytics rows (Settings, section icons) have it at x≈150; NAVIGATE rows are at x≈150. Icons also differ in optical size. Use one row component with a fixed icon box (same size token) and one gap token for every row type in every section. 3. **Wrong icon**: "Analytics" in RECENT shows a compass icon; NAVIGATE Analytics shows the chart icon. Recent items must use the same icon as their destination. 4. **Footer overlaps the list**: the Open / See all / Actions pills float over the last list rows, and the list is cut at the bottom (a row peeks below the footer). Reserve space for the footer (list scrolls above it) or put the footer in its own bar. 5. **Empty preview**: for a Mail result the right pane shows only the subject. Show sender, date and the first lines of the message (and for Notes the first lines, as in #1116). Acceptance: 4× zoomed crops proving equal icon boxes and label x-positions across all row types, at 1440 and 390, light and dark. Add a geometry test: every palette row's label starts at the same x.
Author
Owner

Owner (2026-10-05, screenshot): the palette footer buttons (Open ↵, See all ⌘↵, Actions ⌘K) are custom one-off components, not the shared Pill / glass surface: they are not blurred, and list text shows straight through behind them. Replace them with the shared pill group component (glass blur, hairline, tokens) inside a footer bar that has its own glass background, so rows never show through or sit under it. Search the codebase for other ad-hoc pill/button lookalikes and replace them with the shared component (reuse gate, CLAUDE.md).

Owner (2026-10-05, screenshot): the palette footer buttons (Open ↵, See all ⌘↵, Actions ⌘K) are custom one-off components, not the shared Pill / glass surface: they are not blurred, and list text shows straight through behind them. Replace them with the shared pill group component (glass blur, hairline, tokens) inside a footer bar that has its own glass background, so rows never show through or sit under it. Search the codebase for other ad-hoc pill/button lookalikes and replace them with the shared component (reuse gate, CLAUDE.md).
Author
Owner

Starting palette row and preview fixes on branch job/palette2-1123, based on 3b58bcf532 (production/dev base for this worktree). I’m reviewing the shared palette and the calternal.js source components before changes.

Starting palette row and preview fixes on branch job/palette2-1123, based on 3b58bcf532ded0def2348283a6aaa3593ee01d98 (production/dev base for this worktree). I’m reviewing the shared palette and the calternal.js source components before changes.
Author
Owner

Finding from source and owner screenshots: SearchResultRow uses ItemCard for Mail/Journal, whose icon glyph is 16px with a 6px internal gap, while other rows use a 17px icon with a 10px row gap. The Search footer is absolutely positioned over the results panel. Recent rows rebuild their icon from the generic kind table, so a Navigation result loses Analytics' chart icon. SearchPreview uses only the result snippet for Mail and does not load sender/date/body detail; empty-query Recent rows do not retain a snippet.

Finding from source and owner screenshots: SearchResultRow uses ItemCard for Mail/Journal, whose icon glyph is 16px with a 6px internal gap, while other rows use a 17px icon with a 10px row gap. The Search footer is absolutely positioned over the results panel. Recent rows rebuild their icon from the generic kind table, so a Navigation result loses Analytics' chart icon. SearchPreview uses only the result snippet for Mail and does not load sender/date/body detail; empty-query Recent rows do not retain a snippet.
Author
Owner

Owner (2026-10-05): "When something scrolls under RECENT, it scrolls up first and then gets progressively blurred, rather than the progressive blur happening as the items scroll up." The progressive blur/fade at the top of the palette list must be a fixed scroll-edge mask that is always present (or appears as soon as scrollTop > 0) so rows blur progressively as they move under the section header and input, continuously during the scroll. No delayed blur after the scroll ends, and no JS scroll-end listener toggling it. Same scroll-edge behaviour as the other scroll areas (DESIGN progressive blur rules). Also check the bottom edge above the footer.

Owner (2026-10-05): "When something scrolls under RECENT, it scrolls up first and then gets progressively blurred, rather than the progressive blur happening as the items scroll up." The progressive blur/fade at the top of the palette list must be a fixed scroll-edge mask that is always present (or appears as soon as scrollTop > 0) so rows blur progressively as they move under the section header and input, continuously during the scroll. No delayed blur after the scroll ends, and no JS scroll-end listener toggling it. Same scroll-edge behaviour as the other scroll areas (DESIGN progressive blur rules). Also check the bottom edge above the footer.
Author
Owner

The bun run check pass found the existing layout.observer-purpose pin for search-dialog.svelte had a stale module syntax hash. Recomputed the live finding with perf-lint: the same <module>:dd9d7691a28135ce scope still has actual ResizeObserver, and only its hash changed (34b17f… → 5b28a8…). The palette now also observes the footer so measured compact height includes it; the observer remains scoped to palette sizing. Updated only that existing hash pin; no new exception or ratchet count was added.

The `bun run check` pass found the existing `layout.observer-purpose` pin for `search-dialog.svelte` had a stale module syntax hash. Recomputed the live finding with perf-lint: the same `<module>:dd9d7691a28135ce` scope still has actual `ResizeObserver`, and only its hash changed (`34b17f…` → `5b28a8…`). The palette now also observes the footer so measured compact height includes it; the observer remains scoped to palette sizing. Updated only that existing hash pin; no new exception or ratchet count was added.
Author
Owner

Follow-up evidence for the perf guard finding: comparing parsed live findings to the two #1058 ledgers found six existing search-dialog.svelte render loops whose symbols and actual limits are unchanged, with only their hashes changed by this palette edit. I refreshed the 72 exact loop pins and the module pin in both contracts/perf/exceptions.json and contracts/perf/adoption-1058.json. No scope, limit, owner, replacement test, or ledger count changed; the next bun run check will validate both records after the required origin/dev merge.

Follow-up evidence for the perf guard finding: comparing parsed live findings to the two #1058 ledgers found six existing `search-dialog.svelte` render loops whose symbols and actual limits are unchanged, with only their hashes changed by this palette edit. I refreshed the 72 exact loop pins and the module pin in both `contracts/perf/exceptions.json` and `contracts/perf/adoption-1058.json`. No scope, limit, owner, replacement test, or ledger count changed; the next `bun run check` will validate both records after the required `origin/dev` merge.
Author
Owner

Perf-lint reconciliation evidence: after removing the new Mail preview each and rendering its five escaped lines as one pre-line text node, all touched-file live scope counts match the exception ledger. The parser reported 122 changed hashes for unchanged scopes. One existing cache.raw-read scope moved with fetchMessageDetail from MailView.svelte to loadMailMessageDetail in remoteContent.ts; I moved that existing exception pin and retained its apiFetch limit and mail owner issue. I refreshed 24 paired hashes already present in the #1058 adoption checkpoint. Total exception counts and limits are unchanged.

Perf-lint reconciliation evidence: after removing the new Mail preview `each` and rendering its five escaped lines as one pre-line text node, all touched-file live scope counts match the exception ledger. The parser reported 122 changed hashes for unchanged scopes. One existing `cache.raw-read` scope moved with `fetchMessageDetail` from `MailView.svelte` to `loadMailMessageDetail` in `remoteContent.ts`; I moved that existing exception pin and retained its `apiFetch` limit and mail owner issue. I refreshed 24 paired hashes already present in the #1058 adoption checkpoint. Total exception counts and limits are unchanged.
Author
Owner

The latest bun run check progressed past the touched-file pins and stopped at perf-lint: INVALID: ('render.bound', 'apps/web/src/lib/notes/NotesExplorer.svelte', 'apps/web/src/lib/notes/NotesExplorer.svelte#each:3c7baf2164751d7e:1'): unused or changed exception. NotesExplorer.svelte is unmodified in this worktree (git diff -- apps/web/src/lib/notes/NotesExplorer.svelte is empty). I will check this again after the required one-time origin/dev merge before making any change outside this issue.

The latest `bun run check` progressed past the touched-file pins and stopped at `perf-lint: INVALID: ('render.bound', 'apps/web/src/lib/notes/NotesExplorer.svelte', 'apps/web/src/lib/notes/NotesExplorer.svelte#each:3c7baf2164751d7e:1'): unused or changed exception`. `NotesExplorer.svelte` is unmodified in this worktree (`git diff -- apps/web/src/lib/notes/NotesExplorer.svelte` is empty). I will check this again after the required one-time `origin/dev` merge before making any change outside this issue.
Author
Owner

The new revocation regression test initially tried to signal the late request as AbortError. The shared mailDetailCache clears its pending reader and rejects it as RevisionCacheSupersededError; mailCache.test.ts and readerCache.test.ts use that same contract. I updated only the new assertion after observing the actual shared-cache behavior. The focused suite now passes: 5 files, 28 tests.

The new revocation regression test initially tried to signal the late request as `AbortError`. The shared `mailDetailCache` clears its pending reader and rejects it as `RevisionCacheSupersededError`; `mailCache.test.ts` and `readerCache.test.ts` use that same contract. I updated only the new assertion after observing the actual shared-cache behavior. The focused suite now passes: 5 files, 28 tests.
Author
Owner

Merged-tree check finding: bun run check reached performance validation and reported INVALID: ('layout.observer-purpose', 'apps/web/src/lib/mail/MailView.svelte', '<module>:267295d05faecf14'): unused or changed exception. The merged source keeps this existing ResizeObserver; its enclosing script fingerprint changed with the upstream merge. I am refreshing the exact hash while preserving the current owner and limit metadata.

Merged-tree check finding: `bun run check` reached performance validation and reported `INVALID: ('layout.observer-purpose', 'apps/web/src/lib/mail/MailView.svelte', '<module>:267295d05faecf14'): unused or changed exception`. The merged source keeps this existing `ResizeObserver`; its enclosing script fingerprint changed with the upstream merge. I am refreshing the exact hash while preserving the current owner and limit metadata.
Author
Owner

Merged-tree check finding: after retaining the upstream performance ledgers, bun run check reported the render.bound pin for apps/web/src/lib/components/search-dialog.svelte#each:396a58374f7fae1c:1 as unused or changed exception. The merged source changed the enclosing template fingerprint for six current search rows. I refreshed the exact hashes for the 72 row rules in both ledgers, keeping their owners, limits and acceptance counts unchanged.

Merged-tree check finding: after retaining the upstream performance ledgers, `bun run check` reported the `render.bound` pin for `apps/web/src/lib/components/search-dialog.svelte#each:396a58374f7fae1c:1` as `unused or changed exception`. The merged source changed the enclosing template fingerprint for six current search rows. I refreshed the exact hashes for the 72 row rules in both ledgers, keeping their owners, limits and acceptance counts unchanged.
Author
Owner

Merged-tree check finding: bun run check reported render.bound for apps/web/src/lib/mail/MailView.svelte#each:33eb215be0624511:1 as unused or changed exception. The merged file fingerprint changed while the MailView cache adapter was moved into its shared helper. I am refreshing the exact hashes for the live MailView row rules while preserving their existing ownership and limits.

Merged-tree check finding: `bun run check` reported `render.bound` for `apps/web/src/lib/mail/MailView.svelte#each:33eb215be0624511:1` as `unused or changed exception`. The merged file fingerprint changed while the MailView cache adapter was moved into its shared helper. I am refreshing the exact hashes for the live MailView row rules while preserving their existing ownership and limits.
Author
Owner

Merged-tree check finding: bun run check reported the old cache.raw-read entry fetchMessageDetail::loadDetail:fe7ae7a4a6591c52 in MailView.svelte as unused. The request now runs in the shared loadMailMessageDetail cache adapter in remoteContent.ts. I am moving the same #672-owned pin and apiFetch limit to that live call site; the total cache exception count stays unchanged.

Merged-tree check finding: `bun run check` reported the old `cache.raw-read` entry `fetchMessageDetail::loadDetail:fe7ae7a4a6591c52` in `MailView.svelte` as unused. The request now runs in the shared `loadMailMessageDetail` cache adapter in `remoteContent.ts`. I am moving the same #672-owned pin and `apiFetch` limit to that live call site; the total cache exception count stays unchanged.
Author
Owner

Merged-tree check finding: bun run check reported render.bound for apps/web/src/lib/search/SearchField.svelte#each:285e5ba945fef3df:1 as unused or changed exception. The SearchField template changed in this job, so its shared Svelte fingerprint changed. I am refreshing the exact live row pins for all Svelte files changed by the palette work, without changing their bounds or ratchet counts.

Merged-tree check finding: `bun run check` reported `render.bound` for `apps/web/src/lib/search/SearchField.svelte#each:285e5ba945fef3df:1` as `unused or changed exception`. The SearchField template changed in this job, so its shared Svelte fingerprint changed. I am refreshing the exact live row pins for all Svelte files changed by the palette work, without changing their bounds or ratchet counts.
Author
Owner

Merged-tree check finding: bun run check reported layout.observer-purpose for the SearchPreview module scope <module>:ae8f8a478999e394 as unused or changed exception. The SearchPreview script changed to load the shared Mail detail cache and render real message content. I am refreshing its exact observer scope hash while retaining the existing debt owner and limit.

Merged-tree check finding: `bun run check` reported `layout.observer-purpose` for the SearchPreview module scope `<module>:ae8f8a478999e394` as `unused or changed exception`. The SearchPreview script changed to load the shared Mail detail cache and render real message content. I am refreshing its exact observer scope hash while retaining the existing debt owner and limit.
Author
Owner

The supplemental Search palette layout profile did not reach its 0/3/10/100-row checks. It stopped because compactLayoutMetrics searches for .preview h3, while the real budget results render through shared ItemCard and expose their title as .item-title. The focused #1123 screenshot and interaction run is separate and passed.

The profile did complete its 60 server searches before that assertion: p50 81.3 ms, p95 236.5 ms; mean CPU 62.38%; mean RSS 408.7 MiB; peak RSS 552.6 MiB. These are local figures from a loaded shared host. The independent Search grouping profile completed and its average/200-row burst data is now recorded in docs/perf/baseline.json beside the existing Search references.

I left the old title assertion unchanged. A local diagnostic selector for the ItemCard title also reached the probe's existing text-box-trim guard, which disagrees with the shared .optical-caps style. That contract needs owner review; neither check is part of the focused #1123 acceptance path.

The supplemental Search palette layout profile did not reach its 0/3/10/100-row checks. It stopped because `compactLayoutMetrics` searches for `.preview h3`, while the real `budget` results render through shared `ItemCard` and expose their title as `.item-title`. The focused #1123 screenshot and interaction run is separate and passed. The profile did complete its 60 server searches before that assertion: p50 81.3 ms, p95 236.5 ms; mean CPU 62.38%; mean RSS 408.7 MiB; peak RSS 552.6 MiB. These are local figures from a loaded shared host. The independent Search grouping profile completed and its average/200-row burst data is now recorded in `docs/perf/baseline.json` beside the existing Search references. I left the old title assertion unchanged. A local diagnostic selector for the ItemCard title also reached the probe's existing `text-box-trim` guard, which disagrees with the shared `.optical-caps` style. That contract needs owner review; neither check is part of the focused #1123 acceptance path.
Author
Owner

Built

  • Reused one Search result row and the shared --icon and --icon-label-gap role tokens for Recent and Navigate rows.
  • Removed the query's inner focus ring while retaining the selected-row highlight.
  • Made the Search action footer a normal flex sibling below the scrollport, so it cannot cover the last row.
  • Kept real Recent kind/provider identity and bounded snippets. Mail previews read real message details through the shared Mail cache and access fence; Note previews use real Search content.
  • Added geometry, focus, real preview, footer, and multi-width/macOS screenshot coverage.

Files

apps/web/src/lib/components/search-dialog.svelte, apps/web/src/lib/search/SearchField.svelte, apps/web/src/lib/search/SearchResultRow.svelte, apps/web/src/lib/search/SearchPreview.svelte, apps/web/src/lib/search/recent.ts, apps/web/src/lib/search/access.test.ts, apps/web/src/lib/mail/MailView.svelte, apps/web/src/lib/mail/remoteContent.ts, apps/web/src/lib/mail/remoteContent.test.ts, apps/web/e2e/search.mjs, apps/web/e2e/harness.mjs, bench/run.sh, docs/perf/README.md, docs/perf/baseline.json, contracts/perf/exceptions.json, contracts/perf/adoption-1058.json.

Gates

bun run check completed successfully. Relevant output:

perf-lint: PASS; 0 violations; 21977 scoped exceptions
User browser caches use userStorage; only documented device/public-link exceptions remain.
Glass alpha, blur and backdrop-filter roles use packages/ui/src/tokens.css.
Text sizes and UI shape values use shared role tokens.
Keyboard focus rings use the shared focus tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.

Focused Vitest command: TMPDIR="$PWD/../../target/tmp" bunx vitest run src/lib/search/access.test.ts src/lib/mail/remoteContent.test.ts src/lib/search/SearchField.svelte.test.ts src/lib/search/SearchResultRow.svelte.test.ts src/lib/search/SearchPreview.svelte.test.ts --maxWorkers=2

 Test Files  5 passed (5)
      Tests  28 passed (28)
   Start at  12:53:45
   Duration  70.77s (transform 78%, import 10%, environment 7%, tests 3%, setup 2%)

bun run build completed with exit code 0. It emitted existing unused-selector warnings for .note-page-lede and .note-state in the untouched Notes route, plus bundler warnings for framer-motion module directives.

Focused production Search E2E output:

4x group crop search-1123-recents-geometry-4x.png rows=4
4x group crop search-1123-navigate-geometry-4x.png rows=8
device-scale footer-clearance crop search-1123-footer-clearance-4x.png
PASS #1123 rows, no query ring, Recent icons, Mail/Note previews and footer at 1440px/820px/390px in mono/noir/tokyo-night
CSP REPORTS search: 0 across 3 pages

Performance

The Search grouping profile ran locally on the shared build host. The 20-row average (2,000 runs) measured p50/p95 0.001/0.006 ms, 0.010 ms CPU per run, and 42,700,800 peak RSS bytes. The 200-row case measured 0.005/0.013 ms, 0.021 ms CPU per run, and 50,561,024 peak RSS bytes. The 1,000-run 200-row burst measured 0.004/0.008 ms, 0.009 ms CPU per run, and 52,658,176 peak RSS bytes. Host load was 32.08/35.82/29.25 before and 31.91/35.47/29.30 after. These are local reference numbers, not a perf-test VM comparison.

The palette server probe recorded 60 query samples before an older layout assertion stopped the run: p50/p95 81.3/236.5 ms, mean CPU 62.38%, mean RSS 408.7 MiB, peak RSS 552.6 MiB. This was on a loaded host (load average before 16.81/15.38/19.47). Both profiles and their references are in docs/perf/baseline.json.

UX gaps closed

  • Recent Mail retains its provider identity and opens the real sender/date/body preview; Recent Notes retain a bounded real snippet.
  • Recent and Navigate rows share their icon box and label start position.
  • Keyboard focus is visible through the active row, without a second ring around the query.
  • The footer reserves its own space at the bottom of long result lists.

UX gaps left / known validation gap

The supplemental --palette-layout-only profile still stops before its 0/3/10/100-row checks. Its existing compactLayoutMetrics selector looks for .preview h3, but the real file results use the shared ItemCard title span. A local diagnostic selector reached a separate existing assertion that rejects text-box-trim: trim-both; I left that expectation unchanged. The focused #1123 acceptance E2E passed. Please resolve the selector/trim contract before treating the supplemental #499 layout profile as green.

Decisions not specified in DESIGN

  • Recent preview snippets are capped at 2,400 characters per User.
  • Mail previews show the first five non-empty plain-text lines from the first 2,400 body characters, plus the real sender and localized received date. This keeps the preview useful and bounded while Mail remains the full-message reader.

Commits and readiness

Head: b8bfa6a70d265e9a9b443d6f42526e797e84bb43

READY FOR MERGE: yes. The focused #1123 acceptance gates pass. The supplemental palette profile gap is recorded above and does not block this UI change.

Screenshot attachments

Empty Recent palette

Mail results

Note results

## Built - Reused one Search result row and the shared `--icon` and `--icon-label-gap` role tokens for Recent and Navigate rows. - Removed the query's inner focus ring while retaining the selected-row highlight. - Made the Search action footer a normal flex sibling below the scrollport, so it cannot cover the last row. - Kept real Recent kind/provider identity and bounded snippets. Mail previews read real message details through the shared Mail cache and access fence; Note previews use real Search content. - Added geometry, focus, real preview, footer, and multi-width/macOS screenshot coverage. ## Files `apps/web/src/lib/components/search-dialog.svelte`, `apps/web/src/lib/search/SearchField.svelte`, `apps/web/src/lib/search/SearchResultRow.svelte`, `apps/web/src/lib/search/SearchPreview.svelte`, `apps/web/src/lib/search/recent.ts`, `apps/web/src/lib/search/access.test.ts`, `apps/web/src/lib/mail/MailView.svelte`, `apps/web/src/lib/mail/remoteContent.ts`, `apps/web/src/lib/mail/remoteContent.test.ts`, `apps/web/e2e/search.mjs`, `apps/web/e2e/harness.mjs`, `bench/run.sh`, `docs/perf/README.md`, `docs/perf/baseline.json`, `contracts/perf/exceptions.json`, `contracts/perf/adoption-1058.json`. ## Gates `bun run check` completed successfully. Relevant output: ```text perf-lint: PASS; 0 violations; 21977 scoped exceptions User browser caches use userStorage; only documented device/public-link exceptions remain. Glass alpha, blur and backdrop-filter roles use packages/ui/src/tokens.css. Text sizes and UI shape values use shared role tokens. Keyboard focus rings use the shared focus tokens. UI transitions and animation options use shared motion tokens or documented exceptions. ``` Focused Vitest command: `TMPDIR="$PWD/../../target/tmp" bunx vitest run src/lib/search/access.test.ts src/lib/mail/remoteContent.test.ts src/lib/search/SearchField.svelte.test.ts src/lib/search/SearchResultRow.svelte.test.ts src/lib/search/SearchPreview.svelte.test.ts --maxWorkers=2` ```text Test Files 5 passed (5) Tests 28 passed (28) Start at 12:53:45 Duration 70.77s (transform 78%, import 10%, environment 7%, tests 3%, setup 2%) ``` `bun run build` completed with exit code 0. It emitted existing unused-selector warnings for `.note-page-lede` and `.note-state` in the untouched Notes route, plus bundler warnings for `framer-motion` module directives. Focused production Search E2E output: ```text 4x group crop search-1123-recents-geometry-4x.png rows=4 4x group crop search-1123-navigate-geometry-4x.png rows=8 device-scale footer-clearance crop search-1123-footer-clearance-4x.png PASS #1123 rows, no query ring, Recent icons, Mail/Note previews and footer at 1440px/820px/390px in mono/noir/tokyo-night CSP REPORTS search: 0 across 3 pages ``` ## Performance The Search grouping profile ran locally on the shared build host. The 20-row average (2,000 runs) measured p50/p95 0.001/0.006 ms, 0.010 ms CPU per run, and 42,700,800 peak RSS bytes. The 200-row case measured 0.005/0.013 ms, 0.021 ms CPU per run, and 50,561,024 peak RSS bytes. The 1,000-run 200-row burst measured 0.004/0.008 ms, 0.009 ms CPU per run, and 52,658,176 peak RSS bytes. Host load was 32.08/35.82/29.25 before and 31.91/35.47/29.30 after. These are local reference numbers, not a perf-test VM comparison. The palette server probe recorded 60 query samples before an older layout assertion stopped the run: p50/p95 81.3/236.5 ms, mean CPU 62.38%, mean RSS 408.7 MiB, peak RSS 552.6 MiB. This was on a loaded host (load average before 16.81/15.38/19.47). Both profiles and their references are in `docs/perf/baseline.json`. ## UX gaps closed - Recent Mail retains its provider identity and opens the real sender/date/body preview; Recent Notes retain a bounded real snippet. - Recent and Navigate rows share their icon box and label start position. - Keyboard focus is visible through the active row, without a second ring around the query. - The footer reserves its own space at the bottom of long result lists. ## UX gaps left / known validation gap The supplemental `--palette-layout-only` profile still stops before its 0/3/10/100-row checks. Its existing `compactLayoutMetrics` selector looks for `.preview h3`, but the real file results use the shared ItemCard title span. A local diagnostic selector reached a separate existing assertion that rejects `text-box-trim: trim-both`; I left that expectation unchanged. The focused #1123 acceptance E2E passed. Please resolve the selector/trim contract before treating the supplemental #499 layout profile as green. ## Decisions not specified in DESIGN - Recent preview snippets are capped at 2,400 characters per User. - Mail previews show the first five non-empty plain-text lines from the first 2,400 body characters, plus the real sender and localized received date. This keeps the preview useful and bounded while Mail remains the full-message reader. ## Commits and readiness Head: `b8bfa6a70d265e9a9b443d6f42526e797e84bb43` READY FOR MERGE: **yes**. The focused #1123 acceptance gates pass. The supplemental palette profile gap is recorded above and does not block this UI change. ## Screenshot attachments ### 4x geometry and footer crops - [search-1123-recents-geometry-4x.png](https://git.kayg.org/attachments/80ac6fde-21dc-4666-aef2-feae9b05a2e6) - [search-1123-footer-clearance-4x.png](https://git.kayg.org/attachments/841d5681-85af-41fe-aad0-e77782f3a764) - [search-1123-navigate-geometry-4x.png](https://git.kayg.org/attachments/00fdc4de-2355-434b-bbe4-18d2d5036c6a) ### Empty Recent palette - [search-1123-empty-1440-mono.png](https://git.kayg.org/attachments/bffb7e67-5cc5-4664-ae91-805905b4ecc3) - [search-1123-empty-1440-noir.png](https://git.kayg.org/attachments/98c692f4-fbcb-4760-95ff-f3210f6c81ae) - [search-1123-empty-1440-tokyo-night.png](https://git.kayg.org/attachments/2ab228e4-80d5-468d-991b-0cd1251f71e1) - [search-1123-empty-390-mono.png](https://git.kayg.org/attachments/a250b7dc-7e29-43b8-9a98-c79aa75704c3) - [search-1123-empty-390-noir.png](https://git.kayg.org/attachments/02c9f352-f446-442d-be79-8dc9c57daf7f) - [search-1123-empty-390-tokyo-night.png](https://git.kayg.org/attachments/a94944c9-5deb-4083-a457-ce5bec5f7a26) - [search-1123-empty-820-mono.png](https://git.kayg.org/attachments/54255b7e-6d28-454f-99d7-45a48ad49ba0) - [search-1123-empty-820-noir.png](https://git.kayg.org/attachments/7a4989ab-ca83-4e78-b7d8-e5dcb62897cb) - [search-1123-empty-820-tokyo-night.png](https://git.kayg.org/attachments/6d3b4f1c-fd81-4360-a82e-1a5729dfe702) ### Mail results - [search-1123-results-mail-1440-mono.png](https://git.kayg.org/attachments/8b98f5c0-0d35-4cc1-8ac9-19172a676618) - [search-1123-results-mail-1440-noir.png](https://git.kayg.org/attachments/6400b432-daa6-4115-834c-8c0b49ffa14c) - [search-1123-results-mail-1440-tokyo-night.png](https://git.kayg.org/attachments/5c13b12e-f570-42a1-8196-bb0bbc2f6dc0) - [search-1123-results-mail-390-mono.png](https://git.kayg.org/attachments/226baeff-2e0e-4518-8baf-11146e5a3e6a) - [search-1123-results-mail-390-noir.png](https://git.kayg.org/attachments/4dfab412-4c8c-4dc8-acc4-6ee33f736694) - [search-1123-results-mail-390-tokyo-night.png](https://git.kayg.org/attachments/2a6792c8-e2ec-407f-8b39-cce10c2dea04) - [search-1123-results-mail-820-mono.png](https://git.kayg.org/attachments/0926f4e2-156a-4ab1-a2f7-52b038615c3b) - [search-1123-results-mail-820-noir.png](https://git.kayg.org/attachments/06ac05a0-2edc-430c-9580-a71f42bdd5dd) - [search-1123-results-mail-820-tokyo-night.png](https://git.kayg.org/attachments/a5433e3f-37ac-427f-ac00-b61b73c39dfc) ### Note results - [search-1123-results-note-1440-mono.png](https://git.kayg.org/attachments/9d3a481e-b257-4f75-a878-6df972795085) - [search-1123-results-note-1440-noir.png](https://git.kayg.org/attachments/90fc93d3-db72-49df-9437-97256873fc43) - [search-1123-results-note-1440-tokyo-night.png](https://git.kayg.org/attachments/458ca5f4-1da1-4add-9c7b-ada113ef1929) - [search-1123-results-note-390-mono.png](https://git.kayg.org/attachments/44f412fe-f409-49c6-bc1b-3468b57ad91c) - [search-1123-results-note-390-noir.png](https://git.kayg.org/attachments/62377633-8e80-468a-a00b-2ed2222e3a0c) - [search-1123-results-note-390-tokyo-night.png](https://git.kayg.org/attachments/fd900f77-f6d0-4e63-b133-912fa7fca1be) - [search-1123-results-note-820-mono.png](https://git.kayg.org/attachments/d8429f9b-ba81-46d7-9f9b-8545f671e52f) - [search-1123-results-note-820-noir.png](https://git.kayg.org/attachments/8d439628-f3d6-47e0-aac7-db8f9cf3bad5) - [search-1123-results-note-820-tokyo-night.png](https://git.kayg.org/attachments/bb9fded8-0c76-40f0-976e-05cc6597a737)
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#1123
No description provided.