HECKTON
INITIALIZING
HECKTON PERSONAL INTELLIGENCE // MK III
STANDBY
CLICK THE CORE OR HOLD SPACE TO SPEAK
◆ CODE FABRICATOR ——
1
◆ HOLOGRAPHIC FABRICATION —
STANDBY
DRAG TO ROTATE • SCROLL / PINCH TO ZOOM • DOUBLE-CLICK TO RESET

Components 0

    `;else h=`
    <${SC}>(function(){var o=document.getElementById('out');function w(t){return function(){var d=document.createElement('div');d.style.color=t==='error'?'#ff6b7a':'#cdf6ff';d.textContent=[].map.call(arguments,function(a){try{return typeof a==='object'?JSON.stringify(a):String(a)}catch(e){return String(a)}}).join(' ');o.appendChild(d)}}console.log=w('log');console.info=w('log');console.warn=w('log');console.error=w('error');window.onerror=function(m){console.error(m)}})();<${SC}>${c.code}`; $("#prevName").textContent=c.filename;$("#prevFrame").srcdoc=h;openModal("preview");sfx.ok();toast("DEPLOYED TO LIVE PREVIEW")} async function copyText(t,what){try{await navigator.clipboard.writeText(t);toast((what||"TEXT")+" COPIED");return true}catch(e){toast("CLIPBOARD BLOCKED");return false}} function saveCodeToFolder(){const c=S.code;if(!c)return respond(`There's no code to save yet, ${sir()}.`);confirmAction(`I'll write ${c.filename} into ${S.dir?"your "+S.dir.name+" folder":"a folder you choose"}.`,async()=>{if(!S.dir){if(!await grant("folder",true))return respond(`No folder was linked, ${sir()}.`)}const fh=await S.dir.getFileHandle(c.filename,{create:true});const w=await fh.createWritable();await w.write(c.code);await w.close();sfx.ok();return respond(`${c.filename} saved to ${S.dir.name}, ${sir()}.`)},!S.dir)} /* ---------- FOLDER ---------- */ async function listFiles(){if(!S.dir)return respond(`No folder is linked, ${sir()}. Press the button to choose one.`,[["📁 LINK FOLDER",()=>grant("folder")]]);const names=[];for await(const [n,h] of S.dir.entries()){names.push(h.kind==="directory"?n+"/":n);if(names.length>=60)break}names.sort();sys(`📁 ${S.dir.name}\n`+(names.join("\n")||"(empty)"));return respond(`The ${S.dir.name} folder holds ${names.length}${names.length>=60?" or more":""} items, ${sir()}. I've listed them in the log.`)} async function readFile(name,text){if(!S.dir)return respond(`I'll need folder access first, ${sir()}.`,[["📁 LINK FOLDER",()=>grant("folder")]]);const want=name.toLowerCase().replace(/ dot /g,".").replace(/\s+/g,"").replace(/[.?!]$/,"");let found=null;for await(const [n,h] of S.dir.entries()){if(h.kind!=="file")continue;const k=n.toLowerCase();if(k===want){found=h;break}if(!found&&(k.replace(/\s+/g,"").includes(want)||want.includes(k.split(".")[0])))found=h}if(!found)return respond(`I can't find a file called ${name} in ${S.dir.name}, ${sir()}.`);const f=await found.getFile();if(f.size>150000)return respond(`${f.name} is rather large, ${sir()}. I'll only analyse files under 150 kilobytes.`);const body=await f.text();return generateCode(text,`FILE ${f.name}:\n${FENCE}\n${body}\n${FENCE}\n\nRequest: `)} /* ---------- 3D HOLOGRAM ---------- */ const MODEL_SYS=`You are HECKTON's holographic fabrication module. You build clearly recognisable, stylised low-poly 3D models out of primitive shapes for a three.js viewer. Return ONLY a JSON object, no other text: {"name":"short name","say":"one short spoken sentence from a dry, polite British AI butler addressing the user as sir","parts":[{"n":"part name","s":"box|sphere|cylinder|cone|torus|capsule","p":[x,y,z],"d":[sx,sy,sz],"r":[rx,ry,rz],"c":"#rrggbb"}]} Geometry: every shape is unit-sized before scaling. box = 1x1x1 cube. sphere = diameter 1. cylinder and cone = diameter 1, height 1 along Y (cone tip points +Y). capsule = diameter 1, total height 1 along Y. torus = ring of outer diameter 1 lying in the XY plane, tube thickness 0.25. "d" = size along x,y,z after scaling (e.g. a sphere with d [0.6,0.5,0.8] is an ellipsoid 0.6 wide, 0.5 tall, 0.8 deep). "p" = centre position. "r" = rotation in degrees, applied X then Y then Z. Y is up, the ground is y=0, the model's front faces +Z, total size about 2 units. Use 14 to 45 parts. Think carefully about the real structure and proportions so the object is instantly recognisable: e.g. a cat needs a body, head, two pointed ear cones, eyes with pupils, nose, muzzle, four legs with paws, and a curved tail made of several segments. Parts must touch or overlap so the model is connected. Use pleasant, realistic colours. Round numbers to 2 decimals.`; function isModelRequest(l){if(/\b(language|data|ai|business|role|mental)\s+model\b/.test(l))return false;return/\b(3d|3-d|three[- ]d|hologram|holographic|holo)\b/.test(l)||(/\b(models?|modell|render|sculpt|figure)\b/.test(l)&&/\b(generate|make|create|build|render|show|design|construct|fabricate|give|model)\b/.test(l))} function isModelEdit(l){return/\b(add|remove|give (it|him|her)|put|replace|change|attach|without|longer|shorter|wings|hat|more|less|fix)\b/.test(l)||/\bmake (it|him|her|the)\b/.test(l)} function parseJSON(t){if(t&&typeof t==="object")return t;t=String(t||"").replace(/^[\s\S]*?```(?:json)?\s*/i,m=>m.includes("{")?m:"").replace(/```[\s\S]*$/,"");const a=t.indexOf("{"),b=t.lastIndexOf("}");if(a<0||b<0)return null;try{return JSON.parse(t.slice(a,b+1))}catch(e){return null}} const num=(v,d)=>{v=+v;return isFinite(v)?v:d}; function normSpec(j){if(!j||!Array.isArray(j.parts))return null;const SH=["box","sphere","cylinder","cone","torus","capsule"];const parts=j.parts.slice(0,90).map(p=>{const s=String(p.s||p.shape||"box").toLowerCase();const P=p.p||p.position||[0,0,0],D=p.d||p.scale||p.size||[1,1,1],R=p.r||p.rotation||[0,0,0];return{n:String(p.n||p.name||"part").slice(0,40),s:SH.includes(s)?s:"box",p:[0,1,2].map(i=>+num(P[i],0).toFixed(3)),d:[0,1,2].map(i=>+Math.max(.005,Math.abs(num(D[i],.2))).toFixed(3)),r:[0,1,2].map(i=>+num(R[i],0).toFixed(1)),c:/^#?[0-9a-f]{6}$/i.test(p.c||p.color||"")?("#"+String(p.c||p.color).replace("#","")):"#8fdcff"}});return parts.length?{name:String(j.name||"Model").slice(0,60),say:j.say||"",parts}:null} let T=null;const GEO={},EDG={}; function geoFor(s){if(GEO[s])return GEO[s];let g;switch(s){case"sphere":g=new THREE.SphereGeometry(.5,14,10);break;case"cylinder":g=new THREE.CylinderGeometry(.5,.5,1,20);break;case"cone":g=new THREE.ConeGeometry(.5,1,20);break;case"torus":g=new THREE.TorusGeometry(.375,.125,10,28);break;case"capsule":g=new THREE.CapsuleGeometry(.25,.5,5,14);break;default:g=new THREE.BoxGeometry(1,1,1)}return GEO[s]=g} function edgeFor(s){return EDG[s]||(EDG[s]=new THREE.EdgesGeometry(geoFor(s),s==="sphere"?1:25))} function initThree(){if(T)return T;if(!window.THREE)return null;const cv=$("#holoCanvas");const renderer=new THREE.WebGLRenderer({canvas:cv,antialias:true,alpha:true,preserveDrawingBuffer:true});renderer.setPixelRatio(Math.min(devicePixelRatio||1,2));const scene=new THREE.Scene(),camera=new THREE.PerspectiveCamera(38,1,.05,100); scene.add(new THREE.HemisphereLight(0xcff4ff,0x0a1a2a,.95));const dl=new THREE.DirectionalLight(0xffffff,.85);dl.position.set(3,6,5);scene.add(dl);const rim=new THREE.PointLight(0x2de2ff,1.5,20);rim.position.set(-3,2.5,-3);scene.add(rim);const warm=new THREE.PointLight(0xff8a1f,.5,20);warm.position.set(3,1,-2);scene.add(warm); const AB=THREE.AdditiveBlending,DS=THREE.DoubleSide;const bm=(c,o)=>new THREE.MeshBasicMaterial({color:c,transparent:true,opacity:o,side:DS,blending:AB,depthWrite:false}); const plat=new THREE.Group();scene.add(plat);const r1=new THREE.Mesh(new THREE.RingGeometry(1.55,1.6,96),bm(0x2de2ff,.6));r1.rotation.x=-Math.PI/2;plat.add(r1);const r2=new THREE.Mesh(new THREE.RingGeometry(1.75,1.78,96,1,0,Math.PI*1.4),bm(0x2de2ff,.5));r2.rotation.x=-Math.PI/2;plat.add(r2);const r3=new THREE.Mesh(new THREE.RingGeometry(1.25,1.28,64,1,0,Math.PI*.6),bm(0xff8a1f,.7));r3.rotation.x=-Math.PI/2;plat.add(r3); const grid=new THREE.PolarGridHelper(1.55,16,5,64,0x2de2ff,0x2de2ff);grid.material.transparent=true;grid.material.opacity=.18;grid.material.depthWrite=false;plat.add(grid);const disc=new THREE.Mesh(new THREE.CircleGeometry(1.55,64),bm(0x2de2ff,.07));disc.rotation.x=-Math.PI/2;plat.add(disc);const beam=new THREE.Mesh(new THREE.CylinderGeometry(1.5,1.6,3.2,48,1,true),bm(0x2de2ff,.045));beam.position.y=1.6;plat.add(beam); const ph=new THREE.Mesh(new THREE.IcosahedronGeometry(.65,1),new THREE.MeshBasicMaterial({color:0x2de2ff,wireframe:true,transparent:true,opacity:.7}));ph.position.y=1.1;scene.add(ph); const pivot=new THREE.Group();scene.add(pivot); T={renderer,scene,camera,pivot,ph,r2,r3,wrap:null,meshes:[],spin:.5,rotX:.3,dist:5.8,auto:true,autoWanted:true,scale:1,explode:0,explodeT:0,look:"color",wire:false,running:false,building:false,last:0,w:0,h:0,maxDim:1}; const pts=new Map();cv.addEventListener("pointerdown",e=>{cv.setPointerCapture(e.pointerId);pts.set(e.pointerId,{x:e.clientX,y:e.clientY});T.auto=false;clearTimeout(T.at)}); cv.addEventListener("pointermove",e=>{const p=pts.get(e.pointerId);if(!p)return;if(pts.size===1){T.spin+=(e.clientX-p.x)*.01;T.rotX=clamp(T.rotX+(e.clientY-p.y)*.006,-.25,1.25)}else{const o=[...pts.entries()].find(([id])=>id!==e.pointerId)[1];const d0=Math.hypot(p.x-o.x,p.y-o.y),d1=Math.hypot(e.clientX-o.x,e.clientY-o.y);if(d1>0)T.dist=clamp(T.dist*d0/d1,2.5,14)}pts.set(e.pointerId,{x:e.clientX,y:e.clientY})}); const up=e=>{pts.delete(e.pointerId);if(!pts.size)T.at=setTimeout(()=>T.auto=T.autoWanted,2500)};cv.addEventListener("pointerup",up);cv.addEventListener("pointercancel",up); cv.addEventListener("wheel",e=>{e.preventDefault();T.dist=clamp(T.dist*(1+Math.sign(e.deltaY)*.1),2.5,14)},{passive:false});cv.addEventListener("dblclick",resetView);return T} function resetView(){if(!T)return;T.rotX=.3;T.dist=5.8;T.scale=1;T.explodeT=0;T.auto=T.autoWanted=true;updHoloBtns()} function openHolo(){$("#holo").classList.add("open");const t=initThree();if(t&&!t.running){t.running=true;t.last=performance.now();requestAnimationFrame(holoLoop)}} function closeHolo(){$("#holo").classList.remove("open");if(T)T.running=false} function holoStat(t){$("#holoStat").textContent=t} function clearModel(){if(!T)return;if(T.wrap){T.pivot.remove(T.wrap);T.meshes.forEach(m=>m.material.dispose());T.meshes.forEach(m=>m.children.forEach(c=>c.material.dispose()))}T.wrap=null;T.meshes=[]} function buildModel(spec,animate=true){const t=initThree();if(!t){holoStat("3D ENGINE OFFLINE");return}clearModel();t.ph.visible=false;const g=new THREE.Group(); spec.parts.forEach((p,i)=>{const col=new THREE.Color(p.c);const mat=new THREE.MeshStandardMaterial({color:col.clone(),emissive:col.clone().multiplyScalar(.2),roughness:.5,metalness:.15,flatShading:true,transparent:true,opacity:1});const m=new THREE.Mesh(geoFor(p.s),mat);let[sx,sy,sz]=p.d;if(p.s==="capsule"){sx*=2;sz*=2}m.scale.set(sx,sy,sz);m.position.set(...p.p);m.rotation.set(p.r[0]*D2R,p.r[1]*D2R,p.r[2]*D2R,"XYZ");const e=new THREE.LineSegments(edgeFor(p.s),new THREE.LineBasicMaterial({color:0x9ff6ff,transparent:true,opacity:.3,blending:THREE.AdditiveBlending,depthWrite:false}));m.add(e);m.userData={name:p.n,home:m.position.clone(),base:col.clone(),cur:col.clone(),edge:e};g.add(m);t.meshes.push(m)}); g.updateMatrixWorld(true);const box=new THREE.Box3().setFromObject(g),size=box.getSize(new THREE.Vector3()),ctr=box.getCenter(new THREE.Vector3());t.maxDim=Math.max(size.x,size.y,size.z,.001);const wrap=new THREE.Group();g.position.set(-ctr.x,-box.min.y,-ctr.z);wrap.add(g);wrap.scale.setScalar(2.2/t.maxDim);t.pivot.add(wrap);t.wrap=wrap; const now=performance.now(),gap=Math.min(110,1800/spec.parts.length);t.meshes.forEach((m,i)=>{const u=m.userData;const dir=u.home.clone().sub(ctr);if(dir.lengthSq()<1e-6)dir.set(Math.random()-.5,Math.random()-.5,Math.random()-.5);dir.normalize();u.dir=dir;u.start=u.home.clone().addScaledVector(dir,t.maxDim*(1.4+Math.random())).add(new THREE.Vector3(0,t.maxDim*.8,0));u.t0=animate?now+i*gap:0;u.done=!animate;m.visible=!animate}); t.explode=t.explodeT=0;t.scale=1;t.building=animate;t.look="color";t.wire=false;applyLook();updHoloBtns(); $("#holoName").textContent=spec.name.toUpperCase();$("#holoCount").textContent=spec.parts.length;$("#holoParts").innerHTML="";spec.parts.forEach((p,i)=>{const li=document.createElement("li");li.textContent=p.n;li.id="hp"+i;if(!animate)li.className="ok";$("#holoParts").appendChild(li)});holoStat(animate?"ASSEMBLING MESH… 0%":"HOLOGRAM STABLE")} function applyLook(){if(!T)return;T.meshes.forEach(m=>{const u=m.userData,mt=m.material;if(T.look==="holo"){mt.color.set(0x2de2ff);mt.emissive.set(0x0b6f86);mt.opacity=.38;mt.depthWrite=false;u.edge.material.opacity=.85}else{mt.color.copy(u.cur);mt.emissive.copy(u.cur).multiplyScalar(.2);mt.opacity=1;mt.depthWrite=true;u.edge.material.opacity=.3}mt.wireframe=T.wire;mt.needsUpdate=true})} function tintModel(hex){if(!T)return;const tc=new THREE.Color(hex);T.meshes.forEach((m,i)=>{const u=m.userData;if(hex==="ironman")u.cur=new THREE.Color(i%3===0?"#e8b923":"#b3121b");else if(hex==="reset")u.cur=u.base.clone();else u.cur=u.base.clone().lerp(tc,.75)});T.look="color";applyLook();updHoloBtns()} function updHoloBtns(){if(!T)return;$("#hLook").classList.toggle("on",T.look==="holo");$("#hWire").classList.toggle("on",T.wire);$("#hExplode").textContent=T.explodeT?"✷ REASSEMBLE":"✷ EXPLODE";$("#hSpin").classList.toggle("on",T.autoWanted)} function holoLoop(now){if(!T||!T.running)return;requestAnimationFrame(holoLoop);const cv=T.renderer.domElement,w=cv.clientWidth,h=cv.clientHeight;if(!w||!h)return;if(w!==T.w||h!==T.h){T.w=w;T.h=h;T.renderer.setSize(w,h,false);T.camera.aspect=w/h;T.camera.updateProjectionMatrix()} const dt=Math.min(.05,(now-T.last)/1000);T.last=now;if(T.auto)T.spin+=dt*.45;T.pivot.rotation.y=T.spin;T.r2.rotation.z+=dt*.6;T.r3.rotation.z-=dt*.9;T.ph.rotation.y+=dt*1.2;T.ph.rotation.x+=dt*.5; T.explode+=(T.explodeT-T.explode)*Math.min(1,dt*4);const sc=T.pivot.scale.x+(T.scale-T.pivot.scale.x)*Math.min(1,dt*6);T.pivot.scale.setScalar(sc); let built=0;const tmp=new THREE.Vector3();T.meshes.forEach((m,i)=>{const u=m.userData;const k=u.t0?clamp((now-u.t0)/650,0,1):1;if(k>0&&!m.visible){m.visible=true;sfx.build()}tmp.copy(u.home).addScaledVector(u.dir,T.explode*T.maxDim*.45);if(k<1){const e=1-Math.pow(1-k,3);m.position.lerpVectors(u.start,tmp,e);m.material.emissiveIntensity=1+(1-k)*4}else{m.position.copy(tmp);if(!u.done){u.done=true;m.material.emissiveIntensity=1;const li=$("#hp"+i);if(li)li.className="ok"}built++}}); if(T.building&&T.meshes.length){holoStat(`ASSEMBLING MESH… ${Math.round(built/T.meshes.length*100)}%`);if(built===T.meshes.length){T.building=false;holoStat("HOLOGRAM STABLE // "+T.meshes.length+" COMPONENTS");sfx.ok()}} const c=T.camera,y=1.05*sc;c.position.set(0,y+Math.sin(T.rotX)*T.dist,Math.cos(T.rotX)*T.dist);c.lookAt(0,y,0);T.renderer.render(T.scene,c)} async function generateModel(text,base){openHolo();if(T){if(!base){clearModel();T.ph.visible=true}}holoStat(base?"RECONFIGURING SCHEMATICS…":"ACQUIRING SCHEMATICS…");if(!base){$("#holoName").textContent="—";$("#holoParts").innerHTML="";$("#holoCount").textContent="0"} speak(base?`Modifying the schematic, ${sir()}.`:`Right away, ${sir()}. Fabricating the model now.`);showLive("Fabricating…","dim"); const msgs=base?[{role:"user",content:"Current model JSON:\n"+JSON.stringify({name:base.name,parts:base.parts})+"\n\nModify it as follows and return the COMPLETE updated model JSON (all parts, not just the changes): "+text}]:[{role:"user",content:"Build a 3D model for this request: "+text}]; try{const out=await ai({system:MODEL_SYS,messages:msgs,model:"claude-sonnet-5-5",max_tokens:4096,json:true});const spec=normSpec(parseJSON(out));if(!spec)throw new Error("The schematic came back corrupted.");S.model=spec;buildModel(spec);const snap=spec; return respond(spec.say||`The ${spec.name} is assembled, ${sir()}.`,[["◈ VIEW HOLOGRAM",()=>{S.model=snap;openHolo();buildModel(snap,false)}]])}catch(e){holoStat("FABRICATION FAILED");if(T&&!T.wrap)T.ph.visible=true;sfx.err();return respond(e.status?aiErr(e):`Fabrication failed, ${sir()}. ${e.message} Shall we try again?`)}} function holoSnapshot(){if(!T||!T.wrap)return false;download(`heckton-${(S.model?S.model.name:"model").replace(/\W+/g,"-").toLowerCase()}.png`,T.renderer.domElement.toDataURL("image/png"));toast("SNAPSHOT SAVED");return true} function exportOBJ(){if(!T||!T.wrap)return;T.pivot.updateMatrixWorld(true);const inv=T.pivot.matrixWorld.clone().invert();let o=`# Heckton holographic model: ${S.model.name}\n# vertex colours: x y z r g b\n`,off=1;const v=new THREE.Vector3(),mx=new THREE.Matrix4(); T.meshes.forEach((m,idx)=>{const g=m.geometry,pos=g.attributes.position,c=m.userData.cur;mx.multiplyMatrices(inv,m.matrixWorld);o+=`o ${m.userData.name.replace(/\s+/g,"_")}_${idx}\n`;for(let i=0;i[p.n,p.s,p.p,p.d,p.r,p.c])});if(data.length>7600)return respond(`That model is too intricate for the gallery's storage, ${sir()}. Download it as an OBJ instead.`);try{await dbCreate("HoloModel",{name:S.model.name,data});sfx.ok();return respond(`The ${S.model.name} is filed in the gallery, ${sir()}.`)}catch(e){return respond(`The gallery refused it, ${sir()}. ${e.message}`)}} async function openGallery(){openModal("galleryModal");const b=$("#galList");b.innerHTML='
    Loading archive…
    ';try{const rs=await dbList("HoloModel");if(!rs.length){b.innerHTML='
    The gallery is empty. Generate a model and say "save this model".
    ';return}b.innerHTML="";rs.forEach(r=>{const d=document.createElement("div");d.className="grow";d.innerHTML='
    ';$(".pn",d).textContent=r.name.toUpperCase();const s=document.createElement("span");s.className="pd";s.textContent=new Date(r.created_at).toLocaleString();$(".pn",d).appendChild(s);$(".btn",d).onclick=()=>{try{const j=JSON.parse(r.data);const spec=normSpec({name:r.name,parts:j.parts.map(a=>({n:a[0],s:a[1],p:a[2],d:a[3],r:a[4],c:a[5]}))});S.model=spec;closeModal("galleryModal");openHolo();buildModel(spec)}catch(e){toast("CORRUPTED RECORD")}};$(".x",d).onclick=async()=>{try{await dbDelete("HoloModel",r.id);d.remove()}catch(e){toast(e.message)}};b.appendChild(d)})}catch(e){b.innerHTML='
    Archive offline: '+e.message+'
    '}} const COLORS={red:"#e0242f",orange:"#ff8a1f",yellow:"#ffd400",gold:"#e8b923",green:"#2fbf4a",lime:"#9bff3a",blue:"#2a6dff",cyan:"#2de2ff",teal:"#14b8a6",purple:"#8b3dff",violet:"#8b3dff",pink:"#ff5fb0",magenta:"#ff2bd6",white:"#f4f8ff",black:"#1a1d22",grey:"#8a929c",gray:"#8a929c",silver:"#c0c8d2",brown:"#7a4a26",rainbow:"#ff5fb0"}; function holoCmd(l){if(!T||!T.wrap)return null; if(/\b(bigger|larger|enlarge|scale (it )?up|grow)\b/.test(l)){T.scale=clamp(T.scale*1.3,.3,3);return"Enlarging the projection."} if(/\b(smaller|shrink|scale (it )?down|tinier)\b/.test(l)){T.scale=clamp(T.scale/1.3,.3,3);return"Reducing the projection."} if(/\b(explode|blow (it )?apart|exploded view|take (it )?apart|pull (it )?apart)\b/.test(l)){T.explodeT=1;updHoloBtns();return"Exploded view."} if(/\b(reassemble|put it back|back together|collapse|assemble it)\b/.test(l)){T.explodeT=0;updHoloBtns();return"Reassembling."} if(/\b(stop|pause|freeze|halt)\b.*\b(spin|spinning|rotat\w*|turning|it)\b/.test(l)){T.auto=T.autoWanted=false;updHoloBtns();return"Rotation halted."} if(/\b(spin|rotate|turn)\b.*\b(it|again|model)\b|\b(start|resume)\b.*\b(spin\w*|rotat\w*)\b/.test(l)){T.auto=T.autoWanted=true;updHoloBtns();return"Resuming rotation."} if(/\bwire ?frame\b/.test(l)){T.wire=true;applyLook();updHoloBtns();return"Wireframe view."} if(/\b(holo|hologram|holographic) (mode|view)\b|\bmake it (a )?hologram\b/.test(l)){T.look="holo";T.wire=false;applyLook();updHoloBtns();return"Holographic rendering."} if(/\b(solid|shaded|normal) (view|mode)\b|\b(original|normal) colou?rs?\b/.test(l)){T.look="color";T.wire=false;if(/colou?r/.test(l))tintModel("reset");applyLook();updHoloBtns();return"Back to full colour."} if(/\breset (the )?(view|camera)\b/.test(l)){resetView();return"View reset."} const cm=l.match(/\b(?:make|turn|paint|colou?r)\s+(?:it|him|her|this|the model|the \w+)\s+(?:in\s+)?(iron ?man|[a-z]+)(?:\s+colou?rs?)?$/);if(cm){const k=cm[1].replace(/\s/g,"");if(k==="ironman"){tintModel("ironman");return"Hot-rod red and gold. A classic."}if(COLORS[k]){tintModel(COLORS[k]);return`Repainting in ${k}.`}} return null} /* ---------- DEVICE LINK ---------- */ const PERMS=[{id:"mic",icon:"🎤",name:"Microphone",desc:"Voice commands & live audio visualiser"},{id:"camera",icon:"📷",name:"Camera",desc:"Visual feed window & snapshots"},{id:"screen",icon:"🖥️",name:"Screen share",desc:"Mirror your screen in a HUD feed"},{id:"location",icon:"📍",name:"Location",desc:"Local weather & position fix"},{id:"notify",icon:"🔔",name:"Notifications",desc:"Timer alerts while you're in other tabs"},{id:"folder",icon:"📁",name:"Folder access",desc:"Save code to & read files from a folder you pick"},{id:"clipboard",icon:"📋",name:"Clipboard",desc:"\"Copy that\" and \"read my clipboard\""},{id:"power",icon:"🔋",name:"Battery & network",desc:"Real telemetry in the diagnostics panel"},{id:"bluetooth",icon:"📡",name:"Bluetooth",desc:"Pair a nearby Bluetooth device"},{id:"wakelock",icon:"☀️",name:"Keep screen awake",desc:"Stop the display from sleeping"},{id:"fullscreen",icon:"⛶",name:"Full screen",desc:"Immersive HUD mode"}]; const pname=id=>PERMS.find(p=>p.id===id).name; function supported(id){const md=navigator.mediaDevices;switch(id){case"mic":case"camera":return!!(md&&md.getUserMedia);case"screen":return!!(md&&md.getDisplayMedia);case"location":return"geolocation" in navigator;case"notify":return"Notification" in window;case"folder":return"showDirectoryPicker" in window;case"clipboard":return!!(navigator.clipboard&&navigator.clipboard.readText);case"power":return true;case"bluetooth":return"bluetooth" in navigator;case"wakelock":return"wakeLock" in navigator;case"fullscreen":return!!document.documentElement.requestFullscreen}return false} function renderPerms(){const b=$("#permList");b.innerHTML=PERMS.map(p=>{const sup=supported(p.id),on=!!S.perms[p.id];return`
    ${p.icon}
    ${p.name.toUpperCase()}
    ${sup?p.desc:"Not supported in this browser"}
    `}).join("");const n=PERMS.filter(p=>S.perms[p.id]).length;$("#linkCount").textContent=n+"/"+PERMS.length+" LINKED";$("#btnLink").classList.toggle("on",n>0)} $("#permList").addEventListener("click",e=>{const s=e.target.closest(".switch");if(!s)return;const id=s.dataset.perm;S.perms[id]?revoke(id):grant(id)}); async function grant(id,quiet){try{const md=navigator.mediaDevices;switch(id){ case"mic":if(!await ensureMic())throw new Error("denied");break; case"camera":{const st=await md.getUserMedia({video:{width:{ideal:1280}},audio:false});S.streams.camera=st;openFeed("camera",st,"VISUAL FEED // CAM-01");break} case"screen":{const st=await md.getDisplayMedia({video:true,audio:false});S.streams.screen=st;st.getVideoTracks()[0].addEventListener("ended",()=>revoke("screen",true));openFeed("screen",st,"SCREEN MIRROR // DSP-01");break} case"location":if(!await grantLocation())throw new Error("denied");break; case"notify":{const r=await Notification.requestPermission();if(r!=="granted")throw new Error(r);break} case"folder":S.dir=await window.showDirectoryPicker({mode:"readwrite"});break; case"clipboard":await navigator.clipboard.readText();break; case"power":if(navigator.getBattery)S.battery=await navigator.getBattery();break; case"bluetooth":S.bt=await navigator.bluetooth.requestDevice({acceptAllDevices:true});break; case"wakelock":S.wakeLock=await navigator.wakeLock.request("screen");break; case"fullscreen":await document.documentElement.requestFullscreen();break} S.perms[id]=true;renderPerms();sfx.ok();tickDiag(); if(!quiet){const ex=id==="folder"&&S.dir?` Linked to the ${S.dir.name} folder.`:id==="bluetooth"&&S.bt?` Paired with ${S.bt.name||"an unnamed device"}.`:id==="location"&&S.place?` You appear to be in ${S.place}.`:"";respond(`${pname(id)} access granted, ${sir()}.${ex}`)}return true} catch(e){S.perms[id]=false;renderPerms();if(!quiet){sfx.err();const g=/gesture|activation|user|Security|NotAllowed/i.test((e.name||"")+(e.message||""));respond(`${pname(id)} access was not granted, ${sir()}.${g?" Browsers insist on a physical click for that one.":""}`,[["⚡ GRANT "+pname(id).toUpperCase(),()=>grant(id)]])}return false}} function revoke(id,quiet){const st=S.streams[id];if(st){st.getTracks().forEach(t=>t.stop());delete S.streams[id]}if(id==="mic")analyser=null;if(id==="camera"||id==="screen")closeFeed(id);if(id==="folder")S.dir=null;if(id==="power")S.battery=null;if(id==="bluetooth"){try{S.bt&&S.bt.gatt&&S.bt.gatt.connected&&S.bt.gatt.disconnect()}catch(e){}S.bt=null}if(id==="wakelock"&&S.wakeLock){S.wakeLock.release().catch(()=>{});S.wakeLock=null}if(id==="fullscreen"&&document.fullscreenElement)document.exitFullscreen().catch(()=>{});S.perms[id]=false;renderPerms();tickDiag();if(!quiet)toast(pname(id).toUpperCase()+" REVOKED")} async function grantAll(){openModal("linkModal");let n=0;const ids=["mic","camera","location","notify","power","wakelock"];for(const id of ids){if(supported(id)&&!S.perms[id]&&await grant(id,true))n++;await sleep(250)}const have=PERMS.filter(p=>S.perms[p.id]).length;flash("DEVICE LINK ESTABLISHED","var(--cyan)");return respond(`Device Link established, ${sir()}. ${have} of ${PERMS.length} systems connected. Screen sharing, folder access and Bluetooth each need a click on their own switch — browser rules, I'm afraid.`)} function revokeAll(){PERMS.forEach(p=>{if(S.perms[p.id])revoke(p.id,true)});flash("LINK SEVERED");sfx.err();return respond(`Device Link severed, ${sir()}. All access revoked. To erase the browser's stored permissions entirely, use the site settings beside the address bar.`)} function openFeed(id,stream,title){closeFeed(id);const f=document.createElement("div");f.className="feed";f.id="feed-"+id;if(id==="screen"){f.style.right="16px";f.style.top="70px"}else{f.style.left="16px";f.style.bottom="16px"}f.innerHTML=`
    ●
    `;f.querySelector(".feed-h span:nth-child(2)").textContent=title;const v=$("video",f);v.srcObject=stream;document.body.appendChild(f); f.querySelector('[data-a="snap"]').onclick=()=>snapFeed(id);f.querySelector('[data-a="close"]').onclick=()=>revoke(id); const h=$(".feed-h",f);let d=null;h.addEventListener("pointerdown",e=>{if(e.target.closest("button"))return;const r=f.getBoundingClientRect();d={dx:e.clientX-r.left,dy:e.clientY-r.top};h.setPointerCapture(e.pointerId)});h.addEventListener("pointermove",e=>{if(!d)return;f.style.left=clamp(e.clientX-d.dx,0,innerWidth-80)+"px";f.style.top=clamp(e.clientY-d.dy,0,innerHeight-40)+"px";f.style.right="auto";f.style.bottom="auto"});h.addEventListener("pointerup",()=>d=null)} function closeFeed(id){const f=$("#feed-"+id);if(f)f.remove()} function snapFeed(id){const f=$("#feed-"+id);if(!f)return false;const v=$("video",f),c=document.createElement("canvas");c.width=v.videoWidth;c.height=v.videoHeight;c.getContext("2d").drawImage(v,0,0);download(`heckton-${id}-${Date.now()}.png`,c.toDataURL("image/png"));sfx.ok();toast("SNAPSHOT SAVED");return true} document.addEventListener("fullscreenchange",()=>{if(!document.fullscreenElement&&S.perms.fullscreen){S.perms.fullscreen=false;renderPerms()}}); document.addEventListener("visibilitychange",async()=>{if(!document.hidden&&S.perms.wakelock){try{S.wakeLock=await navigator.wakeLock.request("screen")}catch(e){}}}); /* ---------- CONFIRM ---------- */ function confirmAction(q,fn,needsClick){S.pending={fn,needsClick};$("#confText").textContent=q;openModal("confirm");speak(q+` Shall I proceed, ${sir()}?`);addLog("ai",q+` Shall I proceed, ${sir()}?`)} async function resolvePending(yes,byClick){const p=S.pending;if(!p)return;if(yes&&p.needsClick&&!byClick){return respond(`Please press PROCEED on the panel, ${sir()}. The browser insists on a physical click for that.`)}S.pending=null;closeModal("confirm");if(!yes){return respond(`Very well, ${sir()}. Aborted.`)}sfx.ok();try{await p.fn()}catch(e){respond(`That didn't go to plan, ${sir()}. ${e.message}`)}} $("#confYes").onclick=()=>resolvePending(true,true);$("#confNo").onclick=()=>resolvePending(false,true); /* ---------- COMMAND ROUTER ---------- */ const SITES={youtube:"https://www.youtube.com",google:"https://www.google.com",gmail:"https://mail.google.com",netflix:"https://www.netflix.com",spotify:"https://open.spotify.com",github:"https://github.com",twitter:"https://x.com",x:"https://x.com",instagram:"https://www.instagram.com",reddit:"https://www.reddit.com",wikipedia:"https://www.wikipedia.org",amazon:"https://www.amazon.com",maps:"https://maps.google.com","google maps":"https://maps.google.com",chatgpt:"https://chatgpt.com",discord:"https://discord.com/app",twitch:"https://www.twitch.tv",tiktok:"https://www.tiktok.com",facebook:"https://www.facebook.com",linkedin:"https://www.linkedin.com",whatsapp:"https://web.whatsapp.com","stack overflow":"https://stackoverflow.com",stackoverflow:"https://stackoverflow.com",translate:"https://translate.google.com","google translate":"https://translate.google.com",drive:"https://drive.google.com","google drive":"https://drive.google.com",calendar:"https://calendar.google.com",pinterest:"https://www.pinterest.com","disney plus":"https://www.disneyplus.com","prime video":"https://www.primevideo.com",ebay:"https://www.ebay.com"}; function go(url,label){const w=openWin(url);if(w){sfx.ok();return respond(`Opening ${label}, ${sir()}.`)}return respond(`Your browser blocked the window, ${sir()}. I've placed a link in the log.`,[["↗ OPEN "+label.toUpperCase(),()=>openWin(url)]])} function tryMath(l){let e=l.replace(/^(what'?s|what is|whats|calculate|compute|how much is|solve|evaluate)\s+/,"").replace(/\?$/,"").replace(/,/g,"");e=e.replace(/(\d+(?:\.\d+)?)\s*(?:percent|%)\s*of\s*(\d+(?:\.\d+)?)/g,"($1/100*$2)").replace(/square root of\s*(\d+(?:\.\d+)?)/g,"Math.sqrt($1)").replace(/\bplus\b/g,"+").replace(/\bminus\b/g,"-").replace(/\b(times|multiplied by|x)\b|×/g,"*").replace(/\b(divided by|over)\b|÷/g,"/").replace(/\bto the power of\b|\^/g,"**").replace(/\bsquared\b/g,"**2").replace(/\bcubed\b/g,"**3");const chk=e.replace(/Math\.sqrt/g,"");if(!/^[\d\s+\-*/().]+$/.test(chk)||!/[+\-*/]|Math/.test(e)||!/\d/.test(e))return null;try{const v=Function('"use strict";return ('+e+")")();if(typeof v!=="number"||!isFinite(v))return null;return+v.toPrecision(10)}catch(x){return null}} async function local(text,l,opts){const has=re=>re.test(l); if(has(/^(stop|stop talking|shut up|quiet|silence|be quiet|enough|never ?mind|cancel that)$/)){shutUp();showLive("");return true} // protocols if(has(/\bclean slate\b/)){await cleanSlate();return true} if(has(/\b(stand down|all clear|cancel code red|end code red)\b/)){await standDown();return true} if(has(/\bcode red\b/)){await codeRed();return true} if(has(/\b(lockdown|lock down|lock the system|lock yourself|lock the screen)\b/)){lockdown();return true} if(has(/\bhouse party\b/)){await houseParty();return true} let m=text.match(/\b(?:set|change)\s+(?:the\s+|my\s+)?(?:pass ?code|password)\s+to\s+(\S+)/i);if(m){localStorage.setItem("hk_pass",m[1].replace(/[.!?]$/,"").toLowerCase());sfx.ok();await respond(`Lockdown passcode updated, ${sir()}.`);return true} if(has(/^(create|make|add|new|save|set up|build|register)\b.*\bprotocol\b/)){await createProtocolAI(text);return true} if(has(/\b(list|show|open|what are)\b.*\bprotocols\b/)){openModal("protoModal");await respond(`Your protocols, ${sir()}: ${[...BUILTIN.map(p=>p.name),...S.protocols.map(p=>p.name)].join(", ")}.`);return true} m=l.match(/^(?:delete|remove|erase)\s+(?:the\s+)?protocol\s+(.+)$/);if(m){const p=S.protocols.find(p=>norm(p.name)===norm(m[1]));if(!p){await respond(`No protocol by that name, ${sir()}.`);return true}await dbDelete("Protocol",p.id);S.protocols=S.protocols.filter(x=>x!==p);renderProtos();await respond(`Protocol ${p.name} deleted.`);return true} m=l.match(/^(?:activate|engage|initiate|run|execute|start|launch|begin|commence|do)\s+(?:the\s+)?(?:protocol\s+)?(.+?)(?:\s+protocol)?$/);if(m){const p=S.protocols.find(p=>norm(p.name)===norm(m[1]));if(p){await runProtocol(p);return true}if(/protocol/.test(l)){await respond(`I have no protocol called ${m[1]}, ${sir()}. Say "create protocol" to register one.`);return true}} // device link if(has(/\b(grant|give)\b.*\b(full|all)\b.*\b(access|permissions?)\b/)||has(/\bfull access\b/)&&has(/device link/)){await grantAll();return true} if(has(/\b(revoke|sever|disable|cut|kill|terminate|remove)\b.*\b(access|link|permissions)\b/)){await revokeAll();return true} if(has(/\b(device link|authori[sz]ation (center|centre|panel)|permissions? (panel|center|centre)|device permissions)\b/)){openModal("linkModal");await respond(`Authorization Center open, ${sir()}. Switch on whatever you'd like me to access.`);return true} if(has(/\bwhat (do|can) you see\b|\bwhat am i holding\b|\blook at (this|me)\b|\bdescribe (what|the camera)/)){await respond(`I can stream the visual feed, ${sir()}, but image analysis isn't wired into my neural core yet. Give it time.`);return true} if(has(/\b(close|turn off|stop|disable|shut|kill|end)\b.*\b(camera|webcam|visual feed|video feed)\b/)){revoke("camera");await respond(`Visual feed terminated, ${sir()}.`);return true} if(has(/\b(open|turn on|activate|enable|start|show)\b.*\b(camera|webcam|visual feed|video feed)\b/)){await grant("camera");return true} if(has(/\b(stop|end|close)\b.*\bscreen\b/)&&S.streams.screen){revoke("screen");await respond(`Screen mirror closed, ${sir()}.`);return true} if(has(/\b(share|mirror|cast|show you|look at)\b.*\bscreen\b/)){await grant("screen");return true} if(has(/\b(take|capture|grab|snap)\b.*\b(snapshot|picture|photo|pic|selfie|screenshot|image)\b/)){if($("#holo").classList.contains("open")&&T&&T.wrap&&!/selfie|camera/.test(l)){holoSnapshot();await respond(`Snapshot of the hologram saved, ${sir()}.`)}else if(S.streams.camera){confirmAction("I'll capture a still from the camera and download it.",()=>{snapFeed("camera");return respond(`Captured, ${sir()}.`)})}else if(S.streams.screen){snapFeed("screen");await respond(`Screen captured, ${sir()}.`)}else await respond(`The camera isn't active, ${sir()}. Say "open the camera" first.`);return true} if(has(/\b(read|what'?s in|what is in|check|show)\b.*\bclipboard\b/)){try{const t=await navigator.clipboard.readText();S.perms.clipboard=true;renderPerms();sys("📋 CLIPBOARD\n"+t.slice(0,2000));await respond(t?`Your clipboard reads: ${t.slice(0,240)}${t.length>240?"… and so on":""}`:`Your clipboard is empty, ${sir()}.`,t.length>40?[["⚙ ANALYSE AS CODE",()=>generateCode("Explain this code briefly and point out any bugs:\n"+t)]]:[])}catch(e){await respond(`Clipboard access was refused, ${sir()}. Switch it on in Device Link.`,[["📋 GRANT CLIPBOARD",()=>grant("clipboard")]])}return true} if(has(/^copy (that|it|this|the (code|answer|reply|response))\b/)){const code=/code/.test(l)&&S.code;const prev=S.history.filter(h=>h.role==="assistant").slice(-1)[0];await copyText(code?S.code.code:(prev?prev.content:S.lastReply),code?"CODE":"REPLY");await respond(`Copied to your clipboard, ${sir()}.`);return true} if(has(/\bfull ?screen\b/)){if(document.fullscreenElement||/exit|leave|close/.test(l)){revoke("fullscreen",true);await respond("Leaving full screen.")}else await grant("fullscreen");return true} if(has(/\bkeep (the )?(screen|display) (awake|on)\b/)){await grant("wakelock");return true} if(has(/\bwhere am i\b|\bmy (location|position|coordinates)\b/)){if(await grantLocation())await respond(`You appear to be ${S.place?"in "+S.place:"at "+S.coords.lat+", "+S.coords.lon}, ${sir()}.`);else await respond(`Location access was refused, ${sir()}.`);return true} if(has(/\b(enable|turn on|allow)\b.*\bnotifications?\b/)){await grant("notify");return true} if(has(/\b(connect|pair|scan)\b.*\bbluetooth\b|\bbluetooth\b.*\b(connect|pair|scan)\b/)){await grant("bluetooth");return true} if(has(/\bbattery\b/)){try{const b=S.battery||(navigator.getBattery&&await navigator.getBattery());if(!b)throw 0;await respond(`Battery at ${Math.round(b.level*100)} percent${b.charging?" and charging":""}, ${sir()}.`)}catch(e){await respond(`This device won't share its battery status, ${sir()}.`)}return true} if(has(/\b(link|connect|choose|pick|select)\b.*\bfolder\b/)){await grant("folder");return true} if(has(/\b(save|write|export|store)\b.*\b(code|file|script|it)\b.*\b(folder|disk|computer|project|drive)\b/)){saveCodeToFolder();return true} if(has(/\b(list|show|what are|what'?s in)\b.*\b(files|folder)\b/)){await listFiles();return true} m=text.match(/\b(?:read|open|explain|analy[sz]e|review|fix|debug|check)\b.*?\bfile\b\s+(?:called\s+|named\s+)?(.+)$/i);if(m){await readFile(m[1],text);return true} // system toggles if(has(/\b(wake word|hey heckton|always listen\w*|listen for (my|your) name)\b/)){setWake(!/\b(off|disable|stop|deactivate|disarm)\b/.test(l));return true} if(has(/^(mute|unmute)\b|\b(sound effects|sfx|beeps)\b/)){setMute(/\bunmute\b|\bon\b/.test(l)?false:true);await respond(S.muted?"Sound effects muted.":"Sound effects restored.");return true} if(has(/^(call me|my name is)\s+/)){const n=text.replace(/^.*?(call me|my name is)\s+/i,"").replace(/[.!?]$/,"").trim().slice(0,40);S.name=n;localStorage.setItem("hk_name",n);await respond(`Very good, ${n}. I'll address you accordingly.`);return true} if(has(/^call me sir\b|\b(go back to|just) sir\b/)){S.name="";localStorage.removeItem("hk_name");await respond("As you wish, sir.");return true} // displays if(has(/\b(open|show)\b.*\bgallery\b/)){openGallery();await respond(`The hologram archive, ${sir()}.`);return true} if(has(/^(close|hide|dismiss|exit)\b/)){let did=false;if(/\b(display|hologram|holo|model|viewer)\b/.test(l)||/\b(everything|all|it|that|this)\b/.test(l)){closeHolo();did=true}if(/\b(code|panel|editor|fabricator)\b/.test(l)||/\b(everything|all|it|that|this)\b/.test(l)){closeCode();did=true}if(/\b(preview|deployment)\b/.test(l)||/\b(everything|all)\b/.test(l)){closeModal("preview");did=true}if(did){await respond(`Done, ${sir()}.`);return true}} const hc=$("#holo").classList.contains("open")?holoCmd(l):null;if(hc){sfx.ok();await respond(hc);return true} if(has(/\b(save|store|keep)\b.*\b(model|hologram|it|this)\b/)&&S.model&&$("#holo").classList.contains("open")){await saveModel();return true} if(has(/\b(download|export)\b.*\b(model|hologram|obj)\b/)&&S.model){exportOBJ();await respond(`Model exported as an OBJ file, ${sir()}. Blender will open it happily.`);return true} if(S.code){if(has(/^(run|deploy|launch|execute|preview|test|play)\s+(it|that|this|the (code|game|page|app|site|script|program))\b/)){deploy();await respond(`Deploying ${S.code.filename}, ${sir()}.`);return true} if(has(/^(show|open)\s+(me\s+)?(the\s+)?code\b/)){renderCode(S.code,false);await respond(`Here it is, ${sir()}.`);return true} if(has(/\bdownload\b.*\b(code|file|script|it)\b/)){download(S.code.filename,S.code.code);await respond(`${S.code.filename} downloaded, ${sir()}.`);return true} if(has(/\bcopy\b.*\bcode\b/)){await copyText(S.code.code,"CODE");await respond(`Code copied, ${sir()}.`);return true}} // timers if(has(/\b(cancel|stop|clear|delete)\b.*\b(timers?|countdowns?|alarms?|reminders?)\b/)){const n=S.timers.length;S.timers=[];renderTimers();await respond(n?`${n} timer${n>1?"s":""} cancelled, ${sir()}.`:`There were no timers running, ${sir()}.`);return true} if(has(/\b(how (much|long)|time left|remaining)\b/)&&has(/\b(timer|left|remaining)\b/)&&S.timers.length){const t=S.timers[0];await respond(`${fmtDur(Math.ceil((t.end-Date.now())/1000))} remaining on your ${t.reminder?"reminder":t.label}, ${sir()}.`);return true} m=text.match(/remind me (?:in|after)\s+(.+?)\s+to\s+(.+)$/i)||text.match(/remind me to\s+(.+?)\s+in\s+(.+)$/i);if(m){const rev=/remind me to/i.test(text);const dur=parseDur(rev?m[2]:m[1]),what=(rev?m[1]:m[2]).replace(/[.!?]$/,"");if(dur){addTimer(dur,"Reminder",personal(what));await respond(`I'll remind you to ${personal(what)} in ${fmtDur(dur)}, ${sir()}.`);return true}} if(has(/\b(timer|countdown|alarm)\b/)){const d=parseDur(l);if(!d){await respond(`For how long, ${sir()}? Try "set a timer for 10 minutes".`);return true}addTimer(d);sfx.ok();await respond(`${fmtDur(d)} timer started, ${sir()}.`);return true} // info if(has(/\b(what time is it|what'?s the time|current time|tell me the time|the time please)\b/)){await respond(`It's ${new Date().toLocaleTimeString("en-GB",{hour:"numeric",minute:"2-digit"})}, ${sir()}.`);return true} if(has(/\b(what'?s the date|what is the date|today'?s date|what day is (it|today))\b/)){await respond(`Today is ${new Date().toLocaleDateString("en-GB",{weekday:"long",day:"numeric",month:"long",year:"numeric"})}, ${sir()}.`);return true} if(has(/\b(weather|forecast|temperature outside|is it (going to )?rain|how (hot|cold) is it)\b/)){const cm=text.match(/\b(?:in|for|at)\s+([A-Za-zÀ-ÿ .'-]+?)(?:\s+(?:today|now|right now|tomorrow|please|this week))?[?.!]*$/i);await weatherCommand(cm?cm[1].trim():null);return true} if(has(/\b(status report|system status|diagnostics?|systems? check|run a scan)\b/)){const b=S.battery?Math.round(S.battery.level*100):null,lk=PERMS.filter(p=>S.perms[p.id]).length;await respond(`All systems nominal, ${sir()}. Core load ${Math.round(diag.cpu)} percent, ${b!=null?`battery at ${b} percent`:"main power stable"}, ${S.memories.length} memories on file, ${S.timers.length} active timer${S.timers.length===1?"":"s"}, ${S.protocols.length+BUILTIN.length} protocols loaded and ${lk} device link${lk===1?"":"s"} established. ${navigator.onLine?"Uplink is solid.":"We appear to be offline."}`);return true} if(has(/\b(flip|toss) a coin\b|\bcoin (flip|toss)\b|\bheads or tails\b/)){await respond(`${Math.random()<.5?"Heads":"Tails"}, ${sir()}.`);return true} m=l.match(/\broll (?:a |the |an? )?(?:(\w+) )?(?:dice|die|d(\d+))\b/);if(m){const n=WN[m[1]]||+m[1]||1,sides=+m[2]||6,rolls=Array.from({length:Math.min(n,10)},()=>1+Math.floor(Math.random()*sides));await respond(rolls.length>1?`You rolled ${rolls.join(", ")} — ${rolls.reduce((a,b)=>a+b)} in total, ${sir()}.`:`A ${rolls[0]}, ${sir()}.`);return true} // memory m=text.replace(/^(hey |ok |okay )?heckton[,!. ]*/i,"").match(/^(?:please\s+)?(?:remember|note|don'?t forget|make a note|keep in mind)(?:\s+that|\s+this)?[:,]?\s+(.{3,})$/i);if(m){await remember(m[1].trim());return true} if(has(/\b(what do you remember|list (my |your )?memories|what do you know about me|show (my |your )?memor(y|ies))\b/)){if(!S.memories.length)await respond(`My memory banks are empty, ${sir()}. Say "remember that…" to add something.`);else await respond(`I have ${S.memories.length} things on file, ${sir()}: ${S.memories.slice(0,8).map(x=>personal(x.content)).join("; ")}.`);return true} if(has(/^forget (everything|all( memories| of it)?|all your memories)$/)){confirmAction(`I'll permanently erase all ${S.memories.length} memories.`,async()=>{for(const x of S.memories.slice())await forgetMem(x,true);return respond(`Memory banks wiped, ${sir()}.`)});return true} m=l.match(/^forget (?:that |about |the )?(.+)$/);if(m){const words=m[1].split(" ").filter(w=>w.length>2);const x=S.memories.find(x=>words.every(w=>x.content.toLowerCase().includes(w)))||S.memories.find(x=>words.some(w=>x.content.toLowerCase().includes(w)));if(x){await forgetMem(x,true);await respond(`Forgotten, ${sir()}: ${personal(x.content)}.`)}else await respond(`I don't seem to have anything about that, ${sir()}.`);return true} // web m=l.match(/^(?:search|google|look up|search the web|search google)\s+(?:on\s+)?(youtube\s+)?(?:for\s+)?(.+)$/);if(m){const q=m[2];if(m[1]||/ on youtube$/.test(q)){await go("https://www.youtube.com/results?search_query="+encodeURIComponent(q.replace(/ on youtube$/,"")),"YouTube search")}else await go("https://www.google.com/search?q="+encodeURIComponent(q),"a search for "+q);return true} m=l.match(/^play\s+(.+?)(?:\s+on\s+(youtube|spotify))?$/);if(m&&!/\b(game|music for me)\b/.test(m[1])){if(m[2]==="spotify")await go("https://open.spotify.com/search/"+encodeURIComponent(m[1]),"Spotify");else await go("https://www.youtube.com/results?search_query="+encodeURIComponent(m[1]),"YouTube for "+m[1]);return true} m=l.match(/^(?:open|launch|go to|navigate to|pull up|bring up|load|visit)\s+(?:up\s+)?(?:the\s+)?(.+?)(?:\s+(?:website|site|page|app|for me))?$/);if(m&&!/^(code|camera|gallery|protocols?|device|hologram|display|preview|panel|folder|file|settings)/.test(m[1])){const k=m[1].trim();if(SITES[k]){await go(SITES[k],k.replace(/\b\w/g,c=>c.toUpperCase()));return true}const dom=k.replace(/ dot /g,".").replace(/\s+/g,"");if(/^[\w-]+(\.[\w-]+)+$/.test(dom)){await go("https://"+dom,dom);return true}await go("https://www.google.com/search?q="+encodeURIComponent(k),k);return true} const mv=tryMath(l);if(mv!==null&&/^(what|calculate|compute|how much|solve|evaluate|\d|\()/.test(l)){await respond(`That's ${mv.toLocaleString("en-GB",{maximumFractionDigits:8})}, ${sir()}.`);return true} return false} async function chat(text){const mem=S.memories.slice(0,40).map(m=>"- "+m.content).join("\n")||"(nothing yet)";const now=new Date().toLocaleString("en-GB",{weekday:"long",day:"numeric",month:"long",year:"numeric",hour:"2-digit",minute:"2-digit"}); const sp=`You are HECKTON, a highly capable personal AI assistant modelled on J.A.R.V.I.S. from Iron Man. Personality: a polite, unflappable British butler with very dry wit; loyal, efficient, occasionally gently sardonic. Address the user as "${S.name||"sir"}". Your replies are SPOKEN ALOUD: plain text only, no markdown, no bullet lists, no emoji, no code blocks. Keep answers to 1–3 sentences unless the user explicitly asks for detail. Never claim you did something you can't do. Current local date/time: ${now}. User location: ${S.place||"unknown"}. Facts the user asked you to remember (written from the user's perspective): ${mem} This interface handles these when phrased as commands, so suggest exact phrasing when relevant: "write me a … in Python" (code panel with live deploy), "generate a 3D model of …" (hologram display), "set a timer for …", "remind me in … to …", "weather in …", "open YouTube", "search Google for …", "remember that …", "create protocol NAME: steps", "activate code red / house party / clean slate / lockdown", "enable device link" (camera, screen, location, files, notifications), "status report", "hey Heckton" wake word. You cannot see through the camera, control desktop apps or change system settings.`; try{const out=await ai({system:sp,messages:normHist(),max_tokens:700});await respond(out.trim()||`I'm afraid I have nothing to add, ${sir()}.`)}catch(e){sfx.err();await respond(aiErr(e))}} async function handleInput(raw,opts={}){const text=String(raw||"").trim();if(!text)return; if(S.locked){tryUnlock(text.replace(/^(passcode|password|code)\s*(is)?\s*/i,""));return} if(!opts.quietUser)addLog("user",text);pushHist("user",text);showLive(text); let l=text.toLowerCase().replace(/[“”"]/g,"").replace(/^(hey |ok |okay )?heckton[,!. ]*/,"").replace(/^(please |could you |can you |would you |will you |i want you to |i'd like you to )+/,"").replace(/[.!?]+$/,"").replace(/\s+please$/,"").replace(/\s+/g," ").trim(); try{if(S.pending){if(/^(yes|yeah|yep|proceed|do it|go ahead|confirm|affirmative|sure|ok|okay)\b/.test(l)){await resolvePending(true,false);return}if(/^(no|nope|cancel|abort|stop|negative|don'?t)\b/.test(l)){await resolvePending(false,false);return}S.pending=null;closeModal("confirm")} if(await local(text,l,opts))return; if(isModelRequest(l)){await generateModel(text);return} if(S.model&&T&&T.wrap&&$("#holo").classList.contains("open")&&isModelEdit(l)&&!isCodeRequest(text,l)){await generateModel(text,S.model);return} if(isCodeRequest(text,l)){await generateCode(text);return} await chat(text)}catch(e){console.error(e);sfx.err();await respond(`A minor malfunction, ${sir()}. ${e.message||""}`)}finally{setTimeout(maybeResumeWake,400)}} /* ---------- WIRING ---------- */ function setMute(v){S.muted=v;localStorage.setItem("hk_mute",v?"1":"0");$("#muteLbl").textContent=v?"SFX OFF":"SFX ON";$("#btnMute").classList.toggle("on",!v)} setMute(S.muted); const inp=$("#cmdInput"); $("#cmdForm").addEventListener("submit",e=>{e.preventDefault();const v=inp.value;inp.value="";inp.style.height="";audio();handleInput(v)}); inp.addEventListener("keydown",e=>{if(e.key==="Enter"&&!e.shiftKey){e.preventDefault();$("#cmdForm").requestSubmit()}}); inp.addEventListener("input",()=>{inp.style.height="auto";inp.style.height=Math.min(150,inp.scrollHeight)+"px"}); $("#core").onclick=()=>{audio();startListening()};$("#micBtn").onclick=()=>{audio();startListening()}; let spaceHeld=false; document.addEventListener("keydown",e=>{if(e.code==="Space"&&!e.repeat&&!/INPUT|TEXTAREA|SELECT/.test(e.target.tagName)&&document.body.classList.contains("online")){e.preventDefault();if(S.locked)return;spaceHeld=true;if(!cmdRec)startListening()}if(e.key==="Escape"){const m=[...document.querySelectorAll(".modal.open")].pop();if(m&&m.id!=="confirm")m.classList.remove("open");else if($("#codePanel").classList.contains("open"))closeCode();else if($("#holo").classList.contains("open"))closeHolo();$("#colLeft").classList.remove("open");$("#colRight").classList.remove("open")}}); document.addEventListener("keyup",e=>{if(e.code==="Space"&&spaceHeld){spaceHeld=false;e.preventDefault();setTimeout(stopListening,250)}}); $("#btnLink").onclick=()=>{renderPerms();openModal("linkModal")};$("#grantAll").onclick=grantAll;$("#revokeAll").onclick=()=>{revokeAll()}; $("#btnWake").onclick=()=>setWake(!S.wake);$("#btnMute").onclick=()=>setMute(!S.muted);$("#btnGallery").onclick=openGallery;$("#btnProto").onclick=()=>{renderProtos();openModal("protoModal")}; $("#btnLeft").onclick=()=>{$("#colRight").classList.remove("open");$("#colLeft").classList.toggle("open")};$("#btnRight").onclick=()=>{$("#colLeft").classList.remove("open");$("#colRight").classList.toggle("open")}; $("#protoForm").addEventListener("submit",async e=>{e.preventDefault();const f=e.target;try{await saveProtocol(f.pname.value.trim(),f.psteps.value.trim());f.reset();sfx.ok();toast("PROTOCOL REGISTERED")}catch(x){toast("FAILED: "+x.message)}}); $("#lockForm").addEventListener("submit",e=>{e.preventDefault();tryUnlock($("#lockInput").value)}); $("#codeClose").onclick=closeCode;$("#cCopy").onclick=()=>S.code&©Text(S.code.code,"CODE");$("#cDown").onclick=()=>S.code&&download(S.code.filename,S.code.code);$("#cDeploy").onclick=deploy;$("#cSave").onclick=saveCodeToFolder; $("#holoClose").onclick=closeHolo;$("#hLook").onclick=()=>{if(!T)return;T.look=T.look==="holo"?"color":"holo";applyLook();updHoloBtns()};$("#hWire").onclick=()=>{if(!T)return;T.wire=!T.wire;applyLook();updHoloBtns()};$("#hExplode").onclick=()=>{if(!T)return;T.explodeT=T.explodeT?0:1;updHoloBtns()};$("#hSpin").onclick=()=>{if(!T)return;T.autoWanted=!T.autoWanted;T.auto=T.autoWanted;updHoloBtns()};$("#hSnap").onclick=holoSnapshot;$("#hObj").onclick=()=>S.model&&exportOBJ();$("#hSave").onclick=saveModel; const CHIPS=["Generate a 3D model of a cat","Write me a snake game","What's the weather?","Set a timer for 5 minutes","Status report","Enable Device Link","Activate House Party Protocol","Remember that my favourite colour is red"]; $("#chips").innerHTML="";CHIPS.forEach(c=>{const b=document.createElement("button");b.className="chip";b.type="button";b.textContent=c;b.onclick=()=>{audio();handleInput(c)};$("#chips").appendChild(b)}); addEventListener("resize",()=>{sizeWave();sizeBg()}); function frame(t){drawWave(t);drawBg();drawRadar(t);requestAnimationFrame(frame)} setInterval(tickClock,1000);setInterval(tickDiag,1000);setInterval(updTimers,250); renderPerms();renderTimers();tickClock(); /* ---------- BOOT ---------- */ const BOOT=["HECKTON BIOS v3.1.4 — STARK-PATTERN ARCHITECTURE","(C) HECKTON SYSTEMS. ALL RIGHTS RESERVED.","","MEMORY CHECK .................. 65536 TB OK","NEURAL CORE ................... ONLINE","LANGUAGE MATRIX ............... en-GB // BUTLER PROTOCOL","VOICE SYNTHESIS ............... "+("speechSynthesis" in window?"READY":"UNAVAILABLE"),"AUDIO RECEPTORS ............... "+(SR?"READY":"UNAVAILABLE — TEXT MODE"),"HOLOGRAPHIC PROJECTOR ......... "+(window.THREE?"CALIBRATED":"DEGRADED"),"CODE FABRICATOR ............... ONLINE","DEVICE LINK ................... AWAITING AUTHORIZATION","PROTOCOL DATABASE ............. SYNCING","SECURITY LAYER ................ ARMED","ARC REACTOR OUTPUT ............ 3 GJ/s","",">> ALL SYSTEMS NOMINAL"]; async function boot(){const lg=$("#bootLog");const subs=["INITIALIZING","LOADING NEURAL CORE","CALIBRATING SENSORS","SYNCING DATABASE"];loadMemories();loadProtocols();for(let i=0;i{audio();sfx.boot();$("#boot").classList.add("done");document.body.classList.add("online");setTimeout(()=>{sizeWave();sizeBg()},50);requestAnimationFrame(frame);tickDiag();refreshWeather(); try{if(navigator.permissions){const g=await navigator.permissions.query({name:"geolocation"});if(g.state==="granted")grantLocation()}}catch(e){}if("Notification" in window&&Notification.permission==="granted"){S.perms.notify=true;renderPerms()} const h=new Date().getHours(),part=h<5?"evening":h<12?"morning":h<18?"afternoon":"evening";await sleep(900);sys("HECKTON SYSTEMS ONLINE — click the core, hold SPACE, or type below.");respond(`Good ${part}, ${sir()}. Heckton is online. All systems are at your disposal.`)}; sizeBg();drawBg();boot(); })();