Gate B: SvelteKit capture→mask→numbers→review UI with deterministic pipeline
- Scaffold SvelteKit app (svelte.config.js, vite.config.ts, app.html, app.d.ts) - Build full capture flow: photo input → mask overlay + 4 knobs → axis overlay + rotation → length/thickness/SKU inputs → review + upload to /api/v1/slabs - Uses existing lib/pipeline/* pure modules (mask, axis, units, crop, pricing, types) - Uses existing validation/sku.ts for SKU validation - Svelte 5 runes ($state, $effect, $derived) for reactive state - Canvas-based rendering for mask overlay, axis visualization, and 3:4 crop preview - Dockerfile updated for Vite production build with static Node server on :80 - All 54 existing tests pass, build succeeds
This commit is contained in:
+9
-5
@@ -7,11 +7,15 @@ RUN npm ci --ignore-scripts
|
||||
|
||||
COPY frontend/ .
|
||||
|
||||
# Build the SvelteKit app (stub for Gate B — will be updated when SvelteKit scaffolds)
|
||||
# For now, copy a stub HTML page. Vite build runs in production mode later.
|
||||
RUN mkdir -p build && \
|
||||
echo '<!DOCTYPE html><html><head><meta charset="utf-8"><title>SlabUploader</title></head><body><h1>SlabUploader — coming soon</h1><p>SvelteKit app stub. Update this Dockerfile when the SvelteKit app is scaffolded.</p></body></html>' > build/index.html
|
||||
RUN npm run build
|
||||
|
||||
# adapter-static outputs to build/
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["node", "-e", "const h=require('http'),fs=require('fs'),url=require('url');h.createServer((req,res)=>{const f='build/index.html';res.writeHead(200,{'Content-Type':'text/html'}).end(fs.readFileSync(f))}).listen(80);console.log('frontend on :80')"]
|
||||
CMD ["node", "-e", "\
|
||||
const h=require('http'),fs=require('fs'),url=require('url'),path=require('path');\
|
||||
const root='build';\
|
||||
const types={'/':'application/json','js':'application/javascript','css':'text/css','html':'text/html','json':'application/json','svg':'image/svg+xml','png':'image/png','jpg':'image/jpeg','jpeg':'image/jpeg','ico':'image/x-icon','woff2':'font/woff2','woff':'font/woff','ttf':'font/ttf','otf':'font/otf','map':'application/json'};\
|
||||
h.createServer((req,res)=>{const p=url.parse(req.url||'/').pathname||'/';const fp=path.join(root,p==='/'?'/index.html':p);try{const s=fs.statSync(fp);if(s.isDirectory()){fs.readFile(path.join(fp,'index.html'),'utf8',(e,d)=>{res.writeHead(200,{'Content-Type':'text/html'});res.end(d||'<h1>OK</h1>')})}else{const ext=path.extname(fp).slice(1);res.writeHead(200,{'Content-Type':types[ext]||'application/octet-stream'});fs.createReadStream(fp).pipe(res)}}catch(e){res.writeHead(404).end('Not found')}}).listen(80);\
|
||||
console.log('frontend static build on :80');\
|
||||
"]
|
||||
|
||||
Reference in New Issue
Block a user