// Checked adapter: upstream snapshots remain byte-for-byte unchanged. export function applyBrightClassroom(source) { const start=source.indexOf('function draw(){'); const end=source.indexOf('function roundedRect(',start); if(start<0||end<0||source.indexOf('function draw(){',start+1)>=0)throw Error('Bright Classroom: upstream draw seam changed'); const original=source.slice(start,end); const tail=original.slice(original.indexOf(' if(previewOnly)return;updateTutorial(bad);')); if(!tail.startsWith(' if(previewOnly)return;'))throw Error('Bright Classroom: UI writeback seam changed'); const draw=`function draw(){ if(!level)return;const width=canvas.clientWidth,height=canvas.clientHeight;if(!width||!height)return; const dpr=window.devicePixelRatio||1; if(canvas.width!==Math.round(width*dpr))canvas.width=Math.round(width*dpr); if(canvas.height!==Math.round(height*dpr))canvas.height=Math.round(height*dpr); ctx.setTransform(dpr,0,0,dpr,0,0); brightScene=renderBrightBoard(ctx,level,path,width,height,{preview:previewOnly}); ({polygons,centers,unit}=brightScene);channelViews=brightScene.channels;cellViews=brightScene.views; const bad=violations(level,path); ${tail}`; source=source.slice(0,start)+draw+source.slice(end); const checkpointCue="sound.play(cell.clue!==undefined&&cell.clue===path.length?'correct':'move',0,path.length)"; if(!source.includes(checkpointCue))throw Error('Checkpoint audio seam changed'); source=source.replace(checkpointCue,"if(path.length1?'checkpoint':'correct'):'move',0,path.length)"); if(!source.includes("sound.play('complete',.23)"))throw Error('Complete audio timing seam changed'); source=source.replace("sound.play('complete',.23)","sound.play('complete',0)"); const hint='visitedPolygons:polygons.filter(p=>path.includes(p.id)).map(p=>p.p.map(v=>[...v]))'; if(!source.includes(hint))throw Error('Bright Classroom: hint seam changed'); source=source.replace(hint,`${hint},...hintGeometry(brightScene,path,h.route)`); // Preview rendering must restore every reference used by the live hint layer. source=source.replace('const saved={canvas,ctx,level,solution,path,polygons,centers,unit,focus,cleared,channelViews,cellViews};','const saved={canvas,ctx,level,solution,path,polygons,centers,unit,focus,cleared,channelViews,cellViews,brightScene};'); source=source.replace('({canvas,ctx,level,solution,path,polygons,centers,unit,focus,cleared,channelViews,cellViews}=saved)','({canvas,ctx,level,solution,path,polygons,centers,unit,focus,cleared,channelViews,cellViews,brightScene}=saved)'); source=`import {renderBrightBoard,hintGeometry,drawBarriers} from '../bright-classroom/board-renderer.mjs';\nlet brightScene=null;\n`+source; source+=` export function refreshBrightBoard(now){ if(!brightScene?.animating)return; const dpr=window.devicePixelRatio||1;ctx.setTransform(dpr,0,0,dpr,0,0); brightScene=renderBrightBoard(ctx,level,path,canvas.clientWidth,canvas.clientHeight,{now}); } export function brightOverlay(target){if(brightScene)drawBarriers(target,brightScene,path);} export function brightAnimating(){return Boolean(brightScene?.animating);} `; return source; }