Skip to content

Conversation

@joelim-work
Copy link
Collaborator

@joelim-work joelim-work commented Dec 8, 2025

cc #2287

Currently preloading is based on a FIFO approach (really just reading files from the preload channel). However might not be ideal when scrolling through files quickly, since newer files will have to wait until all the previous files have finished loading.

This PR changes the preloading mechanism to use a stack-based approach instead.

@veltza
Copy link
Contributor

veltza commented Dec 11, 2025

I've been testing this for a few days and I can say that the new stack-based preloading mechanism feels much more responsive when previewing images and jumping from one image directory to another. There is no longer a very long wait time before the first image appears on the screen and that is a very good improvement.

But this is not just a performance improvement, as it also fixes the ctrl-r bug that caused some files to get stuck in the loading state. So this PR kills two birds with one stone, which is excellent.


However, I found a new bug, which is a variation of the loading bug. I can only reproduce it when previewing images with chafa and sixels, so I think it is limited to just that use case. It is very random and difficult to reproduce.

When I scroll down through the images by holding j for a while and then just pressing k, sometimes only a blank screen appears. There is no loading text or image, and that missing image never appears on the screen.

I debugged the issue and found that the blank screen is caused by a race condition because the missing image is loaded properly and it is even printed here:

	fmt.Fprint(os.Stderr, "\033[?2026h") // Begin synchronized update
	fmt.Fprint(os.Stderr, "\0337")       // Save cursor position
	fmt.Fprint(os.Stderr, b.String())    // Write data
	fmt.Fprint(os.Stderr, "\0338")       // Restore cursor position
	fmt.Fprint(os.Stderr, "\033[?2026l") // End synchronized update

But right after that, another image comes in and calls ui.sxScreen.clearSixel() here and goes inside this if statement and calls screen.LockRegion() there:

	if sxs.lastFile != "" && (filePath != sxs.lastFile || *win != sxs.lastWin || sxs.forceClear) {
		screen.LockRegion(sxs.lastWin.x, sxs.lastWin.y, sxs.lastWin.w, sxs.lastWin.h, false)
	}

Then the image calls preview.printReg() here and goes inside this if statement. But since previewLoading is false, it does nothing but exit the function:

	if reg.loading {
		if previewLoading {
			st = st.Reverse(true)
			win.print(screen, 2, 0, st, "loading...")
		}
		return
	}

So that another image is clearly trying to print a loading screen, but in its final steps it realizes that it shouldn't be printing the loading screen. Unfortunately, the damage has already been done, because it has already removed the sixel that was supposed to be printed on the screen.

Since you know the code inside out, I'll let you decide how to resolve this race condition.

@joelim-work
Copy link
Collaborator Author

joelim-work commented Dec 12, 2025

I've been testing this for a few days and I can say that the new stack-based preloading mechanism feels much more responsive when previewing images and jumping from one image directory to another. There is no longer a very long wait time before the first image appears on the screen and that is a very good improvement.

But this is not just a performance improvement, as it also fixes the ctrl-r bug that caused some files to get stuck in the loading state. So this PR kills two birds with one stone, which is excellent.

Thanks, I think this is a large improvement too. I don't see a reason to prefer the current queue-based implementation, so I will remove the draft status from this PR.


As for this new blank sixel issue, I think this is actually separate existing issue. I can reproduce it even with preloading disabled (hopefully we are looking at the same problem here).

So what happens is that lf stores the last sixel image drawn in a variable so it doesn't repeatedly draw it again if nothing has changed. However when selecting a new file, if the preview is still loading then the preview will be cleared to display loading..., but the variable itself doesn't get cleared. I have created a PR for this: #2301

@joelim-work joelim-work marked this pull request as ready for review December 12, 2025 06:31
@veltza
Copy link
Contributor

veltza commented Dec 12, 2025

As for this new blank sixel issue, I think this is actually separate existing issue. I can reproduce it even with preloading disabled (hopefully we are looking at the same problem here).

Ok, I just thought this PR caused it because I haven't noticed this bug before.

joelim-work and others added 2 commits December 13, 2025 17:34
Co-authored-by: veltza <106755522+veltza@users.noreply.github.com>
@joelim-work
Copy link
Collaborator Author

Regarding the missing sixel bug, I have now merged #2301, and merged it into this PR branch.

@veltza
Copy link
Contributor

veltza commented Dec 13, 2025

Everything seems to be working great now. I think this is ready to be merged.

@joelim-work joelim-work merged commit af62d16 into gokcehan:master Dec 15, 2025
32 checks passed
@joelim-work joelim-work deleted the preload-stack branch December 15, 2025 00:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants