Skip to content

feat: improve splitview horizontal image pairing#568

Open
Zaida-3dO wants to merge 1 commit into
immichFrame:mainfrom
Zaida-3dO:feature/splitview-pairing
Open

feat: improve splitview horizontal image pairing#568
Zaida-3dO wants to merge 1 commit into
immichFrame:mainfrom
Zaida-3dO:feature/splitview-pairing

Conversation

@Zaida-3dO

@Zaida-3dO Zaida-3dO commented Jan 18, 2026

Copy link
Copy Markdown

Problem: The current splitview logic only paired portrait images if they were adjacent in the queue. If a landscape image was between two portrait ones, the first portrait would display alone.

This kind of defeats the purpose of the splitview layout, if sometimes you still get portrait images standing alone. The order of assets in the queue is random so we can move stuff around to ensure portrait images are always paired in splitview

Change Summary

Smarter portrait image pairing in splitview layout - looks ahead at 3 candidates instead of 2. Will always avoid showing a lone portrait image where possible.

How it works

Solution: New selectAssetsForDisplay() function will determine what to show using the following heuristic.

  1. If splitview is not enabled → show just the next image [0] in the queue.
  2. If there is only one item in the queue → return that one item.
  3. If next image [0] is landscape → show it alone.
  4. If next 2 images in the queue [0] and [1] are both portrait → pair both of them. i.e. return [0, 1]
  5. NEW: If the first [0] is portrait, second [1] is landscape and third [2] is portrait → pair the first and third portrait images together. i.e. return [0, 2]
    • The middle landscape image will now be the tip of the queue for the next selection.
  6. NEW If first [0] is portrait, second [1] is landscape and third [2] is landscape → skip the lone portrait image and return the next one. i.e. return [1]
    • Whenever a new portrait image reaches the top of the queue, we will hit state 5 above, and then the first portrait image will be returned alongside the new one.
    • What if we never get another portrait image: Then eventually we will drain the queue and hit state 2 above. Only then will you get a lone portrait image.

Note: Videos are always treated as landscape in the algorithm above, this is because isPortrait() returns false for videos (existing logic). They're always shown alone regardless of their orientation.

Summary by CodeRabbit

  • Refactor
    • Updated photo navigation to use a candidate-based next/previous selection flow for more consistent results and improved multi-item pairing.
    • Enhanced orientation/layout logic to better detect portrait vs. landscape and choose display combinations accordingly.
    • Asset loading now consistently includes the persisted client identifier for reliable retrieval.

@coderabbitai

coderabbitai Bot commented Jan 18, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Home page asset loading now includes a client identifier, and asset advancement uses candidate-based selection helpers for next and previous navigation. Ignore rules also expand to cover additional generated configuration and local cache artifacts.

Changes

Home page asset selection

Layer / File(s) Summary
Fetch and next selection
immichFrame.Web/src/lib/components/home-page/home-page.svelte
loadAssets now passes clientIdentifier to api.getAssets, and getNextAssets selects from up to three backlog candidates before removing chosen items.
Previous selection and helpers
immichFrame.Web/src/lib/components/home-page/home-page.svelte
getPreviousAssets now selects from reversed history candidates, maps selected positions back to history indices, and uses removeAtIndices with the new selectAssetsForDisplay helper. The prior splitview helper is removed.

Ignore rules

Layer / File(s) Summary
Ignore entries
.gitignore
.gitignore adds entries for ImmichFrame.WebApi/Config/Settings.json, QNAP thumbnail cache files, and Docker local state.

Estimated code review effort: 2 (Simple) | ~15 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: it improves splitview asset pairing, though "horizontal image pairing" is a bit imprecise.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@immichFrame.Web/src/lib/components/home-page/home-page.svelte`:
- Around line 217-251: The pairing logic is inverted because isHorizontal()
currently returns imageHeight > imageWidth (true for portrait), causing
selectAssetsForDisplay() (variables h0, h1, h2 and checks like if (!h0), if
(h1), etc.) to treat portrait images as "horizontal"; fix by changing
isHorizontal() to return imageWidth > imageHeight so its boolean matches its
name and the intended pairing logic, then run/adjust any comments or tests
referencing isHorizontal() behavior to reflect the corrected orientation
semantics.

Comment thread immichFrame.Web/src/lib/components/home-page/home-page.svelte Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@immichFrame.Web/src/lib/components/home-page/home-page.svelte`:
- Around line 249-253: The current branch that handles portrait/landscape
pairing returns [1] (show the landscape) when candidates[0] is a portrait and no
other portrait exists, which leaves the portrait at index 0 stuck in the
backlog; update that branch in home-page.svelte (the block referencing
candidates and the line with "return [1]") to instead fall back to displaying
the lone portrait (return [0]) so the portrait gets shown rather than
indefinitely remaining in the backlog. Ensure you only change the logic in that
specific conditional handling of candidates so other pairing behavior is
unchanged.

Comment thread immichFrame.Web/src/lib/components/home-page/home-page.svelte Outdated
@3rob3

3rob3 commented Jan 31, 2026

Copy link
Copy Markdown
Collaborator

I have confused myself with the wording. There is currently only portrait splitview. This is just improving that (and correctly renaming to "isPortrait", not actually doing any horizontal (landscape) splitview. Correct?

@Zaida-3dO

Copy link
Copy Markdown
Author

I have confused myself with the wording. There is currently only portrait splitview. This is just improving that (and correctly renaming to "isPortrait", not actually doing any horizontal (landscape) splitview. Correct?

Yeah this is correct, i just renamed the helpers to a correct name and I'm fixing splitview to ALWAYS show 2 images where possible

@3rob3 3rob3 added the enhancement New feature or request label Feb 2, 2026
@Qoen1

Qoen1 commented Apr 8, 2026

Copy link
Copy Markdown

I think this feature has a lot in common with an issue I tried to fix in #616 , and I think it might be benefitial to think of a more stramlined implementation for manipulating the image queue. This is because there are more scenario's which might be cool to explore in wich it is useful to manipulate the queue, and the current placement of this code will mean that the main svelte file wil get bloated pretty quickly.

I tried to think of a way this can be achieved in my PR. What are your thoughts on this @Zaida-3dO ?

@Zaida-3dO

Zaida-3dO commented Apr 9, 2026

Copy link
Copy Markdown
Author

I tried to think of a way this can be achieved in my PR. What are your thoughts on this @Zaida-3dO ?

Hey @Qoen1, i think your PR makes sense. I'm happy to implement a PortraitPairingQueueMutator once you can get it merged, however, I'm not certain server side queue will fully guarantee how the images get displayed.

For example, if we send portraits 1-2-3-4 and landscape 5, but the client skips image 1 for whatever reason (back-navigation, timing, etc.), then 2-3 pair up and 4 ends up displayed alone.

I'm thinking if we go down this path the mutator could also append some sort of metadata that the clients can read to know what images are meant to be shown together

@Zaida-3dO

Copy link
Copy Markdown
Author

also, we should consider making individual mutators configurable, since not all users have splitview enabled?

@Qoen1

Qoen1 commented Apr 10, 2026

Copy link
Copy Markdown

I think making mutators individualy configurable is a great idea and certainly a must if we're going to add more of them!

Enforcing the pairing of images in a better way is certainly a good suggestion, because the current implementation can fall apart if the user skips a back a lot. However, I think that in order to implement that in a maintainable way it would require a rework of how the entire queue is managed. Adding metadata seems a bit hacky.

@Zaida-3dO Zaida-3dO force-pushed the feature/splitview-pairing branch from 3ece987 to 85b4041 Compare July 3, 2026 12:21
@Zaida-3dO

Copy link
Copy Markdown
Author

Hey @JW-CH I have revived this PR. bringing it up to date with the latest changes for immich v3.

I cleaned up the PR description to make things a bit clearer. I hope you would be able to review whenever you have bandwidth. thanks.

@Zaida-3dO

Copy link
Copy Markdown
Author

And circling back to our conversation @Qoen1, per my comment here I don't believe moving this splitview fix to the server is actually the correct way to go, because then this forces the server to make assumptions of how the client will pair items in the queue, or require the server to provide metadata instructing the client how to pair items. Neither of these sound right to me.

I think of this PR as just a fix to the splitview logic, and not really a queue manipulation tool. So i still think this should go in separate from your fix

Re-port of the splitview-pairing feature onto the v3 slideshow engine.

- Pass clientIdentifier on the batch getAssets() request (every other
  asset request already carried it).
- Replace the adjacent-only shouldUseSplitView() with
  selectAssetsForDisplay(), which looks ahead at up to 3 candidates and
  can skip a landscape to pair two portraits ([0,2]), or defer a lone
  portrait so it is never shown alone within a batch.
- Add removeAtIndices() and wire the selection into both forward
  (backlog) and backward (history) navigation.

Keeps v3's isPortrait() (which also treats videos as non-portrait, so
videos are never paired). Verified with svelte-check (0 errors).
@Zaida-3dO Zaida-3dO force-pushed the feature/splitview-pairing branch from 85b4041 to fca58c7 Compare July 3, 2026 14:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants