← Files Velox TriFit API DocsARCHIVED FILE

web/app.mjs

4.8 KB · Nov 10, 2026 · 07:03 UTC

↓ Download file

const form = document.querySelector('#try-on');
const button = document.querySelector('#submit');
const status = document.querySelector('#status');
const base = new URL('./', location.href);
const storageKey = 'velox-demo-pending-request';
let pending = sessionStorage.getItem(storageKey);
let configured = false;
async function request(path, options) {
  const response = await fetch(new URL(path.replace(/^\//, ''), base), { ...options, signal: AbortSignal.timeout(120000), credentials: 'same-origin' });
  const data = await response.json();
  if (!response.ok) throw Object.assign(new Error(data.error || 'Request could not be confirmed.'), { notAccepted: data.notAccepted === true });
  return data;
}
function display(state) {
  const descriptions = {
    uploading: 'Uploading your inputs. No generation has started yet.',
    queued: `In the shared demo queue · position ${state.position}.`,
    running: 'Your DressMe request has been accepted. Waiting for its result…',
    succeeded: 'Your real DressMe result is ready.',
    failed: 'This generation failed. It will not be retried automatically.',
    upload_failed: 'The upload could not be completed. No generation was submitted. Contact the demo operator before retrying.',
    submitting: 'Confirming the generation submission…',
    needs_review: 'The request needs operator review. It has not been submitted again.'
  };
  status.textContent = descriptions[state.status] || 'Checking your request…';
  if (state.queuePaused && state.status === 'queued') status.textContent += ' The queue is paused for review.';
}
async function poll() {
  if (!pending) return;
  document.querySelector('#receipt').textContent = `Your request reference: ${pending}`;
  try {
    const state = await request(`/api/requests/${pending}`); display(state);
    if (state.status === 'succeeded') {
      const result = await request(`/api/requests/${pending}/result`);
      document.querySelector('#result img').src = result.url;
      document.querySelector('#result').hidden = false;
    }
    if (['succeeded', 'failed'].includes(state.status)) {
      pending = null; sessionStorage.removeItem(storageKey); button.disabled = !configured; button.textContent = 'Join the demo queue ↗'; return;
    }
    if (['needs_review', 'upload_failed'].includes(state.status)) return;
    setTimeout(poll, 3000);
  } catch (error) {
    status.textContent = `${error.message} Keep the request reference and reload to check it. Do not submit again.`;
  }
}
async function file(input) {
  const selected = input.files[0];
  if (!selected || !['image/png', 'image/jpeg', 'image/webp'].includes(selected.type) || selected.size > 5 * 1024 * 1024 || !selected.size) throw new Error('Choose two PNG, JPEG or WebP images of at most 5 MiB each.');
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onerror = () => reject(new Error('Image could not be read.'));
    reader.onload = () => resolve({ type: selected.type, base64: reader.result.split(',')[1] });
    reader.readAsDataURL(selected);
  });
}
form.addEventListener('submit', async event => {
  event.preventDefault(); if (pending || !configured || !form.reportValidity()) return;
  button.disabled = true;
  try {
    const product = await file(form.elements.product); const personal = await file(form.elements.personal);
    pending = crypto.randomUUID(); sessionStorage.setItem(storageKey, pending);
    document.querySelector('#result').hidden = true;
    document.querySelector('#receipt').textContent = `Your request reference: ${pending}`;
    status.textContent = 'Sending your images. Keep this page open…';
    const state = await request('/api/requests', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: pending, product, personal, consent: true }) });
    display(state); form.reset(); await poll();
  } catch (error) {
    if (error.notAccepted) { pending = null; sessionStorage.removeItem(storageKey); document.querySelector('#receipt').textContent = ''; }
    status.textContent = pending ? `${error.message} Keep your request reference; reload to check it before retrying.` : error.message;
    button.disabled = Boolean(pending);
  }
});
try {
  const session = await request('/api/session'); configured = session.configured;
  document.querySelector('#gap').textContent = `${session.gapSeconds} seconds`;
  document.querySelector('#mode').textContent = configured ? 'Real DressMe integration · shared demo queue · one active generation. Usage may incur charges for the operator.' : 'Preview only — generation is disabled. Velox must supply the private demo configuration before this can generate real results.';
  button.disabled = !configured || Boolean(pending);
  if (configured && pending) await poll();
} catch { document.querySelector('#mode').textContent = 'Demo unavailable. No request has been sent.'; }

SHA-256: 060cc69c9241f1a0ca0a03c67b7271633d39881ba6bb1cb0eb0be3c51aaa27c4