fix(studio): the strip asks for the frame in view, and lands where it is put

The rail glided to the frame in hand. A glide across a roll of six hundred
fires a scroll event a frame, and every one of them asked the thumbnail
cache for another window: the tiles of the frames being looked at queued
behind two screens of margin, so the strip sat blank exactly where it was
being read while it drew frames nobody had reached. The rail now lands
where it is put, one window is asked for, and the frames in view are the
first asked for a tile.

The strip also keeps its height: `overflow-x: auto` makes the other axis a
scroller too, so a strip squeezed by the column hid its own frames rather
than spilling them. The photo above gives way instead.
This commit is contained in:
2026-10-15 17:52:37 +07:00
parent 164055a6c0
commit b2e2ebb266
4 changed files with 131 additions and 33 deletions
+14 -5
View File
@@ -860,6 +860,7 @@ export function Workspace() {
useEffect(() => {
if (!libPhotoId) {
setLibFolder([]);
setLibVisible([]);
return;
}
let alive = true;
@@ -879,10 +880,12 @@ export function Workspace() {
};
}, [libPhotoId]);
// One tile per frame, made the way the shelf makes them and out of the same
// cache: a folder the wall has just drawn is a strip already pointing at the
// tiles it drew.
const libUrls = useThumbUrls(libFolder);
// Only the frames the rail can actually reach are asked for their tile (see
// Filmstrip): the strip arrives with the stage rather than after the folder,
// and a scroll does not wait behind a decode of a frame nobody is looking
// at. The tiles are the shelf's own, out of the cache it draws from.
const [libVisible, setLibVisible] = useState<LibraryPhoto[]>([]);
const libUrls = useThumbUrls(libVisible);
// Recipes made in this session, guest ones included. A guest's copy lives
// here and nowhere else, so reloading the page is what deletes it.
const [created, setCreated] = useState<Recipe[]>([]);
@@ -4862,7 +4865,13 @@ export function Workspace() {
// nothing for a folder of one, where a strip of one says nothing.
filmstrip={
libPhotoId && libFolder.length > 1 ? (
<Filmstrip frames={libFolder} urls={libUrls} activeId={libPhotoId} onPick={(id) => void openLibraryPhoto(id)} />
<Filmstrip
frames={libFolder}
urls={libUrls}
activeId={libPhotoId}
onPick={openLibraryPhoto}
onVisible={setLibVisible}
/>
) : null
}
onCropRectChange={setCropRect}