fix: close inner-edge gap on split spreads in scroll mode#745
Merged
everpcpc merged 4 commits intoeverpcpc:mainfrom Apr 29, 2026
Merged
fix: close inner-edge gap on split spreads in scroll mode#745everpcpc merged 4 commits intoeverpcpc:mainfrom
everpcpc merged 4 commits intoeverpcpc:mainfrom
Conversation
When Split Wide Pages was enabled and the reader's transition style was Scroll, single-page mode rendered each split half centered within its pager cell. For comics whose pages are narrower than the screen, this produced a letterbox bar on the inner edge of every half, and swiping across the spread stacked the two inner-edge bars into a visible gap in the middle of the spread. Mirror the dual-page (.both) behavior for the single-page split path when the transition is .scroll: anchor .first to .trailing and .second to .leading so the inner edges meet flush at the cell boundary during a swipe. Cover, pageCurl, and none keep the legacy centered alignment to preserve the discrete-page metaphor and avoid a stationary asymmetry regression in those modes. The xOffset math in NativePageItem already auto-flips trailing/leading for RTL, so the same direction-independent mapping that the dual-page case uses works here too. Closes everpcpc#744 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
everpcpc
reviewed
Apr 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.
Closes #744
Summary
In the DIVINA reader, when "Split Wide Pages" is enabled and the user scrolls horizontally across a wide spread that's been split into two halves, a visible black gap appears in the middle of the spread instead of the two halves meeting seamlessly during the swipe.
The bug only manifests when all of these are true:
In landscape, Auto resolves to dual-page and the spread becomes a single
.split(id, .both)rendered as a two-slot dual-page, which already aligns the halves correctly. In portrait single-page, the spread becomes two separate.split(id, .first)/.split(id, .second)pager items — both centered within their cells. The inner-edge letterbox bar of one half plus the inner-edge bar of the other stack at the cell boundary during the swipe, producing the gap.Root cause
KMReader/Features/Reader/Views/PageImage/ReaderViewModel+NativePageData.swift:The single-page
.splitpath usedalignment: .center. The dual-page.bothpath on the same file already does the right thing —.first → .trailing,.second → .leading— so the halves meet flush in the middle of the spread. The single-page path was missed.Fix
For the
.scrolltransition only, mirror the dual-page alignment policy:.cover,.pageCurl, and.nonekeep the legacy centered alignment to preserve the discrete-page metaphor for those transitions and avoid a stationary asymmetry regression for users who don't actually swipe through transitions. (Page Curl is a separate rendering path throughNativeImagePageViewControllerthat doesn't go throughnativePageData, so it's unaffected either way.)The
xOffsetmath inNativePageItem_iOS.swift(lines 444–469) andNativePageItem_macOS.swiftalready flips.trailing/.leadingbased onreadingDirection == .rtl, so the direction-independent mapping above works in both LTR and RTL pagers — same as the existing dual-page case.Plumbing
nativePageDatanow takes apageTransitionStyle: PageTransitionStyleparameter, matching howsplitWidePageModeandreadingDirectionare already passed in. The four call sites inNativePagedPageContentView_iOS,NativePagedPageContentView_macOS, andReaderViewItemImageViewreadAppConfig.pageTransitionStyle(which is the same UserDefaults key the@AppStoragebinding inDivinaReaderViewwrites to, so they stay in sync when the user toggles transition style mid-session).Testing
Not tested. I do not have an Xcode build environment for this project, so the change has not been built or run. The reasoning is purely static:
PageTransitionStyleis exhaustive across all four cases..split(.first/.second)items only originate ingenerateViewItemswhen!allowDualPairs(verified — see lines 1238–1245 ofReaderViewModel.swift), so this code path is not reachable in dual-page mode and cannot affect dual-page rendering.WebtoonPageViewuses its ownWebtoonReaderViewand does not callnativePageData, so vertical/Webtoon mode is unaffected.CurlPageView/CurlDualPageViewrender viaNativeImagePageViewControllerwhich has its ownconfigure(... alignment:)defaulting to.centerand does not go throughnativePageData, so PageCurl is unaffected.AppConfigis already used elsewhere inKMReader/Features/Reader/Views/(e.g.,DivinaReaderView) without explicit imports, so the call sites should compile.A maintainer with the build environment should verify:
.scroll..pageitems,.dualpairings of two portrait pages) is unchanged.🤖 Generated with Claude Code