CMMS fork: hand the board photos to the AI bridge
The board photos live in pcb-retrace's IndexedDB, not as CMMS
attachments, so the assistant had no way to see them and burned all its
tool-call rounds looking. cmms-bridge.js now downscales each project
photo (long edge ~1400px, JPEG q0.8 - a phone photo lands under ~300KB)
and sends them alongside the buffer in the cmms-ai-editor-buffer reply
as `images: [{id,name,mime,data_base64}]` (max 6). The CMMS
read_board_image tool surfaces one at a time.
This commit is contained in:
parent
04d29dc0ac
commit
40373493b5
1 changed files with 44 additions and 1 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue