Conversation
The UPDATE_VISIBLE_TILES scale-change branch concatenated carried fallback tiles with fresh tiles without deduplicating ids. Tile ids encode page/scale/rect but not rotation, so rotating 90 -> 270 under a fit zoom mode revisits an earlier scale and re-generates identical ids while that generation's fallbacks can still be alive (they are only purged once every fresh tile of the page is ready, which a slow rasterizer may never reach between presses). Keyed renderers crash on the duplicates - in Svelte this throws each_key_duplicate mid-flush and can wedge the tab. The fresh tile wins on collision: an identical id means identical page/scale/rect, and the fresh tile keeps the normal render lifecycle so the all-ready fallback purge still fires. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Someone is attempting to deploy a commit to the CloudPDF Team on Vercel. A member of the Team first needs to authorize it. |
This branch has not been deployed
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.
Bug
Rotating a document repeatedly while a fit zoom mode is active (
FitWidth/FitPage/Automatic) can leave duplicatetile.ids invisibleTiles[page]. Every keyed consumer of that array then breaks: the SvelteTilingLayer({#each tiles as tile (tile.id)}) throwseach_key_duplicatemid-flush — in our production app (Svelte,@embedpdf/*@2.14.4) this wedges the whole tab after ~3 rotate presses. Safari reproduces most readily because slower rasterization keeps fallback tiles alive across presses; React/Vue keyed lists would silently misbehave on the same state.Console stack from a production bundle at the moment of the freeze:
Mechanism
Three ingredients, all in
plugin-tiling:`p${page.index}-${scale}-x${x}-y${y}-w${w}-h${h}`. Under a fit zoom mode, rotating alternates between two fit scales, so the third 90° press revisits the first press's scale and rects and regenerates identical ids for a different tile generation.UPDATE_VISIBLE_TILESreducer's scale-change branch concatenates[...fallbackToCarry, ...promoted, ...newTiles]with no id dedup — the same-scale branch right below it carefully dedupes.ready(MARK_TILE_STATUS), so a slow rasterizer (Safari, or several viewers sharing one engine worker) keeps an old generation alive across presses.The failing sequence: press 1 (90°, fit scale
s1, tiles render) → a debounced fit recalc after a viewport resize (e.g. a scrollbar-gutter flip) lands ons1′and promotes the readys1tiles to fallbacks → press 2 (180°,s0; the fresh tiles never all reachready) → press 3 (270°): fit width returns exactlys1, the fresh tiles carry the same ids as the still-alive fallbacks, and the concat emits duplicates.Reproduction (published package, no DOM needed)
Node script replaying the reducer action sequence against
@embedpdf/plugin-tiling@2.14.4— ends withp0-0.455-x0-y0-w766-h383twice in one page's arrayFix
Dedupe carried tiles against fresh ids in the scale-change branch — the fresh tile wins: an identical id means identical page/scale/rect (same visual content), and the fresh tile keeps the normal render lifecycle so the all-ready fallback purge still fires.
Verification: I built
@embedpdf/plugin-tilingfrom this branch and replayed the sequence above against the built output — no duplicates; the same replay against the published 2.14.4 package shows the two duplicate ids.eslint srcreports no new findings for this change.I first included a colocated jest test mirroring the
packages/modelspattern, but backed it out to keep the diff minimal: this package deliberately excludes**/*.test.tsfrom its tsconfig (including it sweeps areducer.test.d.tsintodist), and I found no test-runner wiring in the repo. Happy to contribute the test plus the wiring in a follow-up if you want it — the repro above is the test body.A deeper alternative would be to include rotation (or a generation counter) in tile ids so distinct generations can never collide. I kept this PR to the minimal state-level fix and left that call to you.
We currently carry this exact change as a
pnpm patchin our application; this targets thev2branch since that's where 2.14.x/2.15.x release from (I have not audited whether the v3 architecture onmainhas an equivalent code path).🤖 Generated with Claude Code