← back to Dw Photo Capture
TK-12162: capture at high res on all simple-* pages + route minimal/wizard/probooth
14e31944a15ca6cdcb6fc49908d34e4617a2ae48 · 2026-09-24 13:53:39 -0700 · Steve Abrams
Wizard, Instant and Minimal now match Pro Booth: ask the camera for 4096x3072,
keep up to a 2000px long edge, JPEG 0.92 (was 1920x1080/1440, 1600px cap, 0.85).
server.js served only /simple-instant; the other three pages 404'd. One fixed
allow-list regex now serves all four.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WCvaA6QQCJp2LMdcXyrUD
Files touched
M public/simple-instant.htmlM public/simple-minimal.htmlM public/simple-wizard.htmlM server.js
Diff
commit 14e31944a15ca6cdcb6fc49908d34e4617a2ae48
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 24 13:53:39 2026 -0700
TK-12162: capture at high res on all simple-* pages + route minimal/wizard/probooth
Wizard, Instant and Minimal now match Pro Booth: ask the camera for 4096x3072,
keep up to a 2000px long edge, JPEG 0.92 (was 1920x1080/1440, 1600px cap, 0.85).
server.js served only /simple-instant; the other three pages 404'd. One fixed
allow-list regex now serves all four.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WCvaA6QQCJp2LMdcXyrUD
---
public/simple-instant.html | 6 +++---
public/simple-minimal.html | 8 +++++---
public/simple-wizard.html | 6 +++---
server.js | 10 ++++++----
4 files changed, 17 insertions(+), 13 deletions(-)
diff --git a/public/simple-instant.html b/public/simple-instant.html
index 8e5b946..c0d1524 100644
--- a/public/simple-instant.html
+++ b/public/simple-instant.html
@@ -273,7 +273,7 @@
}
try{
if (stream) stream.getTracks().forEach(function(t){ t.stop(); });
- stream = await navigator.mediaDevices.getUserMedia({ video:{ facingMode:{ ideal:f }, width:{ ideal:1920 }, height:{ ideal:1440 } }, audio:false });
+ stream = await navigator.mediaDevices.getUserMedia({ video:{ facingMode:{ ideal:f }, width:{ ideal:4096 }, height:{ ideal:3072 } }, audio:false });
}catch(e){
try{ stream = await navigator.mediaDevices.getUserMedia({ video:true, audio:false }); }
catch(e2){ toast('Camera unavailable — check permissions.'); return; }
@@ -323,10 +323,10 @@
busy = true; shutterBtn.disabled = true;
var vw = video.videoWidth || 1280, vh = video.videoHeight || 960;
- var maxDim = 1600, scale = Math.min(1, maxDim / Math.max(vw, vh));
+ var maxDim = 2000, scale = Math.min(1, maxDim / Math.max(vw, vh));
canvas.width = Math.round(vw * scale); canvas.height = Math.round(vh * scale);
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
- capturedDataUrl = canvas.toDataURL('image/jpeg', 0.86);
+ capturedDataUrl = canvas.toDataURL('image/jpeg', 0.92);
// ── SPEED: fire the REAL network call the instant we have pixels — before any
// flash/develop UI even starts. The cosmetic animation below runs on its own
diff --git a/public/simple-minimal.html b/public/simple-minimal.html
index dfbe554..060f3c8 100644
--- a/public/simple-minimal.html
+++ b/public/simple-minimal.html
@@ -238,7 +238,7 @@
function startCamera(){
permWrap.classList.remove('on');
if (stream) { stream.getTracks().forEach(function(t){ t.stop(); }); stream = null; }
- var constraints = { audio:false, video:{ facingMode:{ ideal:facing }, width:{ideal:1920}, height:{ideal:1080} } };
+ var constraints = { audio:false, video:{ facingMode:{ ideal:facing }, width:{ideal:4096}, height:{ideal:3072} } };
navigator.mediaDevices.getUserMedia(constraints).then(function(s){
stream = s; camEl.srcObject = s; camEl.classList.toggle('mirror', facing==='user');
}).catch(function(e){
@@ -293,12 +293,14 @@
// Another" needs no getUserMedia round trip either — just hide the frozen frame. ────────
var shotCanvas = document.createElement('canvas');
function captureFrame(){
- var w = camEl.videoWidth || 1280, h = camEl.videoHeight || 960;
+ var vw = camEl.videoWidth || 1280, vh = camEl.videoHeight || 960;
+ var k = Math.min(1, 2000 / Math.max(vw, vh)); // same 2000px max edge as Pro Booth
+ var w = Math.round(vw * k), h = Math.round(vh * k);
shotCanvas.width = w; shotCanvas.height = h;
var ctx = shotCanvas.getContext('2d');
if (facing === 'user') { ctx.translate(w,0); ctx.scale(-1,1); }
ctx.drawImage(camEl, 0, 0, w, h);
- return shotCanvas.toDataURL('image/jpeg', 0.85);
+ return shotCanvas.toDataURL('image/jpeg', 0.92);
}
var tStart = 0;
diff --git a/public/simple-wizard.html b/public/simple-wizard.html
index 7d4a91c..01b14ef 100644
--- a/public/simple-wizard.html
+++ b/public/simple-wizard.html
@@ -300,7 +300,7 @@
throw new Error('This device/browser can\'t access the camera. Try Safari on iPhone/iPad or Chrome on Android.');
}
const constraints = [
- { video:{ facingMode:{ ideal:'environment' }, width:{ ideal:1920 }, height:{ ideal:1440 } }, audio:false },
+ { video:{ facingMode:{ ideal:'environment' }, width:{ ideal:4096 }, height:{ ideal:3072 } }, audio:false },
{ video:{ facingMode:{ ideal:'environment' } }, audio:false },
{ video:true, audio:false }
];
@@ -384,12 +384,12 @@
const canvas = $('snapCanvas');
// downscale to a sane max edge — keeps the base64 payload small so the (already-fast) round
// trips to /api/extract and /api/create-item aren't padded by an unnecessarily huge JPEG.
- const MAXW = 1600;
+ const MAXW = 2000; // same max edge as Pro Booth
const vw = v.videoWidth || 1280, vh = v.videoHeight || 960;
const scale = Math.min(1, MAXW / Math.max(vw, vh));
canvas.width = Math.round(vw * scale); canvas.height = Math.round(vh * scale);
canvas.getContext('2d').drawImage(v, 0, 0, canvas.width, canvas.height);
- capturedDataUrl = canvas.toDataURL('image/jpeg', 0.85);
+ capturedDataUrl = canvas.toDataURL('image/jpeg', 0.92);
mark('frameGrabbed');
$('reviewImg').src = capturedDataUrl;
diff --git a/server.js b/server.js
index 7491877..1290a93 100644
--- a/server.js
+++ b/server.js
@@ -1337,10 +1337,12 @@ const appHandler = (req, res) => {
// ── TK-12162 "Instant Film" concept — additive, self-contained page, own route (mirrors /batch
// below). NOT wired into any nav/menu; reuses the EXISTING /api/extract + /api/create-item
- // endpoints as-is. Local exploration only — never linked from index.html, never deployed.
- if (u.pathname === '/simple-instant' || u.pathname === '/simple-instant.html') {
- const f = path.join(ROOT, 'public/simple-instant.html');
- if (!fs.existsSync(f)) return send(res, 404, { err: 'simple-instant.html missing' });
+ // endpoints as-is. Not linked from index.html. The other three TK-12162 concept pages
+ // (minimal, wizard, probooth) are served by the same fixed allow-list.
+ const _simple = /^\/(simple-(?:instant|minimal|wizard|probooth))(?:\.html)?$/.exec(u.pathname);
+ if (_simple) {
+ const f = path.join(ROOT, 'public', _simple[1] + '.html');
+ if (!fs.existsSync(f)) return send(res, 404, { err: _simple[1] + '.html missing' });
return res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' }), res.end(fs.readFileSync(f, 'utf8'));
}
← 2b1d006 Consolidate SKU field cleanup into shared field-clean.js hel
·
back to Dw Photo Capture
·
TK-12162: full-res native-camera capture on all simple-* pag 4d1311b →