File size: 5,158 Bytes
81936e8
fe3b41d
 
 
 
81936e8
fe3b41d
 
81936e8
 
9f32d99
 
 
 
 
fe3b41d
 
 
 
 
 
 
 
 
81936e8
fe3b41d
 
 
 
81936e8
fe3b41d
 
81936e8
 
9f32d99
 
fe3b41d
 
9f32d99
 
fe3b41d
 
81936e8
9f32d99
 
 
 
 
 
81936e8
 
 
 
 
 
 
 
 
 
 
 
fe3b41d
 
81936e8
 
 
 
 
 
9f32d99
 
 
 
 
 
 
 
 
 
 
 
81936e8
 
 
 
fe3b41d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// The server starts the generation clock after weights and GPU admission.
const root = document.getElementById('st-app');
if (!root || root.dataset.generationProgressInstalled) return;
root.dataset.generationProgressInstalled = 'true';
let token = '';
let started = null;
let elapsedFloor = 0;
let lastState = '';
let clock = 'waiting';
let phase = 'queued';
let queueState = 'checking';
let queuePosition = null;
let queueSize = null;
let queueAhead = null;
let queueRunning = null;

const text = (element, value) => {
    if (element && element.textContent !== value) element.textContent = value;
};
const paint = () => {
    const card = root.querySelector('.st-generation');
    if (!card) {
        token = '';
        lastState = '';
        started = null;
        return;
    }
    if (card.dataset.token !== token) {
        token = card.dataset.token;
        started = null;
        elapsedFloor = 0;
        lastState = '';
        clock = 'waiting';
        phase = card.dataset.phase || 'queued';
        queueState = 'checking';
        queuePosition = queueSize = queueAhead = queueRunning = null;
    }
    const state = root.querySelector('#st-generation-state [data-token]');
    const signature = state ? [state.dataset.token, state.dataset.status, state.dataset.label, state.dataset.elapsedSeconds, state.dataset.clock,
        state.dataset.queueState, state.dataset.queuePosition, state.dataset.queueSize, state.dataset.queueAhead, state.dataset.queueRunning].join('|') : '';
    if (state && signature !== lastState && state.dataset.token === token) {
        lastState = signature;
        phase = state.dataset.status;
        queueState = state.dataset.queueState || 'checking';
        const count = value => value !== undefined && /^\d+$/.test(value) ? Number(value) : null;
        queuePosition = count(state.dataset.queuePosition);
        queueSize = count(state.dataset.queueSize);
        queueAhead = count(state.dataset.queueAhead);
        queueRunning = count(state.dataset.queueRunning);
        const nextClock = state.dataset.clock || 'waiting';
        const serverElapsed = Number(state.dataset.elapsedSeconds) || 0;
        if (nextClock === 'running') {
            // A delayed poll must not rewind a clock that is already running.
            const localElapsed = started === null ? 0 : (performance.now() - started) / 1000;
            elapsedFloor = Math.max(serverElapsed, localElapsed);
            started = performance.now() - elapsedFloor * 1000;
        } else {
            elapsedFloor = serverElapsed;
            started = null;
        }
        clock = nextClock;
        text(card.querySelector('.st-generation-stage'), 'Stage: ' + state.dataset.label);
    }
    const preparing = phase === 'preparing';
    const timed = clock !== 'waiting';
    text(card.querySelector('h3'), preparing ? 'Loading weights' : timed ? 'Generating 3D' : 'Waiting for GPU');
    card.querySelector('.st-generation-preparing').hidden = !preparing;
    card.querySelector('.st-generation-timing').hidden = !timed;
    card.querySelector('.st-generation-stage').hidden = !timed;
    card.querySelector('.st-generation-queue').hidden = timed || preparing;
    let positionText = queueState === 'checking' ? 'Checking queue position…' : 'Queue position is currently unavailable.';
    let detailText = '';
    if (queueState === 'waiting' && queuePosition > 0 && queueSize >= queuePosition && queueAhead !== null && queueRunning !== null) {
        positionText = `Your queue position: #${queuePosition} of ${queueSize} waiting`;
        detailText = `${queueAhead} ${queueAhead === 1 ? 'task' : 'tasks'} waiting ahead · ${queueRunning} running`;
    } else if (queueState === 'admitted') {
        positionText = 'Your task has reached the GPU worker.';
        detailText = 'Starting…';
    }
    text(card.querySelector('.st-generation-queue-position'), positionText);
    text(card.querySelector('.st-generation-queue-detail'), detailText);
    card.classList.toggle('waiting', false);
    if (!timed) return;
    const elapsed = started === null ? elapsedFloor : Math.max(elapsedFloor, (performance.now() - started) / 1000);
    const estimate = Number(card.dataset.estimate) || 360;
    const percent = Math.min(95, elapsed / estimate * 100);
    text(card.querySelector('.st-generation-elapsed'), `Elapsed: ${Math.floor(elapsed)} s`);
    const track = card.querySelector('.st-estimate-track');
    track.setAttribute('aria-valuenow', String(Math.floor(percent)));
    track.setAttribute('aria-valuetext', `Elapsed ${Math.floor(elapsed)} seconds; estimated total ${estimate} seconds`);
    card.querySelector('.st-estimate-fill').style.width = `${percent}%`;
    const overdue = elapsed >= estimate;
    card.querySelector('.st-generation-overdue').hidden = !overdue;
    card.classList.toggle('waiting', overdue);
};
const observer = new MutationObserver(paint);
observer.observe(root, {childList: true, subtree: true});
const interval = window.setInterval(paint, 250);
paint();
window.addEventListener('pagehide', () => {
    observer.disconnect();
    window.clearInterval(interval);
}, {once: true});