Skip to content

fix(preview): step through the list the gallery is actually showing - #151

Merged
lstein merged 4 commits into
mainfrom
fix/preview-navigation-follows-gallery-order
Aug 28, 2026
Merged

fix(preview): step through the list the gallery is actually showing#151
lstein merged 4 commits into
mainfrom
fix/preview-navigation-follows-gallery-order

Conversation

@lstein

@lstein lstein commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

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 main today 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:

  • re-sorting by date (relevance order is the list),
  • overlaying local generations (the grid overlays none into a ranked set),
  • splicing in the generating placeholder (a ranking has no chronological insertion point).

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.

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 and others added 2 commits August 28, 2026 11:38
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
@lstein
lstein merged commit 13435ab into main Aug 28, 2026
19 checks passed
@lstein
lstein deleted the fix/preview-navigation-follows-gallery-order branch August 28, 2026 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant