fix(preview): step through the list the gallery is actually showing - #151
Merged
Conversation
Pressing next/prev in Preview while a similarity search was active walked the board listing in chronological order instead of the ranked results on screen, so the arrows jumped to images that were not in the result set at all. The filmstrip and the successor picked after a deletion read the same list, so they were wrong in the same way. Preview rebuilds its list from `selectedImageQuery` — a record of the board, view, sort, page and search term the selection was made in — which had no notion of a similarity search, so it could only ever reconstruct a board listing. Rather than stamp the search into that record, Preview now reads the gallery's CURRENT search directly. The chip is a view mode: the grid shows ranked results while it is set and the board listing once it is cleared, and Preview has to follow that the moment it changes. A stamped copy would go stale in both directions — kept walking a ranked set after the chip was cleared, and carried a ranked set onto selections made outside it (an upload, an image-map click, stepping off the live tile), where the cursor would land outside the list and both arrows would go dead. Under a ranked list Preview also stops re-sorting by date, stops overlaying local generations, and stops splicing in the generating placeholder — three things the grid already does not do for a ranked set, each of which would otherwise reorder the list or add members the user cannot see. The one exception is the selected item itself, which is kept at the head when the ranking does not contain it, so the cursor always has somewhere to stand and one arrow press moves into the visible list. "Next" now means next most similar rather than next chronologically, which is the point: the arrows match the grid. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y3qLymWvCN3BcYpTAhLSR1
lstein
requested review from
JPPhoto,
Pfannkuchensack and
blessedcoolant
as code owners
August 25, 2026 03:52
Two overlapping changes to Preview's navigation window had to be reconciled by hand: - main anchors an INFINITE window at the selection's own page when that page lies past the base window's reach (a deep reveal from the image map); this branch replaced the same expression with `navigationWindow`, which additionally mirrors the grid's paging under a ranked list. The deep-reveal anchoring now lives inside `navigationWindow`'s board-listing branch, and the ranked branch mirrors the grid's page in infinite mode too — in infinite mode the grid's page *is* its window offset, so mirroring it covers the deep-reveal case for a ranking. - both sides added a re-export from `core/semanticImageQuery` to the gallery contract; the two statements are merged into one, so `GallerySemanticReference` is exported once. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SsiLhQJTBHJsEr78dTqdQG
Stepping through a ranked list re-stamped `selectedImageQuery.page` with
the page the preview was opened on. That page indexes the BOARD listing,
and the item picked out of a ranking is nowhere near the slice it names,
so the stamp outlived the context that produced it:
image-map click at board index >= 600 -> grid anchors deep, page 30
similarity search -> grid resets to page 0,
the stamp stays at 30
arrow-step through the ranking -> 30 is re-stamped onto an
item at board row ~5
clear the chip -> navigation re-anchors at
offset 1800, the grid sits
at 0
Right then jumped ~1795 rows to an image the grid was not showing and
Left went dead, which is the failure this branch exists to remove.
A ranked pick now stamps the top of the listing. The grid's own page is
not the answer either: in paginated mode the footer paginates the
RANKING, so that number is a rank page and stamping it lands navigation
on an unrelated board slice. Setting a search and clearing it both reset
the grid to page 0 in either mode, so 0 is the board context a ranked
session hands back, and Preview and the grid agree once the chip goes.
Both cells are covered: a ranked pick under an infinite window with a
deep stamp, and one under a paginated window whose footer is on a rank
page. Both tests fail against the previous behaviour.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SsiLhQJTBHJsEr78dTqdQG
This was referenced Aug 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pressing next/prev in Preview while a similarity search is active walked the board listing in date order instead of the ranked results on screen, so the arrows jumped to images that were not in the result set at all. The filmstrip and the successor selected after a deletion read the same list, so they were wrong the same way.
Reproducible on
maintoday without any new feature: run a text search with the sparkle button (or drop an image onto the search field), click a result, then press>.Why
Preview rebuilds its list from
selectedImageQuery— the record of which list this selection was made in: board, view, sort, page, search term. It had no field for a similarity search, so Preview could only ever reconstruct a board listing, while the grid was showing a ranked one.The fix
Preview now reads the gallery's current search rather than a copy stamped onto the selection.
That distinction matters. Stamping was the obvious fix and it is wrong in both directions: the stamp keeps walking a ranked set after the chip is cleared, and it carries a ranked set onto selections made outside it — an upload, an image-map click, stepping off the live progress tile — where the cursor lands outside the list and both arrows go dead with no gesture short of clicking the grid to recover. The chip is a view mode: the grid shows ranked results while it is set and the board listing once it is cleared, so Preview follows it live and cannot go stale.
Under a ranked list Preview also stops doing three things the grid already does not do, each of which would otherwise reorder the list or add members that are not on screen:
The one deliberate exception is the selected item itself, kept at the head when the ranking does not contain it, so the cursor always has somewhere to stand and one arrow press moves into the visible results.
A ranked list also mirrors the grid's paging rather than the stamped board context. A stamped page indexes the board listing; applied to a ranking it lands on an unrelated slice, or — past the end of a ranking shorter than the board — on an empty one. For the same reason a ranked window's page params are never written back into
selectedImageQuery.page, which every other consumer reads as a board page.Live-follow is suppressed while a search is active, matching the grid, which hides pending items there entirely.
Behaviour change
"Next" now means next most similar rather than next chronologically. That is the point — the arrows match the order the grid is showing. A reference that no longer resolves after a reload (dropped files, and image-map clusters once #149 lands, both live in an in-memory registry) reads as the board listing, the same fallback the gallery's own search chip already takes.
Scope
Frontend only. Based on
main, so it covers the search kinds that exist there today (text, gallery image, web URL, dropped file). The image-map cluster kind in #149 inherits the fix automatically when that merges — a cluster is just another reference kind — so the two PRs need no coordination.Testing
pnpm run lint(format, oxlint, typecheck, architecture check), the performance budget gate, 6,399 unit tests and 833 browser tests all pass. New coverage for the ranked merge: relevance order preserved, and a selection outside the ranking anchored rather than dropped.Two rounds of fresh-context adversarial review were run against the diff; the first is what rejected the stamped design, and the second found the paginated-anchor and live-follow interactions fixed above. Every finding was verified against the code before acting on it.