// Notebook blueprints: starter designs, saved library and code sharing. function renderBlueprintTab(body) { const lib = libLoad(); let h = `

Select the Blueprint tool (5), drag over something you built to copy it, then click to place a ghost. Your bot builds ghosts from the materials in your bag when it is within reach. Right-click clears ghosts.

`; h += `

Starter designs

Optional. Tested builds to learn from or copy; place one as a ghost and your bot builds it from your bag. Clear the space first: ghosts wait for open air.

`; STARTER_DESIGNS.forEach((d, k) => { const bp = d.bp(); h += `
${esc(d.name)}${bp.bw}×${bp.bh} blocks
${esc(d.desc)}
Build cost: ${esc(costText(bpCost(bp), 1))}
`; }); h += '

Your blueprints

'; h += `
Clipboard
${GAME.clip ? `${GAME.clip.bw} × ${GAME.clip.bh} blocks, ${GAME.clip.blocks.length} pieces${GAME.clip.machines.length ? `, ${GAME.clip.machines.length} machines` : ''}${GAME.clip.molds.length ? `, ${GAME.clip.molds.length} molds` : ''}. Needs ${esc(costText(bpCost(GAME.clip), 1) || 'nothing')}` : 'Empty'}
`; h += `
Import a blueprint code
Paste a code that starts with DGBP2.
`; h += `

Library

`; if (!lib.length) h += `

No saved blueprints yet.

`; else { h += '
'; lib.forEach((it, k) => { const ok = bpValid(it.bp); h += `
${esc(it.name)}${ok ? `${it.bp.bw}×${it.bp.bh}` : 'old format'}
${ok ? esc(costText(bpCost(it.bp), 1) || 'free') : 'Made before the block grid. It cannot be placed.'}
${ok ? `` : ''}
`; }); h += '
'; } body.innerHTML = h; const sv = $('bp-save'); if (sv) sv.onclick = () => { dialog('Name this blueprint', '', [ { label: 'Cancel', cls: 'ghost' }, { label: 'Save', cls: 'primary', fn: () => { const n = ($('bp-name').value || 'Untitled').trim(); const l = libLoad(); l.push({ name: n, bp: GAME.clip }); if (libSave(l)) toast(`Saved “${n}”.`); renderNotebook(); } }, ]); setTimeout(() => $('bp-name') && $('bp-name').focus(), 30); }; const cc = $('bp-code'); if (cc) cc.onclick = () => copyCode(bpEncode(GAME.clip)); $('bp-import').onclick = () => { dialog('Import blueprint', '', [ { label: 'Cancel', cls: 'ghost' }, { label: 'Import', cls: 'primary', fn: () => { try { GAME.clip = bpDecode($('bp-paste').value); GAME.paste = true; hudDirty = true; toast('Blueprint ready. Click to place it.'); closeAllModals(); } catch (e) { toast(e.message); return true; } } }, ]); }; body.querySelectorAll('[data-bpload]').forEach(b => b.onclick = () => { GAME.clip = libLoad()[+b.dataset.bpload].bp; GAME.paste = true; hudDirty = true; closeAllModals(); toast('Blueprint ready. Click to place it.'); }); body.querySelectorAll('[data-starter]').forEach(b => b.onclick = () => { GAME.clip = STARTER_DESIGNS[+b.dataset.starter].bp(); GAME.paste = true; hudDirty = true; closeAllModals(); toast('Click to place it. R flips it.'); }); body.querySelectorAll('[data-bpcode]').forEach(b => b.onclick = () => copyCode(bpEncode(libLoad()[+b.dataset.bpcode].bp))); body.querySelectorAll('[data-bpdel]').forEach(b => b.onclick = () => { const l = libLoad(); l.splice(+b.dataset.bpdel, 1); libSave(l); renderNotebook(); }); } function copyCode(code) { const done = () => toast('Blueprint code copied.'); const fallback = () => { dialog('Blueprint code', `

Select the text and copy it.

`, [{ label: 'Done', cls: 'primary' }]); setTimeout(() => { const t = $('bp-out'); if (t) { t.focus(); t.select(); } }, 30); }; try { navigator.clipboard.writeText(code).then(done, fallback); } catch (e) { fallback(); } }