Skip to content

Conversation

nicokaiser
Copy link
Contributor

Description

This PR uses an exact JS port of the @immich-app/justified-layout module to replace justified-layout.

Some weeks ago, there was an attempt to use @immich-app/justified-layout instead of Flickr'sjustified-layout module to render the layout boxes.However there seem to have been problems, since the WASM module requies a top-level await, so this attempt was postponed.

Once the WASM module becomes available, this can still be a fallback which behaves the same (see below). Also, even this JS implementation should be much faster than the justified-layout (I did not measure this, but since Flickr's implementation is quite complex, I assume with large timeline buckets this should already be noticable when resizing windows).

How Has This Been Tested?

Tested in web.

Note: this implementation (just like the WASM version) produces slightly different results than justified-layout:

  • justified-layout sets the last line's height to the previous line's height if it is not filled while the Immich algorithm uses the rowHeight for unfinished last lines. This is not an issue here at all, it's just different (see screen recording below).
  • In some cases, the Immich algorithm is not able to fill the width. This has improved since Rows are sometimes not filled justified-layout#9 but is still an issue for edge cases (large rowHeight).

Screenshots (if appropriate)

Example of the last line, Flickr algorithm:

resize-old.mov

Example of the last line, Immich algorithm (notice the image in the unfinished line staying the same size):

resize-new.mov

Example of line being filled (Flickr algorithm):

wrap-old.mov

Example of line not being filled (Immich algorithm):

wrap-new.mov

Checklist:

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation if applicable
  • I have no unrelated changes in the PR.
  • I have confirmed that any new dependencies are strictly necessary.
  • I have written tests for new code (if applicable)
  • I have followed naming conventions/patterns in the surrounding code
  • All code in src/services/ uses repositories implementations for database calls, filesystem operations, etc.
  • All code in src/repositories/ is pretty basic/simple and does not have any immich specific logic (that belongs in src/services/)

Copy link
Contributor

github-actions bot commented Jul 31, 2025

Label error. Requires exactly 1 of: changelog:.*. Found: 🖥️web. A maintainer will add the required label.

@nicokaiser
Copy link
Contributor Author

Possibly related issues:

immich-app/justified-layout#9
#19226
#19150

FYI @mertalev

@nicokaiser
Copy link
Contributor Author

This PR also adjusts the (commented out) WASM code, since the interface seems to have changed (assets may return their ratio).

I could not get the WASM version running at all, even when adding await init() at the beginning (which I assume is the problem for Safari etc.), then browser just complains about "WebAssembly.instantiate(): expected magic word 00 61 73 6d, found 3c 21 64 6f @+0 (500)".

But since the algorithm is now the same, I assume the WASM version would have the same optical behaviour than this PR.

So it remains to be discussed whether we can solve the "line not finished" problem, since it does not look very nice, especially in shared albums with many assets of the same date.

@nicokaiser nicokaiser changed the title feat: use JS implementation of @immich-app/justified-layout feat(web): use JS implementation of @immich-app/justified-layout Jul 31, 2025
@alextran1502
Copy link
Member

Thank you for the PR, however, I am hesitant to merge the PR at this time, partly due to the sophistication in the timeline implementation, which could open up edge case errors. Secondly, the WASM version still has some quirks that remain unresolved. Finally, in the demo video, you can see that there are bad cases of wrapping

image

@nicokaiser
Copy link
Contributor Author

I completely understand that. Because of the WASM quirks I tried to provide this as JS-only implementation of the same algorithm (to have a good fallback which gives the same results).

The bad wrapping needs to be addressed in the justified-layout repo, I'll open an issue.

@nicokaiser
Copy link
Contributor Author

I updated my example and opened immich-app/justified-layout#20

@mertalev
Copy link
Member

mertalev commented Jul 31, 2025

I'd suggest starting with increasing the tolerance since it gives the algorithm more flexibility to get filled rows (0.3 was the number that I landed on earlier).

@mertalev
Copy link
Member

mertalev commented Jul 31, 2025

There is room for improvement following immich-app/justified-layout#20 . I think it's fine to have a JS version as a fallback, though it's better for it to live in the justified layout repo rather than here.

@nicokaiser nicokaiser force-pushed the feat/justified-layout-js branch from e417e0c to 7c8ab97 Compare August 8, 2025 20:26
@nicokaiser
Copy link
Contributor Author

I updated the JS implementation to the lastest WASM version (which is not released yet).
The heightTolerance is still at 0.15, which is far too small. In my experiments, the tolerance needs to be rather 0.5 to avoid problems, especially when the viewport is close to the breakpoint (where heightTolerance switches from 235 to 100).

@nicokaiser nicokaiser mentioned this pull request Aug 26, 2025
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants