diff --git a/manifest.ts b/manifest.ts index e086837..701a15c 100644 --- a/manifest.ts +++ b/manifest.ts @@ -40,7 +40,7 @@ import type { PluginManifest } from '@@/plugins/manifest'; */ export const manifest: PluginManifest = { publisher: 'officerdev', - version: '2026.09.05.02', + version: '2026.09.05.03', platform: '>=1.0.0', label: 'Karakeep', diff --git a/web/BookmarksPanel.tsx b/web/BookmarksPanel.tsx index 1aabf5b..af5b648 100644 --- a/web/BookmarksPanel.tsx +++ b/web/BookmarksPanel.tsx @@ -79,15 +79,46 @@ export const BookmarksPanel = () => { } }, []); + /* + `tick` exists to re-ask the same question, which is different from asking a new one. + + Everything else in this effect is a NEW question — a different list, different words. This one is + the poll below saying "ask that again", and it has to go through the same path so a refresh cannot + drift from a first load. + */ + const [tick, setTick] = useState(0); + // Debounced: long enough that a five-letter word is one query instead of five, short enough that the // grid moves while you type. useEffect(() => { const timer = setTimeout(() => void load(list, query), 250); return () => clearTimeout(timer); - }, [list, query, load]); + }, [list, query, load, tick]); const waiting = rows?.filter((b) => b.content?.type === 'link' && b.content.crawlStatus === 'pending').length ?? 0; + /* + ── While anything is still being fetched, ask again ── + + Without this the panel loads once and never moves. That is wrong here in a way it would not be for + most lists, because a bookmark is not finished when it is saved: Karakeep crawls it afterwards, and + the picture, the description and the favicon all arrive minutes later. + + Worse, the page most likely to be full of unfinished ones is the FIRST page — newest first, and the + crawler works through the backlog — so a fresh import shows a grid of "fetching…" that never + resolves until somebody reloads by hand. Measured on this instance: 79 of 97 crawled, and all 16 + pending sitting on page one. + + The poll is self-limiting and needs no switch: it only re-arms while something is actually pending, + so a settled library makes no requests at all. Five seconds because a crawl takes a few and nobody + is watching the clock. + */ + useEffect(() => { + if (waiting === 0) return; + const timer = setTimeout(() => setTick((n) => n + 1), 5000); + return () => clearTimeout(timer); + }, [waiting, tick]); + return (
@@ -105,7 +136,11 @@ export const BookmarksPanel = () => { A bookmark has no picture until Karakeep has crawled it, and on a fresh import that is most of them for a while. Without this the panel looks broken at exactly the moment it is working. */} - {waiting > 0 && {waiting} still being fetched — pictures appear as they finish.} + {waiting > 0 && ( + + {waiting} still being fetched — this updates itself as they finish. + + )}