Skip to content

Commit d073488

Browse files
authored
Merge pull request #60 from Unity-Lab-AI/codex/add-background-job-for-image-request-tracking
Add background polling to resolve placeholder images
2 parents 8ce0838 + 2140928 commit d073488

2 files changed

Lines changed: 93 additions & 0 deletions

File tree

js/ui/imagePoller.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { image as polliImage } from '../polliLib/src/image.js';
2+
3+
let imageFn = polliImage;
4+
let pollIntervalMs = 2000;
5+
let timeoutMs = 20000;
6+
let fallbackSrc = 'https://via.placeholder.com/512?text=Image+Unavailable';
7+
8+
const pending = new Map();
9+
let timer = null;
10+
11+
export function trackPlaceholder(img, { prompt, model, width, height } = {}) {
12+
if (!img) return;
13+
pending.set(img, { prompt, model, width, height, start: Date.now() });
14+
if (!timer) {
15+
timer = setInterval(() => {
16+
checkPending().catch(() => {});
17+
}, pollIntervalMs);
18+
}
19+
}
20+
21+
async function checkPending() {
22+
for (const [img, info] of pending) {
23+
if (Date.now() - info.start > timeoutMs) {
24+
img.src = fallbackSrc;
25+
img.classList?.remove?.('placeholder');
26+
pending.delete(img);
27+
continue;
28+
}
29+
try {
30+
const data = await imageFn(info.prompt, { model: info.model, width: info.width, height: info.height, json: true });
31+
if (data && data.url) {
32+
img.src = data.url;
33+
img.classList?.remove?.('placeholder');
34+
pending.delete(img);
35+
}
36+
} catch (err) {
37+
img.src = fallbackSrc;
38+
img.classList?.remove?.('placeholder');
39+
pending.delete(img);
40+
}
41+
}
42+
if (pending.size === 0 && timer) {
43+
clearInterval(timer);
44+
timer = null;
45+
}
46+
}
47+
48+
export function _setImageFn(fn) {
49+
imageFn = fn;
50+
}
51+
52+
export function _configure({ intervalMs, timeout, fallback } = {}) {
53+
if (intervalMs != null) pollIntervalMs = intervalMs;
54+
if (timeout != null) timeoutMs = timeout;
55+
if (fallback != null) fallbackSrc = fallback;
56+
if (timer) {
57+
clearInterval(timer);
58+
timer = setInterval(() => {
59+
checkPending().catch(() => {});
60+
}, pollIntervalMs);
61+
}
62+
}

tests/site-image-poller.mjs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import assert from 'assert/strict';
2+
import { trackPlaceholder, _configure, _setImageFn } from '../js/ui/imagePoller.js';
3+
4+
let calls = 0;
5+
_setImageFn(async () => {
6+
calls++;
7+
if (calls === 1) return {}; // still pending
8+
return { url: 'https://example.com/final.png' };
9+
});
10+
11+
_configure({ intervalMs: 5, timeout: 100 });
12+
13+
const img = {
14+
src: 'about:blank',
15+
classList: {
16+
set: new Set(['placeholder']),
17+
remove(cls) { this.set.delete(cls); },
18+
contains(cls) { return this.set.has(cls); }
19+
}
20+
};
21+
22+
trackPlaceholder(img, { prompt: 'test', model: 'foo' });
23+
24+
// wait long enough for two polling cycles
25+
await new Promise(r => setTimeout(r, 30));
26+
await new Promise(r => setTimeout(r, 30));
27+
28+
assert.equal(img.src, 'https://example.com/final.png');
29+
assert.equal(img.classList.contains('placeholder'), false);
30+
31+
console.log('image-poller test passed');

0 commit comments

Comments
 (0)