diff --git a/docs/cmms-bridge.js b/docs/cmms-bridge.js index fdaedbc..b2ca6f9 100644 --- a/docs/cmms-bridge.js +++ b/docs/cmms-bridge.js @@ -160,6 +160,47 @@ return JSON.stringify(view); } + // The board photos live in pcb-retrace's IndexedDB (imported by the user or + // unpacked from the .pcbretrace.zip), NOT as CMMS attachments, so the + // assistant can't reach them with read_attachment_image. Downscale + JPEG + // each and hand them along with the buffer so a `read_board_image` tool can + // surface them. Long edge ~1400px, quality 0.8 - a phone photo lands well + // under 300KB. + async function encodeImage(blob, maxEdge) { + const bmp = await createImageBitmap(blob); + const s = Math.min(1, maxEdge / Math.max(bmp.width, bmp.height)); + const w = Math.max(1, Math.round(bmp.width * s)); + const h = Math.max(1, Math.round(bmp.height * s)); + const c = document.createElement('canvas'); + c.width = w; c.height = h; + c.getContext('2d').drawImage(bmp, 0, 0, w, h); + bmp.close(); + const out = await new Promise(res => c.toBlob(res, 'image/jpeg', 0.8)); + const b64 = await new Promise((res, rej) => { + const r = new FileReader(); + r.onload = () => res(String(r.result).split(',', 2)[1] || ''); + r.onerror = rej; + r.readAsDataURL(out); + }); + return { w, h, b64 }; + } + + async function buildProjectImages() { + if (!currentBomId || !bomImages.length) return []; + const out = []; + for (const img of bomImages.slice(0, 6)) { + try { + const enc = await encodeImage(img.blob, 1400); + if (enc.b64.length > 3 * 1024 * 1024) { trace('image too large after encode, skipping', img.id); continue; } + out.push({ id: img.id, name: img.name || (img.id + '.jpg'), mime: 'image/jpeg', data_base64: enc.b64 }); + } catch (e) { + trace('image encode failed', img.id, e && e.message); + } + } + trace('buildProjectImages:', out.length, 'of', bomImages.length, 'photos encoded'); + return out; + } + async function applyProjectPatch(patchJson) { const patch = JSON.parse(patchJson); if (!currentBomId) { status('Select or create a board first', true); return; } @@ -227,8 +268,10 @@ if (d.type === 'cmms-ai-editor-get-buffer') { trace('get-buffer received'); let buffer = null; + let images = []; try { buffer = await buildProjectView(); } catch (e) { trace('buildProjectView threw:', e && e.message); } - replyToChat(evt.source, { type: 'cmms-ai-editor-buffer', buffer: buffer }); + try { images = await buildProjectImages(); } catch (e) { trace('buildProjectImages threw:', e && e.message); } + replyToChat(evt.source, { type: 'cmms-ai-editor-buffer', buffer: buffer, images: images }); } else if (d.type === 'cmms-ai-editor-apply' && d.editor === 'pcb' && typeof d.new_source === 'string') { trace('apply received'); try {