← back to Dw Photo Capture

public/caps.html

76 lines

<!doctype html><html><head><script>
// Credential-safe fetch guard (fleet drop-in) — if this page is opened with
// credentials in the URL (a saved bookmark / Chrome-remembered basic-auth:
// https://user:pass@host/…), the browser poisons document.baseURI, so any
// relative or root-relative fetch('/api/…') throws "Request cannot be constructed
// from a URL that includes credentials" and callers silently fall to an empty
// state. Resolve every non-absolute request URL against the credential-free
// location instead of baseURI. Placed first so it wraps window.fetch before any
// app script runs. Ref: creds-in-url-fetch-guard-fleet-pattern.
(function () {
  var _fetch = window.fetch.bind(window);
  var cleanBase = function () { return location.origin + location.pathname; };
  window.fetch = function (input, init) {
    try {
      if (typeof input === 'string' && !/^[a-z]+:\/\//i.test(input) && input.indexOf('//') !== 0) {
        input = new URL(input, cleanBase()).href;
      } else if (input instanceof Request && !/^[a-z]+:\/\//i.test(input.url)) {
        input = new Request(new URL(input.url, cleanBase()).href, input);
      }
    } catch (_) { /* fall through to native */ }
    return _fetch(input, init);
  };
})();
</script><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title>DW camera capability probe</title>
<style>
  body{margin:0;background:#0b0a08;color:#eee;font:15px/1.5 -apple-system,system-ui,sans-serif;padding:18px}
  h1{font-size:18px;margin:0 0 4px}.sub{color:#9a917d;font-size:13px;margin-bottom:14px}
  #v{width:100%;border-radius:12px;background:#000;margin-bottom:12px}
  .row{display:flex;justify-content:space-between;gap:10px;padding:9px 0;border-bottom:1px solid #ffffff14;font-size:14px}
  .k{color:#b7ad98}.y{color:#2bd06a;font-weight:700}.n{color:#ff6a6a;font-weight:700}
  button{width:100%;padding:14px;margin-top:14px;border:none;border-radius:12px;background:#2bd06a;color:#032;font-size:16px;font-weight:700}
  #done{color:#2bd06a;text-align:center;margin-top:12px;font-weight:700;display:none}
</style></head><body>
<h1>DW camera probe</h1>
<div class="sub">Point the back camera at anything, then tap “Send to Claude”. This tells us if your device can measure size by moving the phone (no card).</div>
<video id="v" autoplay playsinline muted></video>
<div id="out"></div>
<button id="send">Send to Claude</button>
<div id="done">✓ Sent — tell Claude “probe done”.</div>
<script>
const $=s=>document.querySelector(s); let CAPS={};
function row(k,ok,val){ return `<div class="row"><span class="k">${k}</span><span class="${ok?'y':'n'}">${val}</span></div>`; }
(async()=>{
  const rep={ua:navigator.userAgent, motion:typeof DeviceMotionEvent!=='undefined',
    motionPerm:(typeof DeviceMotionEvent!=='undefined' && typeof DeviceMotionEvent.requestPermission==='function')};
  try{
    if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia){
      const chrome=/CriOS|FxiOS|EdgiOS/.test(navigator.userAgent);
      throw new Error(chrome?'This is Chrome/other app — iOS only allows the camera in SAFARI. Open in Safari.':'camera API unavailable (open over https in Safari)');
    }
    const st=await navigator.mediaDevices.getUserMedia({video:{facingMode:{ideal:'environment'}}});
    $('#v').srcObject=st; const tr=st.getVideoTracks()[0];
    const caps=tr.getCapabilities?tr.getCapabilities():{}, set=tr.getSettings?tr.getSettings():{};
    rep.resolution=(set.width||'?')+'x'+(set.height||'?');
    rep.zoom=caps.zoom?JSON.stringify(caps.zoom):null;
    rep.focusDistance=caps.focusDistance?JSON.stringify(caps.focusDistance):null;
    rep.focusDistanceNow=(set.focusDistance!==undefined)?set.focusDistance:null;
    rep.focusMode=caps.focusMode?JSON.stringify(caps.focusMode):null;
    rep.capsKeys=Object.keys(caps);
  }catch(e){ rep.cameraErr=e.message; }
  CAPS=rep;
  $('#out').innerHTML =
    row('Camera', !rep.cameraErr, rep.cameraErr?('ERR '+rep.cameraErr):(rep.resolution||'ok'))+
    row('Focus distance (→ card-free!)', !!rep.focusDistance, rep.focusDistance?('YES '+rep.focusDistance):'no')+
    row('Live focus distance', rep.focusDistanceNow!=null, rep.focusDistanceNow!=null?String(rep.focusDistanceNow):'no')+
    row('Zoom control', !!rep.zoom, rep.zoom?('yes '+rep.zoom):'no')+
    row('Motion (parallax fallback)', rep.motion, rep.motion?(rep.motionPerm?'yes (needs tap)':'yes'):'no');
})();
$('#send').addEventListener('click',async()=>{
  try{ await fetch('/api/caps',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(CAPS)}); }catch(e){}
  $('#done').style.display='block'; $('#send').style.display='none';
});
</script></body></html>