three-live 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/index.d.ts +190 -0
- package/dist/three-author-engine.js +171 -163
- package/dist/three-author.cjs +145 -163
- package/dist/three-author.d.ts +190 -0
- package/dist/three-author.js +145 -163
- package/dist/three-convert.cjs +142 -160
- package/dist/three-convert.d.ts +190 -0
- package/dist/three-convert.js +142 -160
- package/dist/three-live-ai.cjs +41 -0
- package/dist/three-live-ai.d.ts +759 -0
- package/dist/three-live-ai.js +41 -0
- package/dist/three-live-ai.min.js +41 -0
- package/dist/three-live-animation.cjs +6 -6
- package/dist/three-live-animation.d.ts +190 -0
- package/dist/three-live-animation.js +5 -5
- package/dist/three-live-animation.min.js +5 -5
- package/dist/three-live-cad.cjs +2 -2
- package/dist/three-live-cad.d.ts +190 -0
- package/dist/three-live-cad.js +2 -2
- package/dist/three-live-cad.min.js +2 -2
- package/dist/three-live-element.cjs +50 -85
- package/dist/three-live-element.d.ts +190 -0
- package/dist/three-live-element.js +50 -85
- package/dist/three-live-full.cjs +907 -189
- package/dist/three-live-full.js +907 -189
- package/dist/three-live-full.min.js +907 -189
- package/dist/three-live-procedural.cjs +16 -0
- package/dist/three-live-procedural.d.ts +759 -0
- package/dist/three-live-procedural.js +16 -0
- package/dist/three-live-procedural.min.js +16 -0
- package/dist/three-live-sandbox.html +11 -1
- package/dist/three-live-studio.cjs +72 -17
- package/dist/three-live-studio.d.ts +190 -0
- package/dist/three-live-studio.js +72 -17
- package/dist/three-live-studio.min.js +73 -18
- package/dist/three-live-tsl.cjs +103 -0
- package/dist/three-live-tsl.d.ts +759 -0
- package/dist/three-live-tsl.js +103 -0
- package/dist/three-live-tsl.min.js +103 -0
- package/dist/three-live-viewer.cjs +50 -85
- package/dist/three-live-viewer.d.ts +190 -0
- package/dist/three-live-viewer.js +50 -85
- package/dist/three-live-viewer.min.js +50 -85
- package/dist/three-live-webgpu.cjs +585 -0
- package/dist/three-live-webgpu.d.ts +759 -0
- package/dist/three-live-webgpu.js +585 -0
- package/dist/three-live-webgpu.min.js +585 -0
- package/dist/three-live.cjs +147 -165
- package/dist/three-live.js +147 -165
- package/dist/three-live.min.js +147 -165
- package/dist/three-view.cjs +49 -84
- package/dist/three-view.d.ts +190 -0
- package/dist/three-view.js +50 -85
- package/dist/three-xr.cjs +141 -159
- package/dist/three-xr.d.ts +190 -0
- package/dist/three-xr.js +140 -158
- package/package.json +21 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/var V="__THREE_LIVE_REGISTRY__";function F(){let e=typeof globalThis<"u"?globalThis:{};if(!e[V]){let t={plugins:[],decoders:new Map};try{Object.defineProperty(e,V,{value:t,writable:!1,configurable:!1,enumerable:!1})}catch{e[V]=t}}return e[V]}var hn=F().plugins;function tr(e){let t=F().plugins;t.some(r=>r.name===e.name)||t.push(e),e.onRegister?.()}var De={eye:[0,1.6,5],target:[0,.8,0]},Oe=new Set("the a an please can could would you will i want wanna to me my that this those these of one ones object objects thing things item items mesh meshes piece pieces part parts just only also too is are there here which select pick choose highlight grab focus on in at by some most side hand them it its and s now again as well other others".split(" ")),rr=["sofa couch settee","lamp light lantern","door gate doorway","table desk","chair seat stool","rug carpet mat","cup mug","tv television screen monitor","car vehicle automobile","box crate cube","ball sphere orb","floor ground","plant tree bush","cabinet cupboard closet"].map(e=>e.split(" ")),H=[["red",[220,40,40],"red"],["orange",[245,140,30],"orange"],["yellow",[240,220,40],"yellow"],["gold",[212,175,55],"yellow"],["green",[50,170,60],"green"],["teal",[0,128,128],"green"],["cyan",[40,200,220],"blue"],["blue",[40,80,220],"blue"],["navy",[20,30,100],"blue"],["purple",[130,50,180],"purple"],["magenta",[220,40,200],"pink"],["pink",[240,120,180],"pink"],["brown",[130,80,40],"brown"],["beige",[225,205,160],"brown"],["white",[245,245,245],"white"],["silver",[192,192,192],"gray"],["gray",[128,128,128],"gray"],["black",[20,20,20],"black"]],qe={grey:"gray",golden:"gold",crimson:"red",maroon:"red",violet:"purple",aqua:"cyan",turquoise:"cyan",tan:"beige"},le={};function $(e,t){for(let r of e.split(" "))le[r]=t}$("left leftmost",["right",!1]),$("right rightmost",["right",!0]),$("front nearest closest frontmost",["depth",!1]),$("back farthest furthest rear far backmost",["depth",!0]),$("top highest upper topmost",["up",!0]),$("bottom lowest lower bottommost",["up",!1]),$("biggest largest big large huge bigger larger",["size",!0]),$("smallest small tiny little smaller",["size",!1]),$("tallest tall taller",["height",!0]),$("shortest short shorter",["height",!1]),$("middle center centre central","middle");var ze={first:0,second:1,third:2,fourth:3,fifth:4,last:-1},Be={two:2,pair:2,three:3,four:4,five:5,six:6,seven:7,eight:8,nine:9,ten:10},We=new Set(["all","every","everything","each","everybody"]),ce=e=>/^[0-9]+$/.test(e),J=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),ue=e=>J(le,e)?le[e]:null,Ve=e=>J(ze,e)?ze[e]:null;function He(e){let t;if(J(Be,e))t=Be[e];else if(/^[0-9]{1,3}$/.test(e))t=Number(e);else return null;return t>=1&&t<=50?t:null}function Je(e){return J(qe,e)?qe[e]:H.some(t=>t[0]===e)?e:null}function nr(e){for(let t=0;t<e.length;t++){let r=e[t],n=e[t+1]??"";if(/^(near|nearby|beside|behind|under|underneath|beneath|above|below|between|inside|within|against|with|beyond|around)$/.test(r)||(r==="next"||r==="close")&&n==="to"||(r==="front"||r==="top")&&n==="of")return!0;if(r==="on"||r==="by"){let i=t+1;for(;i<e.length&&/^(the|a|an|its|my)$/.test(e[i]);)i++;let o=e[i];if(o!==void 0&&!ue(o)&&o!=="side"&&o!=="end")return!0}}return!1}function ir(e){return String(e??"").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)}function Ge(e){let t=e.length;if(t>4&&e.endsWith("ies"))return e.slice(0,-3)+"y";if(t>4&&e.endsWith("ves"))return e.slice(0,-3)+"f";for(let r of["ches","shes","xes","sses","zes"])if(t>r.length+1&&e.endsWith(r))return e.slice(0,-2);return t>3&&e.endsWith("s")&&!e.endsWith("ss")&&!e.endsWith("us")?e.slice(0,-1):e}function or(e){let t=[],r="",n="",i=s=>s>="a"&&s<="z",o=s=>s>="A"&&s<="Z",a=s=>s>="0"&&s<="9";for(let s of String(e)){if(!(i(s)||o(s)||a(s))){r&&t.push(r),r="",n="";continue}n&&r&&(i(n)&&o(s)||a(n)!==a(s))&&(t.push(r),r=""),r+=s.toLowerCase(),n=s}return r&&t.push(r),t.map(Ge)}var ar=e=>e.replace(/[^A-Za-z0-9]/g,"").toLowerCase(),sr=(e,t)=>rr.some(r=>r.includes(e)&&r.includes(t));function Ye(e,t){return e===t||sr(e,t)||t.length>=4&&e.length>=4&&(e.startsWith(t)||t.startsWith(e))}function he(e){let t=Math.min(1,Math.max(0,Number.isFinite(e)?e:0));return(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)*255}function lr(e){let t=[he(e[0]),he(e[1]),he(e[2])],r=H[0][0],n=1/0;for(let[i,o]of H){let a=(t[0]-o[0])**2+(t[1]-o[1])**2+(t[2]-o[2])**2;a<n&&(n=a,r=i)}return r}var cr=e=>H.find(t=>t[0]===e)?.[2]??"";function ur(e,t){return e.words.includes(t)?!0:e.colorName!=null&&(e.colorName===t||cr(e.colorName)===t)}function de(e){return e.map((t,r)=>{let n=[0,1,2].map(i=>Math.abs(t.max[i]-t.min[i]));return{id:r,name:t.name,words:or(t.name),colorName:t.color?lr(t.color):null,size:Math.max(n[0],.01)*Math.max(n[1],.01)*Math.max(n[2],.01),height:n[1],center:[0,1,2].map(i=>(t.min[i]+t.max[i])/2)}})}function hr(e){let t=[0,1,2].map(a=>e.target[a]-e.eye[a]),r=Math.hypot(t[0],t[1],t[2]),n=r<1e-9?[0,0,-1]:t.map(a=>a/r),i=[-n[2],0,n[0]],o=Math.hypot(i[0],i[1],i[2]);return{eye:e.eye,right:o<1e-9?[1,0,0]:i.map(a=>a/o)}}function N(e,t,r){let n=[0,1,2].map(i=>e.center[i]-r.eye[i]);switch(t){case"right":return n[0]*r.right[0]+n[1]*r.right[1]+n[2]*r.right[2];case"depth":return Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);case"up":return e.center[1];case"size":return e.size;default:return e.height}}var Ze=e=>e.length?e.reduce((t,r)=>t+r,0)/e.length:0;function pe(e,t,r){let n;if(t==="middle"){if(e.length<3)return e;let i=e.map(s=>N(s,"right",r)),o=Math.min(...i),a=Math.max(...i);n=e.filter((s,l)=>i[l]>o+1e-6&&i[l]<a-1e-6)}else{let i=e.map(a=>N(a,t[0],r)),o=Ze(i);n=e.filter((a,s)=>t[1]?i[s]>o+1e-6:i[s]<o-1e-6)}return n.length?n:e}function Ke(e,t,r){let n;if(t==="middle"){let i=Ze(e.map(o=>N(o,"right",r)));n=e.map(o=>[o,Math.abs(N(o,"right",r)-i)])}else n=e.map(i=>[i,t[1]?-N(i,t[0],r):N(i,t[0],r)]);return n.sort((i,o)=>i[1]-o[1])}function E(e,t,r){let n=[...e].sort((a,s)=>a.id-s.id),i=[...new Set(n.map(a=>a.id))],o=[];for(let a of n)!o.includes(a.name)&&o.length<50&&o.push(a.name);return t==="high"?{ids:i,names:o,confidence:t,candidates:[],reason:r}:t==="ambiguous"?{ids:[],names:[],confidence:t,candidates:o.slice(0,10),reason:r}:{ids:[],names:[],confidence:t,candidates:[],reason:r}}var I=e=>E([],"none",e);function Xe(e,t,r=De,n){let i=n?e.filter(a=>n.includes(a.name)):[...e],o=ir(t);for(let a=0;a<o.length;a++){let s=o[a],l=o[a+1]??"",c=s==="except"||s==="excluding";if(!(c||s==="but"&&l==="not"||s==="other"&&l==="than"))continue;let u=me(i,o.slice(0,a),r);if(u.confidence!=="high")return u;let d=i.filter(p=>u.ids.includes(p.id)),m=me(d,o.slice(a+(c?1:2)),r);if(m.confidence!=="high")return E(d,"ambiguous","except");let h=d.filter(p=>!m.ids.includes(p.id));return h.length?E(h,"high","match"):I("no_match")}return me(i,o,r)}function dr(e){return!(Oe.has(e)||We.has(e)||e==="both"||He(e)!==null||Ve(e)!==null||Je(e)!==null||ue(e)!==null||ce(e))}function me(e,t,r){let n=[[]];for(let l of t)l==="and"?n.push([]):n[n.length-1].push(l);let i=n.filter(l=>l.length);if(i.length<2||!i.every(l=>l.some(dr)))return Qe(e,t,r);let o=[],a=!1;for(let l of i){let c=Qe(e,l,r);if(c.confidence==="none")return c;c.confidence==="ambiguous"?(a=!0,o.push(...e.filter(u=>c.candidates.includes(u.name)))):o.push(...e.filter(u=>c.ids.includes(u.id)))}let s=[...new Map(o.map(l=>[l.id,l])).values()];return a?E(s,"ambiguous","multiple"):E(s,"high","match")}function Qe(e,t,r){if(!e.length)return I("no_match");let n=0;for(;n<t.length&&/^(select|pick|choose|highlight|grab|the|a|an|just|only)$/.test(t[n]);)n++;let i=t.slice(n).join("");if(i){let f=e.filter(g=>ar(g.name)===i);if(f.length)return E(f,"high","exact")}if(nr(t))return I("relation");let o=[],a=!1,s=!1,l=null,c=null,u=[],d=[],m=!1;for(let f of t){if(m&&ce(f)){o.push(f);continue}m=!1;let g,x,w;if(We.has(f))s=!0,a=!0;else if(f==="both")l=2,a=!0;else if((g=He(f))!==null)l=g,a=a||g>1;else if((g=Ve(f))!==null)c=g;else if((x=Je(f))!==null)u.push(x);else if((w=ue(f))!==null)d.push(w);else if(!Oe.has(f)){let b=Ge(f);b!==f&&(a=!0),o.push(b),m=!0}}if(!o.length&&!u.length&&!d.length&&c===null&&!s&&l===null)return I("empty");let h=hr(r),p=e;if(o.length){let f=p.map(b=>({e:b,n:o.filter(k=>b.words.some(T=>Ye(T,k))).length})),g=Math.max(0,...f.map(b=>b.n));if(g===0)return I("no_match");p=f.filter(b=>b.n===g).map(b=>b.e);let x=b=>b.words.filter(k=>!ce(k)&&!o.some(T=>Ye(k,T))).length,w=Math.min(...p.map(x));p=p.filter(b=>x(b)===w)}if(u.length&&(p=p.filter(f=>u.some(g=>ur(f,g)))),!p.length)return I("no_match");if(c!==null){for(let w of d)p=pe(p,w,h);let f=Ke(p,["right",!1],h),g=Math.min(l??1,f.length),x;if(c<0)x=f.slice(f.length-g);else{let w=c*(l!==null?g:1);if(w>=f.length)return I("no_match");x=f.slice(w,w+g)}return E(x.map(w=>w[0]),"high","match")}let y=!a&&l===null,v=y?1:s?null:l;if(v!==null&&d.length){for(let x of d.slice(0,-1))p=pe(p,x,h);let f=Ke(p,d[d.length-1],h),g=v;if(f.length<g)return E(f.map(x=>x[0]),"ambiguous","count_mismatch");if(f.length>g&&Math.abs(f[g][1]-f[g-1][1])<=1e-6){let x=f[g-1][1];return E(f.filter(w=>Math.abs(w[1]-x)<=1e-6).map(w=>w[0]),"ambiguous","multiple")}return E(f.slice(0,g).map(x=>x[0]),"high","match")}for(let f of d)p=pe(p,f,h);return y?p.length===1?E(p,"high","match"):E(p,"ambiguous","multiple"):l!==null&&p.length!==l?E(p,"ambiguous","count_mismatch"):E(p,"high","match")}var et="make paint color colour turn move rotate scale resize delete remove change set add put give raise lower shrink enlarge hide show swap replace spin flip double halve duplicate copy animate brighten darken".split(" "),G=(e,t)=>{for(let r of t)if(e.startsWith(r))return e.slice(r.length).trim()||null;return null},fe=(e,t)=>{for(let r of t)if(e.endsWith(r))return e.slice(0,-r.length).trim()||null;return null},ge=e=>e.replace(/[.!?,]+$/,"");function pr(e){let t=e.split(" ");for(let r=0;r<t.length;r++){let n=t[r].endsWith(","),i=r>0&&(t[r]==="and"||t[r]==="then");if(!(n||i))continue;let o=r+1;for(;o<t.length&&(t[o]==="then"||t[o]==="and");)o++;if(o>=t.length||!et.includes(t[o]))continue;let a=(i?t.slice(0,r):t.slice(0,r+1)).join(" ").replace(/,+$/,"").trim();if(a)return[a,t.slice(o).join(" ")]}return[e,null]}function tt(e,t){let r=e.split(/\s+/).filter(Boolean);return r.length&&ge(r[r.length-1]).toLowerCase()==="please"&&r.pop(),ge(r.slice(Math.max(0,r.length-t)).join(" "))}function ye(e,t,r){let[n,i]=pr(t),o=fe(n,[" too"," as well"," also"]);if(o!==null&&e==="replace"&&(e="add",n=o),i!==null){let a=i.split(/\s+/).filter(Boolean).length;return{kind:"mixed",mode:e,query:n,instruction:tt(r,a)}}return{kind:"select",mode:e,query:n}}var mr=["please ","ok ","ok, ","okay ","okay, ","now ","now, ","hey ","um ","um, ","uh ","uh, ","so ","so, ","and "],fr=new Set(["clear selection","clear the selection","reset selection","reset the selection","select nothing","select none","deselect","deselect all","deselect everything","unselect","unselect all","unselect everything","clear","clear all"]),gr=["select only ","select just ","only select ","just select ","select ","pick ","choose ","highlight ","grab ","focus on "];function rt(e){let t=String(e??"").trim(),r=t.toLowerCase().replace(/[\u2018\u2019]/g,"'").split(/\s+/).filter(Boolean).join(" ");for(r=ge(r);;){let i=r.length;for(let o of mr)r.startsWith(o)&&(r=r.slice(o.length));if(r.endsWith(" please")&&(r=r.slice(0,-7).replace(/,+$/,"")),r.length===i)break}if(r=r.trim(),fr.has(r))return{kind:"clear",mode:"clear"};let n=G(r,["deselect ","unselect ","don't select ","do not select "]);if(n!==null)return ye("remove",n,t);for(let i of["remove ","take "])if(r.startsWith(i)){let o=fe(r.slice(i.length),[" from the selection"," from selection"," out of the selection"]);if(o!==null)return{kind:"select",mode:"remove",query:o}}if(n=G(r,["also select ","also pick ","also choose ","also add "]),n!==null)return ye("add",n,t);if(r.startsWith("add ")){let i=fe(r.slice(4),[" to the selection"," to selection"]);if(i!==null)return{kind:"select",mode:"add",query:i}}return n=G(r,gr),n!==null||(n=G(r,["just ","only "]),n!==null&&!et.includes(n.split(" ")[0]))?ye("replace",n,t):{kind:"edit",instruction:tt(t,1/0)}}var nt=50;function be(e){let t=[];for(let r of e){let n=typeof r=="string"?r.slice(0,200):"";if(n&&!t.includes(n)&&t.push(n),t.length>=nt)break}return t}function A(e){let t=e.__tlSelection??[],r=e.selectedMesh||null;return(t[0]??null)!==r?r?[r]:[]:[...t]}function j(e,t,r={}){let n=r.mode??"replace",i=A(e),o=be(typeof t=="string"?[t]:t??[]),a;switch(n){case"clear":a=[];break;case"add":a=be([...i,...o]);break;case"remove":a=i.filter(u=>!o.includes(u));break;case"toggle":a=be([...i.filter(u=>!o.includes(u)),...o.filter(u=>!i.includes(u))]);break;default:a=o}let s=a.filter(u=>!i.includes(u)),l=i.filter(u=>!a.includes(u));if(!s.length&&!l.length&&a.join(`
|
|
17
|
+
`)===i.join(`
|
|
18
|
+
`))return null;e.__tlSelection=a,e.selectedMesh=a[0]??null,e.viewer?.setSelectedMeshes?.(a);let c={mesh:a[0]??null,meshes:a,added:s,removed:l,source:r.source??"api",...r.query?{query:String(r.query).slice(0,2e3)}:{}};return e.fireEvent?.("three-live-select",c),c}function yr(e){!e||e.__tlSelectionInstalled||(e.__tlSelectionInstalled=!0,"selectedMeshes"in e||Object.defineProperty(e,"selectedMeshes",{get:()=>A(e),set:t=>{j(e,t??[],{mode:"replace",source:"api"})},configurable:!0}),e.addEventListener?.("three-load",()=>{let t=A(e);t.length>1&&e.viewer?.setSelectedMeshes?.(t)}))}function we(e){if(!e.length)return"The user cleared the selection.";if(e.length===1)return`The user selected the mesh "${e[0]}". "This" or "it" now refers to it.`;let t=e.slice(0,20).map(n=>`"${n}"`),r=e.length>20?` and ${e.length-20} more`:"";return`The user selected ${e.length} meshes: ${t.join(", ")}${r}. "These", "them", or "it" refer to them.`}var ve=new WeakMap;function br(e){let t=e;if(ve.has(t))return ve.get(t);let r=[1/0,1/0,1/0],n=[-1/0,-1/0,-1/0];for(let o=0;o+2<e.length;o+=3)for(let a=0;a<3;a++){let s=e[o+a];s<r[a]&&(r[a]=s),s>n[a]&&(n[a]=s)}let i=Number.isFinite(r[0])?[r,n]:null;return ve.set(t,i),i}function Y(e){let t=e.viewer?.meshes??[],r=[];for(let n of t.slice(0,5e3)){if(!n?.name||n.visible===!1)continue;let i=n.transform,o=[1/0,1/0,1/0],a=[-1/0,-1/0,-1/0];for(let l of n.primitives??[]){let c=l.positions?br(l.positions):null;if(c)for(let u=0;u<8;u++){let d=c[u&1?1:0][0],m=c[u&2?1:0][1],h=c[u&4?1:0][2];for(let p=0;p<3;p++){let y=i?i[p]*d+i[4+p]*m+i[8+p]*h+i[12+p]:[d,m,h][p];y<o[p]&&(o[p]=y),y>a[p]&&(a[p]=y)}}}if(!Number.isFinite(o[0]))continue;let s=n.primitives?.[0]?.baseColor;r.push({name:n.name,min:o,max:a,color:s?[s[0],s[1],s[2]]:null})}return r}function xe(e){let t=e.viewer,r=t?.getCameraOrbit?.(),n=t?.getCameraTarget?.();if(!r||!n||![r.theta,r.phi,r.radius,n.x,n.y,n.z].every(Number.isFinite))return De;let i=r.radius*Math.cos(r.phi);return{eye:[n.x+i*Math.sin(r.theta),n.y+r.radius*Math.sin(r.phi),n.z+i*Math.cos(r.theta)],target:[n.x,n.y,n.z]}}function it(e,t,r={}){let n=de(Y(e));return Xe(n,t,xe(e),r.within??null)}var wr=["You find the objects a user means in a 3D scene.",'Reply with JSON only: {"ids": [numbers], "confidence": "high" or "low", "question": "text"}.',"Use only ids from the objects list.",`Left and right are as seen from the camera: "right" is the camera's right direction.`,'If several objects fit equally, or none fits, set confidence to "low" and ask one short question that names the options.'].join(" "),P=e=>Math.round(e*100)/100;function ot(e,t,r,n=[]){let i=[0,1,2].map(s=>r.target[s]-r.eye[s]),o=Math.hypot(i[2],i[0])||1,a={camera:{eye:r.eye.map(P),target:r.target.map(P),right:[P(-i[2]/o),0,P(i[0]/o)]},objects:e.slice(0,300).map(s=>({id:s.id,name:s.name,color:s.colorName,center:s.center.map(P),size:P(Math.cbrt(s.size))})),selected:n.slice(0,50),request:String(t).slice(0,500)};return[{role:"system",content:wr},{role:"user",content:JSON.stringify(a)}]}function at(e,t){let r=String(e??""),n=r.indexOf("{"),i=r.lastIndexOf("}"),o={};if(n>=0&&i>n)try{o=JSON.parse(r.slice(n,i+1))??{}}catch{}let a=[];for(let l of Array.isArray(o.ids)?o.ids:[]){let c=Number.isInteger(l)?t[l]:void 0;c&&!a.includes(c.name)&&a.length<nt&&a.push(c.name)}let s=(typeof o.question=="string"?o.question:"").replace(/[\u0000-\u001f\u007f]+/g," ").trim().slice(0,300);return{names:a,confidence:o.confidence==="high"&&a.length?"high":"low",question:s}}async function st(e,t,r,n={}){let i=Y(e);n.within&&(i=i.filter(s=>n.within.includes(s.name)));let o=de(i);if(!o.length)return{names:[],confidence:"low",question:""};let a=await r.complete({messages:ot(o,t,xe(e),A(e)),signal:n.signal});return at(a,o)}var vr=32,xr=32,Sr=80,lt=2e3,Z=32*1024*1024,kr=1024,_r=256,Er=2048,ct=["You reconstruct a 3D scene from a reference image into a Three.js script for Three-Live, which compiles it to glTF for Web, Android, and iOS.","Reply with the COMPLETE script in a single ```js code block and nothing else.","Rules:","- Use the pre-defined globals `THREE`, `scene`, and `camera`. No imports, renderer, loaders, DOM, network, or timers.","- Decompose visible objects into `THREE.Group` and `THREE.Mesh` hierarchies with a unique, descriptive `.name` on every Mesh and Group (for example `Table`, `Chair_Left`, `Desk_Lamp`) before attaching `userData.xr` or `userData.interactivity`.","- Use real-world meters (1 unit = 1 m), +Y up, with the floor or resting surface at y = 0 (or the given metric floorY).","- When metric spatial priors (floor height, camera pose, planes, or 3D object boxes) are provided, place meshes at those exact metric [x, y, z] coordinates, dimensions, and yaw angles, and set `mesh.userData.xr = { grab: true }` on movable props.","- Supported: core geometries (`BoxGeometry`, `SphereGeometry`, `CylinderGeometry`, `ConeGeometry`, `CapsuleGeometry`, `PlaneGeometry`, `TorusGeometry`, `LatheGeometry`, `ExtrudeGeometry`, `TubeGeometry`, `Shape`, `Path`), `THREE.CSG`, `MeshStandardMaterial` / `MeshPhysicalMaterial` / `MeshStandardNodeMaterial` (loop-free `THREE.TSL`), `THREE.CanvasTexture`, `THREE.ProceduralTexture` presets (`wood`, `marble`, `brick`, `concrete`, `brushedMetal`, `rust`, `leather`, `fabric`, `carbonFiber`, `checkerboard`, `grid`), lights (`AmbientLight`, `HemisphereLight`, `DirectionalLight`, `PointLight`, `SpotLight`), and canonical `node.userData.xr` traits (`grab`, `anchor`, `physics`, `joint`, `handles`, `dimensions`, `billboard`, `orbit`, `follow`).","- Always include balanced lighting (`AmbientLight` or `HemisphereLight` plus a `DirectionalLight` or `PointLight`) and frame `camera.position.set(x, y, z)` and `camera.lookAt(tx, ty, tz)` so 100% of meshes are inside the camera frustum."].join(`
|
|
19
|
+
`);function ut(e){if(typeof e!="string")return"";let t="",r=!0;for(let n of e){let i=n.charCodeAt(0);i<=32||i===127||n==='"'||/\s/.test(n)?r||(t+=" ",r=!0):(t+=n,r=!1)}return Array.from(t.trim()).slice(0,Sr).join("")}function L(e){let t=Math.round(e*100)/100;return(Object.is(t,-0)||Math.abs(t)<1e-9?0:t).toFixed(2)}function ht(e){let t=Math.round(e),r=Object.is(t,-0)?0:t;return String(r)}function U(e){if(!Array.isArray(e)||e.length<3)return null;let t=Number(e[0]),r=Number(e[1]),n=Number(e[2]);return!Number.isFinite(t)||!Number.isFinite(r)||!Number.isFinite(n)?null:[t,r,n]}function Tr(e){if(!Array.isArray(e)||e.length<2)return null;let t=Number(e[0]),r=Number(e[1]);return!Number.isFinite(t)||!Number.isFinite(r)||t<=0||r<=0?null:[t,r]}function Ar(e){let t=U(e);return!t||t[0]<=0||t[1]<=0||t[2]<=0?null:t}function q(e){return`[${L(e[0])}, ${L(e[1])}, ${L(e[2])}]`}function Se(e){if(!e||typeof e!="object")return"";let t=[];if(typeof e.floorY=="number"&&Number.isFinite(e.floorY)&&t.push(`- Floor height: y = ${L(e.floorY)} m`),e.camera&&typeof e.camera=="object"){let r=U(e.camera.eye),n=U(e.camera.target);if(r&&n){let i=`- Camera: eye ${q(r)}, target ${q(n)}`,o=Number(e.camera.fovDeg);Number.isFinite(o)&&o>=1&&o<=179&&(i+=`, fov ${ht(o)} deg`),t.push(i)}}if(Array.isArray(e.planes)){let r=[];for(let n of e.planes){if(r.length>=vr)break;if(!n||typeof n!="object")continue;let i=U(n.center);if(!i)continue;let o=String(n.orientation??"").toLowerCase()==="vertical"?"vertical":"horizontal",a=ut(n.label),s=Tr(n.extent),l=U(n.normal),c=` ${r.length+1}. ${o}`;a&&(c+=` "${a}"`),c+=` at ${q(i)}`,s&&(c+=`, extent ${L(s[0])}m x ${L(s[1])}m`),l&&(c+=`, normal ${q(l)}`),r.push(c)}r.length>0&&t.push("- Planes:",...r)}if(Array.isArray(e.objects)){let r=[];for(let n of e.objects){if(r.length>=xr)break;if(!n||typeof n!="object")continue;let i=ut(n.label),o=U(n.center);if(!i||!o)continue;let a=Ar(n.size),s=typeof n.yawDeg=="number"&&Number.isFinite(n.yawDeg)?n.yawDeg:null,l=` ${r.length+1}. "${i}" at ${q(o)}`;a&&(l+=`, size ${L(a[0])}m x ${L(a[1])}m x ${L(a[2])}m`),s!==null&&(l+=`, yaw ${ht(s)} deg`),r.push(l)}r.length>0&&t.push("- Detected 3D objects (place meshes at these metric bounds):",...r)}return t.length===0?"":`Metric spatial priors (world meters, +Y up):
|
|
20
|
+
${t.join(`
|
|
21
|
+
`)}
|
|
22
|
+
`}function C(e){let t=String(e??"").trim();if(!t)return!1;if(/^data:image\/(png|jpeg|jpg|webp);base64,[A-Za-z0-9+/=\s]+$/i.test(t)){let r=t.indexOf(",");return r>0&&t.slice(r+1).trim().length>0}if(/[\s\x00-\x1f\x7f]/.test(t))return!1;try{let r=new URL(t);return r.protocol==="https:"?!!r.hostname:r.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(r.hostname)}catch{return!1}}function dt(e,t){let r=`Reconstruct the 3D scene shown in this reference image as a complete Three-Live script.
|
|
23
|
+
`;r+=Se(t);let n=Array.from(String(e??"").trim()).slice(0,lt).join("");return n&&(r+=`Additional instructions: ${n}
|
|
24
|
+
`),r+="Return only the complete script in a single ```js code block.",r}function pt(e,t,r){let n=String(e??"").trim();if(!C(n))throw new Error("compileFromImage: imageUrl must be a base64 data:image/(png|jpeg|webp) URI or an https:// URL.");let i=dt(t,r);return[{role:"system",content:ct},{role:"user",content:[{type:"image_url",image_url:{url:n}},{type:"text",text:i}]}]}function mt(e,t){let r=String(e??"").trim(),n=(t??[]).map(i=>String(i??"").trim()).filter(Boolean).slice(0,20);if(r){let i=`That script was rejected by the Three-Live sandbox: ${r}
|
|
25
|
+
`;return n.length>0&&(i+=`Additional diagnostics:
|
|
26
|
+
${n.map(o=>`- ${o}`).join(`
|
|
27
|
+
`)}
|
|
28
|
+
`),i+="Return the complete corrected script in a single ```js code block.",i}return n.length>0?`The script compiled, but the Three-Live linter reported warnings that should be fixed:
|
|
29
|
+
${n.map(i=>`- ${i}`).join(`
|
|
30
|
+
`)}
|
|
31
|
+
Fix every warning above (assign unique \`.name\` values before attaching \`userData.xr\`, use only canonical \`userData.xr\` traits, replace unsupported classes with supported Three-Live primitives, and frame \`camera.position\` and \`camera.lookAt\` so all meshes are inside the frustum) while keeping the full scene intact.
|
|
32
|
+
Return the complete corrected script in a single \`\`\`js code block.`:"Return the complete corrected script in a single ```js code block."}function ft(e){return e.length>=8&&e[0]===137&&e[1]===80&&e[2]===78&&e[3]===71&&e[4]===13&&e[5]===10&&e[6]===26&&e[7]===10?"image/png":e.length>=3&&e[0]===255&&e[1]===216&&e[2]===255?"image/jpeg":e.length>=12&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70&&e[8]===87&&e[9]===69&&e[10]===66&&e[11]===80?"image/webp":null}function Mr(e){let t=globalThis;if(t.Buffer&&typeof t.Buffer.from=="function")return t.Buffer.from(e.buffer,e.byteOffset,e.byteLength).toString("base64");let r="",n=8192;for(let i=0;i<e.length;i+=n){let o=e.subarray(i,Math.min(i+n,e.length));r+=String.fromCharCode(...o)}return btoa(r)}function $r(e){let t=Number(e);return Number.isFinite(t)?Math.max(_r,Math.min(Er,Math.round(t))):kr}function K(e){if(e.byteLength===0)throw new Error("compileFromImage: image buffer is empty.");if(e.byteLength>Z)throw new Error("compileFromImage: image exceeds the 32 MB limit.");let t=ft(e);if(!t)throw new Error("compileFromImage: unsupported image format (expected PNG, JPEG, or WebP magic bytes).");return`data:${t};base64,${Mr(e)}`}function jr(e,t,r,n){let i=Math.max(t,r),o=i>n?n/i:1,a=Math.max(1,Math.round(t*o)),s=Math.max(1,Math.round(r*o)),l=e;if(o===1&&typeof l.toDataURL=="function")try{let c=l.toDataURL("image/jpeg",.88);if(C(c))return c}catch{}if(typeof document<"u"&&typeof document.createElement=="function")try{let c=document.createElement("canvas");if(c&&typeof c.getContext=="function"){c.width=a,c.height=s;let u=c.getContext("2d");if(u&&typeof u.drawImage=="function"&&(u.drawImage(e,0,0,a,s),typeof c.toDataURL=="function")){let d=c.toDataURL("image/jpeg",.88);if(C(d))return d}}}catch{}if(typeof l.toDataURL=="function")try{let c=l.toDataURL("image/jpeg",.88);if(C(c))return c}catch{}return null}async function ke(e,t={}){if(t.signal?.aborted)throw t.signal.reason??new Error("compileFromImage aborted");if(e==null)throw new Error("compileFromImage: image input is required.");let r=$r(t.maxEdge);if(typeof e=="string"){let a=e.trim();if(!a)throw new Error("compileFromImage: image input is empty.");if(/^data:/i.test(a)){if(!C(a))throw new Error("compileFromImage: data URI must be a base64-encoded image/png, image/jpeg, or image/webp.");let h=a.indexOf(","),p=a.slice(0,h).toLowerCase().replace("image/jpg","image/jpeg"),y=a.slice(h+1).replace(/\s+/g,"");if(Math.floor(y.length*3/4)>Z)throw new Error("compileFromImage: image exceeds the 32 MB limit.");return`${p},${y}`}if(/^(javascript|vbscript|file|ftp|ws|wss):/i.test(a))throw new Error("compileFromImage: unsupported or unsafe image URL scheme.");if(/^http:/i.test(a)&&!C(a))throw new Error("compileFromImage: remote image URLs must use https:// (or http:// on localhost).");let s=/^https?:/i.test(a)&&C(a);if(s&&!t.fetch)return a;let l=a;if(!/^https?:|^blob:/i.test(a)){let h=globalThis.location?.href;if(!h)throw new Error("compileFromImage: imageUrl must be a base64 data:image/(png|jpeg|webp) URI or an https:// URL.");if(l=new URL(a,h).href,!/^blob:/i.test(l)&&!C(l))throw new Error("compileFromImage: resolved image URL must use https:// (or http:// on localhost).")}let c=t.fetch??globalThis.fetch?.bind(globalThis);if(typeof c!="function"){if(s)return a;throw new Error("compileFromImage: no fetch() implementation is available.")}let u=await c(l,{mode:"cors",credentials:"omit",signal:t.signal});if(!u.ok)throw new Error(`compileFromImage: failed to fetch image (HTTP ${u.status}).`);let d=Number(u.headers?.get?.("content-length"));if(Number.isFinite(d)&&d>Z)throw new Error("compileFromImage: image exceeds the 32 MB limit.");let m=await u.arrayBuffer();if(t.signal?.aborted)throw t.signal.reason??new Error("compileFromImage aborted");return K(new Uint8Array(m))}if(e instanceof ArrayBuffer||Object.prototype.toString.call(e)==="[object ArrayBuffer]")return K(new Uint8Array(e));if(ArrayBuffer.isView(e))return K(new Uint8Array(e.buffer,e.byteOffset,e.byteLength));if(typeof Blob<"u"&&e instanceof Blob||typeof e.arrayBuffer=="function"){let a=e;if(typeof a.size=="number"&&a.size>Z)throw new Error("compileFromImage: image exceeds the 32 MB limit.");let s=await a.arrayBuffer();if(t.signal?.aborted)throw t.signal.reason??new Error("compileFromImage aborted");return K(new Uint8Array(s))}let n=e,i=Number(n.videoWidth||n.naturalWidth||n.width||0),o=Number(n.videoHeight||n.naturalHeight||n.height||0);if(i>0&&o>0){let a=jr(e,i,o,r);if(a)return a}if(typeof n.toDataURL=="function"){let a=n.toDataURL("image/jpeg",.88);if(C(a))return a}if(typeof n.src=="string"&&n.src.trim())return ke(n.src,t);throw new Error("compileFromImage: unsupported or unloaded image source.")}async function gt(e,t,r={}){if(!e?.allowClientScripts)throw new Error("compileFromImage requires client scripts: add the allow-client-scripts attribute.");let n=Ne(e,r,"compileFromImage"),i=Pe(e);e.__tlAiAbort?.abort(new Error("compileFromImage superseded by a newer call"));let o=new AbortController;e.__tlAiAbort=o;let a=()=>o.abort(r.signal?.reason);r.signal?.aborted&&a(),r.signal?.addEventListener("abort",a,{once:!0});let s=o.signal,l=Array.from(String(r.prompt??"").trim()).slice(0,lt).join(""),c=Se(r.priors).length>0,u=Math.max(0,Math.min(3,Math.floor(Number(r.maxRepairs??1))||0)),d="",m="",h=[],p=0,y=null;try{let f=await ke(t,{maxEdge:r.maxEdge,signal:s,fetch:r.fetch});if(s.aborted)throw s.reason;let g=pt(f,l,r.priors);e.fireEvent?.("three-live-image-start",{prompt:l,hasPriors:c});for(let x=0;x<=u;x++){let w=await n.complete({messages:g,signal:s});if(s.aborted)throw s.reason;if(d=Fe(w),h=[],!d)m="The model reply did not contain a scene script.";else if(new TextEncoder().encode(d).length>Jt)m="The generated script exceeds the 1 MB script limit.";else try{let b=await Xt(e,d,s,"compileFromImage");h=[...b.warnings];let k=Kt(b.model);k.unnamedXrNodes>0&&!h.some(T=>T.includes("TL204"))&&h.push("[TL204] Node with userData.xr is missing a unique .name."),k.meshes===0?m="The generated scene contains no meshes.":(m="",y={code:d,warnings:[...h]})}catch(b){if(s.aborted)throw s.reason;let k=b,T=String(k?.message||b);m=k?.name&&k.name!=="Error"&&!T.startsWith(k.name)?`${k.name}: ${T}`:T}if(!m&&h.length===0)break;x<u&&(p++,g.push({role:"assistant",content:w},{role:"user",content:mt(m,h)}))}}finally{r.signal?.removeEventListener("abort",a),e.__tlAiAbort===o&&(e.__tlAiAbort=null)}if(m&&y&&(d=y.code,h=y.warnings,m=""),!m&&!r.dryRun)try{if(await e.executeScript(d),i.trim()){let f=e.__tlAiHistory??(e.__tlAiHistory=[]);f.push(i),f.length>Gt&&f.shift()}e.__tlAiSource=d,j(e,[],{mode:"clear",source:"api"})}catch(f){m=`The generated scene compiled but failed to load: ${f?.message||String(f)}`,e.__tlAiSource=i,i.trim()&&e.executeScript(i).catch(()=>{})}let v={applied:!m,code:d,repairs:p,warnings:h,...m?{error:m}:{}};return e.fireEvent?.("three-live-image",{prompt:l,...v}),v}var _e="three-live-texture-pack",Ee=1,yt=64,bt=6*1024*1024,X=16*1024*1024,Cr=64*1024*1024,wt=" Seamless tileable PBR albedo texture, flat even lighting, orthographic top-down view, no perspective, no shadows, no text.";function vt(){return F()}function xt(e){let t=vt().canonicalTextureSpec;if(typeof t!="function")throw new Error("AI textures need the procedural plugin (three-live-procedural.min.js or a full bundle).");return t(e)}function Rr(e){try{let t=new URL(e);return t.protocol==="https:"?!0:t.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(t.hostname)}catch{return!1}}function Q(e,t){let r=e.replace(/\s+/g,"");if(r.length>Math.ceil(t*4/3)+4)throw new Error("image exceeds the size limit");let n=atob(r),i=new Uint8Array(n.length);for(let o=0;o<n.length;o++)i[o]=n.charCodeAt(o);return i}function Lr(e){let t="";for(let r=0;r<e.length;r+=32768)t+=String.fromCharCode(...e.subarray(r,r+32768));return btoa(t)}async function Ir(e){if(e instanceof Uint8Array)return e;if(e instanceof ArrayBuffer)return new Uint8Array(e);if(e&&typeof e.arrayBuffer=="function")return new Uint8Array(await e.arrayBuffer());throw new Error("image provider returned no image bytes")}function St(e){let t=String(e?.endpoint??"");if(!Rr(t))throw new Error("ai-image: endpoint must be an https:// URL (or http:// on localhost).");let r=e.fetch??globalThis.fetch?.bind(globalThis);if(typeof r!="function")throw new Error("ai-image: no fetch() implementation is available.");let n=Math.max(1e3,Number(e.timeoutMs)||12e4),i=new URL(t).origin;return{name:"openai-images",model:e.model,async generateImage({prompt:o,signal:a}){let s=new AbortController,l=()=>s.abort(a?.reason);a?.aborted&&l(),a?.addEventListener("abort",l,{once:!0});let c=setTimeout(()=>s.abort(new Error(`ai-image: timed out after ${n} ms`)),n);try{let u={prompt:o+wt,n:1,size:e.imageSize||"1024x1024"};e.model&&(u.model=e.model),e.responseFormat!==null&&(u.response_format=e.responseFormat??"b64_json");let d=await r(t,{method:"POST",headers:{"Content-Type":"application/json",...e.headers},body:JSON.stringify(u),signal:s.signal}),m=await d.text();if(!d.ok)throw new Error(`ai-image: HTTP ${d.status} from endpoint: ${m.slice(0,300)}`);let h;try{h=JSON.parse(m)}catch{throw new Error("ai-image: endpoint returned non-JSON content.")}let p=h?.data?.[0];if(typeof p?.b64_json=="string")return Q(p.b64_json,X);let y=typeof p?.url=="string"?p.url:"";if(y.startsWith("data:image/"))return Q(y.slice(y.indexOf(",")+1),X);if(y){let v=!1;try{v=new URL(y,t).origin===i}catch{}if(!e.allowResponseUrls||!v)throw new Error('ai-image: the endpoint returned an image URL; use response_format "b64_json" (or allowResponseUrls for same-origin URLs).');let f=await r(new URL(y,t).href,{signal:s.signal});if(!f.ok)throw new Error(`ai-image: HTTP ${f.status} fetching the image.`);let g=new Uint8Array(await f.arrayBuffer());if(g.length>X)throw new Error("ai-image: image exceeds 16 MB.");return g}throw new Error("ai-image: response had no image.")}finally{clearTimeout(c),a?.removeEventListener("abort",l)}}}}function kt(e){return e.length>=8&&e[0]===137&&e[1]===80&&e[2]===78&&e[3]===71?"png":e.length>=3&&e[0]===255&&e[1]===216&&e[2]===255?"jpeg":e.length>=12&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70&&e[8]===87&&e[9]===69&&e[10]===66&&e[11]===80?"webp":null}var ee={async decode(e){let t=kt(e);if(!t)throw new Error("image must be PNG, JPEG, or WebP");let r=new Blob([e],{type:`image/${t}`}),n=await createImageBitmap(r,{premultiplyAlpha:"none",colorSpaceConversion:"none"}),{width:i,height:o}=n,a=(typeof OffscreenCanvas<"u"?new OffscreenCanvas(i,o):Object.assign(document.createElement("canvas"),{width:i,height:o})).getContext("2d",{willReadFrequently:!0});if(!a)throw new Error("2D canvas unavailable for image decoding");a.drawImage(n,0,0),n.close?.();let s=a.getImageData(0,0,i,o).data;return{width:i,height:o,rgba:new Uint8Array(s.buffer,s.byteOffset,s.byteLength)}},async encodePng(e,t){let r=vt().encodeRgbaPngDataUri;if(typeof r=="function"){let n=r(e,t,t),i=Q(n.slice(n.indexOf(",")+1),1/0),o=F().makePngDeflater?.(globalThis.CompressionStream);return o?o.png(i):i}throw new Error("PNG encoding needs the procedural plugin")}};function Fr(e,t,r,n,i){let o=new Uint8Array(n*i*4);for(let a=0;a<i;a++){let s=Math.floor(a*r/i),l=Math.min(r,Math.max(s+1,Math.floor((a+1)*r/i)));for(let c=0;c<n;c++){let u=Math.floor(c*t/n),d=Math.min(t,Math.max(u+1,Math.floor((c+1)*t/n))),m=0,h=0,p=0,y=0,v=0;for(let g=s;g<l;g++)for(let x=u;x<d;x++){let w=(g*t+x)*4;m+=e[w],h+=e[w+1],p+=e[w+2],y+=e[w+3],v++}let f=(a*n+c)*4;o[f]=Math.floor((m+v/2)/v),o[f+1]=Math.floor((h+v/2)/v),o[f+2]=Math.floor((p+v/2)/v),o[f+3]=Math.floor((y+v/2)/v)}}return o}function Nr(e,t){let r=Math.max(1,Math.floor(t/8)),n=Math.floor(t/2),i=a=>{let s=Math.max(0,Math.min(1,a));return s*s*(3-2*s)},o=new Uint8Array(t*t*4);for(let a=0;a<t;a++){let s=(Math.min(a,t-1-a)+.5)/r;for(let l=0;l<t;l++){let c=(Math.min(l,t-1-l)+.5)/r,u=1-i(Math.min(c,s)),d=(a*t+l)*4,m=((a+n)%t*t+(l+n)%t)*4;for(let h=0;h<3;h++){let p=e[d+h];o[d+h]=Math.max(0,Math.min(255,Math.floor(p+(e[m+h]-p)*u+.5)))}o[d+3]=255}}return o}function _t(e,t){let r=Math.min(e.width,e.height);if(!(r>0))throw new Error("generated image is empty");let n=Math.floor((e.width-r)/2),i=Math.floor((e.height-r)/2),o=new Uint8Array(r*r*4);for(let s=0;s<r;s++){let l=((i+s)*e.width+n)*4;o.set(e.rgba.subarray(l,l+r*4),s*r*4)}let a=r===t?o:Fr(o,r,r,t,t);for(let s=3;s<a.length;s+=4)a[s]=255;return Nr(a,t)}async function Et(e,t=ee){let r=typeof e=="string"?JSON.parse(e):e;if(!r||r.format!==_e)throw new Error(`texture pack: format must be "${_e}"`);if(r.version!==Ee)throw new Error(`texture pack: version must be ${Ee}`);if(!r.entries||typeof r.entries!="object"||Array.isArray(r.entries))throw new Error("texture pack: entries must be an object");let n=Object.keys(r.entries);if(n.length>yt)throw new Error(`texture pack: more than ${yt} entries`);let i=new Map;for(let o of n){let a=r.entries[o];if(!/^[0-9a-f]{16}$/.test(o))throw new Error(`texture pack: invalid key '${o}'`);let s=xt(a?.spec);if(!s)throw new Error(`texture pack: entry ${o}: invalid spec`);if(s.key!==o)throw new Error(`texture pack: entry ${o}: key does not match its spec (${s.key})`);let l=typeof a?.albedo=="string"?a.albedo:"",c="data:image/png;base64,";if(!l.startsWith(c))throw new Error(`texture pack: entry ${o}: albedo must be a data:image/png;base64 URL`);let u=Q(l.slice(c.length),bt);if(u.length>bt||kt(u)!=="png")throw new Error(`texture pack: entry ${o}: albedo is not a PNG within 6 MB`);let d=await t.decode(u);if(d.width!==s.size||d.height!==s.size)throw new Error(`texture pack: entry ${o}: albedo is ${d.width}x${d.height}, expected ${s.size}x${s.size}`);let m=new Uint8Array(d.rgba);for(let p=3;p<m.length;p+=4)m[p]=255;let h=a.provenance&&typeof a.provenance=="object"?a.provenance:void 0;i.set(o,{key:o,spec:{prompt:s.prompt,size:s.size,seed:s.seed},rgba:m,...h?{provenance:h}:{}})}return i}async function Tt(e,t=ee){let r={};for(let n of[...e.keys()].sort()){let i=e.get(n),o=await t.encodePng(i.rgba,i.spec.size);r[n]={spec:i.spec,albedo:`data:image/png;base64,${Lr(o)}`,...i.provenance?{provenance:i.provenance}:{}}}return{format:_e,version:Ee,entries:r}}function Pr(e){let t=e?.gltf;if(t)return t;if(e instanceof ArrayBuffer&&e.byteLength>=20){let r=new DataView(e);if(r.getUint32(0,!0)===1179937895)try{return JSON.parse(new TextDecoder().decode(new Uint8Array(e,20,r.getUint32(12,!0))))}catch{}}return null}function Te(e){let t=new Map;for(let r of Pr(e)?.nodes??[]){let n=r?.extras?.xr?.material;if(!n||typeof n!="object"||n.prompt==null)continue;let i=xt(n);if(!i)continue;let o=t.get(i.key)??{spec:{prompt:i.prompt,size:i.size,seed:i.seed},meshes:[]};typeof r.name=="string"&&r.name&&o.meshes.push(r.name),t.set(i.key,o)}return[...t].map(([r,n])=>({key:r,spec:n.spec,meshes:n.meshes}))}function At(e=Cr){if(typeof indexedDB>"u")return null;let t=null,r=()=>t??(t=new Promise((i,o)=>{let a=indexedDB.open("three-live-textures",1);a.onupgradeneeded=()=>{a.result.createObjectStore("textures",{keyPath:"key"})},a.onsuccess=()=>i(a.result),a.onerror=()=>o(a.error)})),n=(i,o)=>r().then(a=>new Promise((s,l)=>{let c=a.transaction("textures",i),u=o(c.objectStore("textures"));c.oncomplete=()=>s(u?u.result:void 0),c.onerror=()=>l(c.error)}));return{async get(i){try{let o=await n("readonly",a=>a.get(i));return!o||!(o.rgba instanceof Uint8Array)?null:(n("readwrite",a=>a.put({...o,t:Date.now()})).catch(()=>{}),o)}catch{return null}},async put(i){try{await n("readwrite",l=>l.put({...i,t:Date.now()}));let o=await n("readonly",l=>l.getAll())??[],a=o.reduce((l,c)=>l+(c.rgba?.byteLength??0),0);o.sort((l,c)=>l.t-c.t);let s=[];for(let l of o){if(a<=e)break;a-=l.rgba?.byteLength??0,s.push(l.key)}s.length&&await n("readwrite",l=>{for(let c of s)l.delete(c)})}catch{}}}}var z=null,Mt=!1;function R(e){return e.__tlTex??(e.__tlTex={pack:new Map,packJson:null,failed:new Set,inflight:new Map,lastModel:null,lastRun:null,cache:void 0,loadSeq:0})}function Ur(e){let t=e.__tlTex;if(!t||t.pack.size===0)return null;let r={};for(let[n,i]of t.pack)r[n]={size:i.spec.size,rgba:i.rgba};return r}function Dr(){let e=F(),t=e.scriptRunner;if(!t||t.__tlTexWrapped)return;let r=n=>z&&!n?.texturePack?{...n,texturePack:z}:n;e.scriptRunner={__tlTexWrapped:!0,execute:(n,i,o,a)=>t.execute(n,i,o,r(a)),...typeof t.executeUrl=="function"?{executeUrl:(n,i,o,a)=>t.executeUrl(n,i,o,r(a))}:{}}}function $t(e,t){Dr();let r=z;z=Ur(e);try{return t()}finally{z=r}}function jt(e,t){let r=t??e.aiImageProvider;if(r&&typeof r.generateImage=="function")return r;let n=e.getAttribute?.("ai-image-endpoint");return n?St({endpoint:n,model:e.getAttribute?.("ai-image-model")||void 0}):null}function Ct(e){if(e.textureCache!==void 0)return e.textureCache;let t=R(e);return t.cache===void 0&&(t.cache=At()),t.cache}function Ae(e){return e.textureCodec??ee}function B(e){let t=e.querySelector?.('script[type="text/three"], script[type="text/three-live"]')?.textContent,r=R(e).lastRun??(t?{kind:"script",value:t}:null);return r?r.kind==="url"&&typeof e.loadScriptFromSrc=="function"?e.loadScriptFromSrc(r.value):e.executeScript(r.value):Promise.resolve()}function Rt(e,t){let r=R(e);r.pack.set(t.key,t),r.packJson=null,r.failed.delete(t.key)}async function Lt(e,t){let r=Ct(e);if(!r)return[];let n=[];for(let i of t){let o=await r.get(i.key);o&&o.spec?.size===i.spec.size&&o.rgba?.length===i.spec.size*i.spec.size*4&&(Rt(e,{key:i.key,spec:i.spec,rgba:o.rgba,provenance:o.provenance}),e.fireEvent?.("three-live-texture",{key:i.key,meshes:i.meshes,source:"cache"}),n.push(i.key))}return n}async function It(e,t,r,n){let i=R(e),o=i.inflight.get(r.key);if(o)return o;let a=(async()=>{try{let s=await Ir(await t.generateImage({...r.spec,signal:n}));if(s.length>X)throw new Error("generated image exceeds 16 MB");let l=_t(await Ae(e).decode(s),r.spec.size),c={provider:t.name??"custom",...t.model?{model:t.model}:{},createdAt:new Date().toISOString()};return Rt(e,{key:r.key,spec:r.spec,rgba:l,provenance:c}),Ct(e)?.put({key:r.key,spec:r.spec,rgba:l,provenance:c}),e.fireEvent?.("three-live-texture",{key:r.key,meshes:r.meshes,source:"provider"}),!0}catch(s){return i.failed.add(r.key),e.fireEvent?.("three-live-texture-error",{key:r.key,meshes:r.meshes,error:String(s?.message||s)}),!1}finally{i.inflight.delete(r.key)}})();return i.inflight.set(r.key,a),a}async function Or(e,t,r){let n=0,i=Array.from({length:Math.min(t,e.length)},async()=>{for(;n<e.length;)await r(e[n++])});await Promise.all(i)}function Ft(e,t){let r=R(e);r.lastModel=t;let n;try{n=Te(t).filter(o=>!r.pack.has(o.key))}catch{return}let i=n.filter(o=>!r.failed.has(o.key)&&!r.inflight.has(o.key));i.length&&(async()=>{if((await Lt(e,i)).length){await B(e).catch(()=>{});return}if((e.getAttribute?.("ai-textures")||"auto").toLowerCase()==="manual")return;let o=jt(e);if(!o)return;e.fireEvent?.("three-live-texture-start",{keys:i.map(s=>s.key)});let a=(await Promise.all(i.map(s=>It(e,o,s)))).filter(Boolean).length;a&&(Mt||(Mt=!0,console.info(`[three-live] Generated ${a} AI texture(s) at runtime. Each visitor without a cached copy triggers new (and different) generations. For consistent results, save el.exportTexturePack() and load it via texture-pack.`)),await B(e).catch(()=>{}))})()}function Nt(e,t){R(e).lastRun=t}async function Me(e,t){let r=R(e),n=++r.loadSeq;if(t==null){r.pack=new Map,r.packJson=null,await B(e).catch(()=>{});return}try{let i=await Et(t,Ae(e));if(n!==r.loadSeq)return;r.pack=i,r.packJson=typeof t=="string"?JSON.parse(t):t,r.failed.clear(),await B(e).catch(()=>{})}catch(i){e.fireEvent?.("three-live-texture-error",{key:null,meshes:[],error:String(i?.message||i)})}}async function Pt(e,t){try{let r=typeof document<"u"?document.baseURI:void 0,n=await fetch(new URL(t,r).href,{credentials:"same-origin"});if(!n.ok)throw new Error(`HTTP ${n.status} loading texture-pack "${t}"`);await Me(e,await n.json())}catch(r){e.fireEvent?.("three-live-texture-error",{key:null,meshes:[],error:String(r?.message||r)})}}async function qr(e,t={}){let r=R(e),n=t.meshes?new Set(t.meshes):null,i=Te(r.lastModel).filter(c=>(t.force||!r.pack.has(c.key))&&(!n||c.meshes.some(u=>n.has(u)))),o=[],a=[],s=[],l=i;if(!t.force){let c=new Set(await Lt(e,i));o.push(...c),l=i.filter(u=>!c.has(u.key))}if(l.length){let c=jt(e,t.provider);if(!c)throw new Error("generateTextures: no image provider. Set el.aiImageProvider, or the ai-image-endpoint (and ai-image-model) attributes.");e.fireEvent?.("three-live-texture-start",{keys:l.map(m=>m.key)});let u=Math.max(1,Math.min(4,Math.floor(Number(t.concurrency??2))||2)),d=m=>s.push(m);await Or(l,u,async m=>{r.failed.delete(m.key),t.force&&r.pack.delete(m.key),await It(e,c,m,t.signal)?a.push(m.key):d({key:m.key,error:"generation failed (see three-live-texture-error)"})})}return(o.length||a.length)&&await B(e).catch(()=>{}),{generated:a,cached:o,failed:s,pack:await Ut(e)}}async function Ut(e){let t=R(e);return t.packJson??(t.packJson=await Tt(t.pack,Ae(e)))}function zr(e){if(!e||e.__tlTexInstalled)return;e.__tlTexInstalled=!0,R(e),Object.defineProperty(e,"texturePack",{get:()=>e.__tlTex?.packJson??null,set:i=>{Me(e,i)},configurable:!0}),e.generateTextures=i=>qr(e,i),e.exportTexturePack=()=>Ut(e);let t=e.querySelector?.('script[type="application/three-live-texture-pack"]');t?.textContent?.trim()&&Me(e,t.textContent);let r=e.getAttribute?.("texture-pack");r&&Pt(e,r);let n=e;typeof MutationObserver<"u"&&n&&typeof n.nodeType=="number"&&new MutationObserver(()=>{let i=e.getAttribute?.("texture-pack");i&&Pt(e,i)}).observe(n,{attributes:!0,attributeFilter:["texture-pack"]})}var te=12;function re(e){return String(e).replace(/([a-z])([A-Z])/g,"$1 $2").replace(/([A-Za-z])([0-9])/g,"$1 $2").replace(/[_\-.]+/g," ").trim().slice(0,60)}function Br(e){if(typeof document>"u")return!1;let t=(e.getAttribute?.("ai-select-chips")??"").toLowerCase();return t!=="off"&&t!=="false"}function ne(e,t,r,n=!1){let i=document.createElement("button");return i.type="button",i.textContent=e,i.setAttribute("aria-label",t),Object.assign(i.style,{border:"none",borderRadius:"14px",padding:"4px 10px",font:"13px system-ui, sans-serif",cursor:"pointer",background:n?"#1a73e8":"rgba(255,255,255,0.92)",color:n?"#fff":"#202124"}),i.addEventListener("click",o=>{o.stopPropagation(),r()}),i}function ie(e){let t=e.__tlChips;t&&(t.off(),t.root.remove(),e.__tlChips=null)}function Dt(e,t,r){if(!Br(e))return null;let n=e.__tlChips;if(!n){let i=document.createElement("div");i.setAttribute("role","status"),i.setAttribute("aria-live","polite"),i.setAttribute("part","selection-chips"),Object.assign(i.style,{position:"absolute",left:"64px",bottom:"12px",maxWidth:"calc(100% - 76px)",boxSizing:"border-box",display:"flex",flexWrap:"wrap",alignItems:"center",gap:"6px",padding:"6px 8px",borderRadius:"18px",background:"rgba(32,33,36,0.85)",boxShadow:"0 1px 4px rgba(0,0,0,0.3)",zIndex:"10",font:"13px system-ui, sans-serif",color:"#fff",pointerEvents:"auto"});let o=()=>{e.__tlChips&&!e.__tlChips.state.question&&oe(e,e.__tlChips.state,r)};e.addEventListener?.("three-live-select",o),e.appendChild?.(i),n={root:i,off:()=>e.removeEventListener?.("three-live-select",o),state:t},e.__tlChips=n}return oe(e,t,r),n.root}function oe(e,t,r){let n=e.__tlChips;if(!n)return;n.state=t;let i=n.root;i.replaceChildren();let o=a=>{let s=document.createElement("span");s.textContent=a,i.appendChild(s)};if(t.question){o(t.question);for(let a of(t.candidates??[]).slice(0,te))i.appendChild(ne(re(a),`Select ${re(a)}`,()=>{j(e,[a],{mode:t.mode==="add"||t.mode==="remove"?t.mode:"replace",source:"tap",query:t.query}),oe(e,{pending:t.pending},r)}))}else{let a=A(e);if(!a.length&&!t.pending){ie(e);return}o(a.length?"Selected:":"Nothing selected");for(let s of a.slice(0,te))i.appendChild(ne(`${re(s)} \xD7`,`Remove ${re(s)} from the selection`,()=>j(e,[s],{mode:"remove",source:"tap"})));if(a.length>te&&o(`+${a.length-te} more`),t.pending&&r&&a.length){let s=t.pending;i.appendChild(ne(`Apply: ${s.slice(0,40)}`,`Apply: ${s.slice(0,200)}`,()=>{oe(e,{},r),r(s)},!0))}}i.appendChild(ne("\u2715","Dismiss",()=>ie(e)))}var Wr=500,D=2e3,Vr=8,Hr=[{name:"edit_scene",description:'Change the 3D scene the user is looking at. Call this whenever the user asks to add, remove, move, resize, recolor, or otherwise modify anything. Pass their request as a clear, self-contained instruction. If they selected a mesh, "this" or "it" refers to that mesh; the tool knows which one.',parameters:{type:"object",properties:{instruction:{type:"string",description:'The requested change, e.g. "make the roof red".'}},required:["instruction"]}},{name:"undo_edit",description:"Undo the last scene change.",parameters:{type:"object",properties:{}}},{name:"select_meshes",description:'Select objects in the scene so the user can see what an edit will change. Call this when the user refers to objects by name, color, or position ("the two red chairs on the left"). Pass their words as query, or exact mesh names as names. The result lists the selection, or a question to ask when the reference was unclear.',parameters:{type:"object",properties:{query:{type:"string",description:`The user's words for the objects, e.g. "the lamp next to the sofa".`},names:{type:"array",items:{type:"string"},description:"Exact mesh names, when known."},mode:{type:"string",enum:["replace","add","remove","clear"],description:"replace (default), add, remove, or clear."}}}}],Jr=["You are a voice assistant inside a live 3D scene editor.","Keep spoken replies short: one sentence.","To change the scene, call edit_scene with the request; never describe code.","After a tool result, say briefly what changed, or why it could not be done.","When the user refers to objects by name, color, or position, call select_meshes with their words first.","If its result has a question, ask it and wait; otherwise say what is selected, and if they also asked for a change, call edit_scene."].join(" ");function Gr(e){try{let t=new URL(e);return t.protocol==="wss:"?!0:t.protocol==="ws:"&&["localhost","127.0.0.1","[::1]"].includes(t.hostname)}catch{return!1}}function Yr(e){if(!Gr(e))throw new Error("connectVoice: url must be wss:// (or ws:// on localhost).");let t=new WebSocket(e),r=[],n={send(i){let o=JSON.stringify(i);t.readyState===1?t.send(o):t.readyState===0&&r.length<256&&r.push(o)},close(){r.length=0,t.onopen=t.onmessage=t.onclose=null,t.close(),n.onClose?.()}};return t.onopen=()=>{for(let i of r.splice(0))t.send(i)},t.onmessage=async i=>{let o=n.onMessage;try{let a=typeof i.data=="string"?i.data:await i.data.text(),s=JSON.parse(a);s&&typeof s=="object"&&o?.(s)}catch{}},t.onclose=()=>n.onClose?.(),n}function S(e){return typeof e=="string"?e:""}function M(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function $e(e,t){let r={...e};for(let[n,i]of Object.entries(t??{})){if(n==="__proto__"||n==="constructor"||n==="prototype")continue;let o=r[n];r[n]=i&&typeof i=="object"&&!Array.isArray(i)&&o&&typeof o=="object"&&!Array.isArray(o)?$e(o,i):i}return r}function Ot(e){if(e&&typeof e=="object")return M(e);try{return M(JSON.parse(S(e)||"{}"))}catch{return{}}}var qt={name:"ai-realtime",inputRate:24e3,setup(e){let t={type:"realtime",instructions:e.instructions,audio:{input:{format:{type:"audio/pcm",rate:24e3},turn_detection:{type:"server_vad"}},output:{format:{type:"audio/pcm",rate:24e3},...e.voice?{voice:e.voice}:{}}},tools:e.tools.map(r=>({type:"function",...r})),tool_choice:"auto",...e.model?{model:e.model}:{}};return[{type:"session.update",session:$e(t,e.overrides)}]},audio(e){return{type:"input_audio_buffer.append",audio:e}},text(e,t){let r=[{type:"conversation.item.create",item:{type:"message",role:t?"user":"system",content:[{type:"input_text",text:e}]}}];return t&&r.push({type:"response.create"}),r},toolResult(e,t,r){return[{type:"conversation.item.create",item:{type:"function_call_output",call_id:e,output:JSON.stringify(r)}},{type:"response.create"}]},parse(e){switch(S(e.type)){case"response.output_audio.delta":case"response.audio.delta":return S(e.delta)?[{type:"audio",base64:S(e.delta),rate:24e3}]:[];case"response.output_audio_transcript.delta":case"response.audio_transcript.delta":return[{type:"transcript",speaker:"model",text:S(e.delta),isFinal:!1}];case"response.output_audio_transcript.done":case"response.audio_transcript.done":return[{type:"transcript",speaker:"model",text:S(e.transcript),isFinal:!0}];case"conversation.item.input_audio_transcription.delta":return[{type:"transcript",speaker:"user",text:S(e.delta),isFinal:!1}];case"conversation.item.input_audio_transcription.completed":return[{type:"transcript",speaker:"user",text:S(e.transcript),isFinal:!0}];case"response.function_call_arguments.done":return[{type:"toolCall",id:S(e.call_id)||S(e.item_id),name:S(e.name),args:Ot(e.arguments)}];case"input_audio_buffer.speech_started":return[{type:"interrupted"}];case"response.done":return[{type:"turnComplete"}];case"error":return[{type:"error",message:S(M(e.error).message)||"Realtime server error"}];default:return[]}}},zt={name:"gemini-live",inputRate:16e3,setup(e){let t={...e.model?{model:e.model}:{},generationConfig:{responseModalities:["AUDIO"],...e.voice?{speechConfig:{voiceConfig:{prebuiltVoiceConfig:{voiceName:e.voice}}}}:{}},systemInstruction:{parts:[{text:e.instructions}]},tools:[{functionDeclarations:e.tools.map(r=>({...r}))}],inputAudioTranscription:{},outputAudioTranscription:{}};return[{setup:$e(t,e.overrides)}]},audio(e){return{realtimeInput:{audio:{mimeType:"audio/pcm;rate=16000",data:e}}}},text(e,t){return[{clientContent:{turns:[{role:"user",parts:[{text:e}]}],turnComplete:t}}]},toolResult(e,t,r){return[{toolResponse:{functionResponses:[{id:e,name:t,response:r}]}}]},parse(e){let t=[],r=M(e.serverContent);r.interrupted&&t.push({type:"interrupted"});let n=M(r.inputTranscription);S(n.text)&&t.push({type:"transcript",speaker:"user",text:S(n.text),isFinal:!1});let i=M(r.outputTranscription);S(i.text)&&t.push({type:"transcript",speaker:"model",text:S(i.text),isFinal:!1});let o=M(r.modelTurn).parts;for(let l of Array.isArray(o)?o:[]){let c=M(M(l).inlineData),u=S(c.mimeType);if(u.startsWith("audio/pcm")&&S(c.data)){let d=Number(/rate=(\d+)/.exec(u)?.[1])||24e3;t.push({type:"audio",base64:S(c.data),rate:d})}}r.turnComplete&&t.push({type:"turnComplete"});let a=M(e.toolCall).functionCalls;for(let l of Array.isArray(a)?a:[]){let c=M(l);t.push({type:"toolCall",id:S(c.id),name:S(c.name),args:Ot(c.args)})}let s=M(e.error);return S(s.message)&&t.push({type:"error",message:S(s.message)}),t}},Zr={"ai-realtime":qt,"gemini-live":zt};function Kr(e){let t=new Uint8Array(e.buffer,e.byteOffset,e.byteLength),r="";for(let n=0;n<t.length;n+=32768)r+=String.fromCharCode(...t.subarray(n,n+32768));return btoa(r)}function Xr(e,t,r){let n=t/r,i=Math.max(0,Math.floor(e.length/n)),o=new Int16Array(i);for(let a=0;a<i;a++){let s=a*n,l=Math.floor(s),c=Math.min(l+1,e.length-1),u=e[l]+(e[c]-e[l])*(s-l),d=Math.max(-1,Math.min(1,u));o[a]=d<0?d*32768:d*32767}return o}function Qr(e){let t=atob(e),r=t.length>>1,n=new Float32Array(r);for(let i=0;i<r;i++){let o=t.charCodeAt(i*2)|t.charCodeAt(i*2+1)<<8;o&32768&&(o-=65536),n[i]=o/32768}return n}function Bt(){let e=globalThis;return e.AudioContext??e.webkitAudioContext}function en(){let e=null,t=null,r=null;return{async start(n,i){let o=Bt(),a=globalThis.navigator?.mediaDevices;if(!o||!a?.getUserMedia)throw new Error("connectVoice: microphone capture is not available here.");e=await a.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,channelCount:1}}),t=new o;let s=t.createMediaStreamSource(e);r=t.createScriptProcessor(4096,1,1);let l=t.sampleRate;r.onaudioprocess=c=>{let u=Xr(c.inputBuffer.getChannelData(0),l,n);u.length&&i(Kr(u))},s.connect(r),r.connect(t.destination)},stop(){r&&(r.onaudioprocess=null),r?.disconnect();for(let n of e?.getTracks()??[])n.stop();t?.close(),r=null,e=null,t=null}}}function tn(){let e=null,t=0,r=new Set,n={play(i,o){let a=Bt();if(!e&&a)try{e=new a}catch{return}if(!e)return;e.state==="suspended"&&e.resume?.();let s=Qr(i);if(!s.length)return;let l=e.createBuffer(1,s.length,o);l.getChannelData(0).set(s);let c=e.createBufferSource();c.buffer=l,c.connect(e.destination);let u=Math.max(e.currentTime,t);c.start(u),t=u+l.duration,r.add(c),c.onended=()=>r.delete(c)},flush(){for(let i of r)try{i.stop()}catch{}r.clear(),t=0},close(){n.flush(),e?.close(),e=null}};return n}function _(e,t){e.fireEvent?.("three-live-voice",t)}var Wt=class{constructor(e,t){this.el=e,this.options=t,this.state="connecting",this.userText="",this.modelText="",this.onSelect=i=>{let o=i.detail;if(o?.source==="voice")return;let a=Array.isArray(o?.meshes)?o.meshes:o?.mesh?[o.mesh]:[];this.sendContext(we(a))};let r=t.provider??"ai-realtime",n=Zr[r];if(!n)throw new Error(`connectVoice: unknown provider '${r}' (use ai-realtime or gemini-live).`);if(this.adapter=n,t.transport)this.transport=t.transport;else if(t.url)this.transport=Yr(t.url);else throw new Error("connectVoice: pass a wss:// url or a transport.");this.mic=t.microphone===!1?null:t.microphoneSource??en(),this.sink=t.playAudio===!1?null:t.audioSink??tn()}async start(){this.transport.onMessage=r=>this.handle(r),this.transport.onClose=()=>this.shutdown();let e=[Jr,this.options.instructions].filter(Boolean).join(`
|
|
33
|
+
`);for(let r of this.adapter.setup({instructions:e,tools:Hr,model:this.options.model,voice:this.options.voice,overrides:this.options.session}))this.transport.send(r);this.sink?.play("",24e3),this.el.addEventListener?.("three-live-select",this.onSelect);let t=A(this.el);t.length&&this.sendContext(we(t));try{await this.mic?.start(this.adapter.inputRate,r=>{this.state!=="closed"&&this.transport.send(this.adapter.audio(r))})}catch(r){throw this.setState("error",{error:r?.message||String(r)}),this.close(),r}return this.setState("listening"),this}sendText(e){let t=String(e??"").trim().slice(0,D);if(!(!t||this.state==="closed"))for(let r of this.adapter.text(t,!0))this.transport.send(r)}close(){this.state!=="closed"&&(this.transport.onClose=void 0,this.transport.close(),this.shutdown())}shutdown(){this.state!=="closed"&&(this.mic?.stop(),this.sink?.close(),this.el.removeEventListener?.("three-live-select",this.onSelect),this.el.__tlVoiceSession===this&&(this.el.__tlVoiceSession=null),this.setState("closed"))}sendContext(e){if(this.state!=="closed")for(let t of this.adapter.text(e,!1))this.transport.send(t)}setState(e,t={}){this.state=e,_(this.el,{state:e,...t})}handle(e){if(this.state==="closed")return;let t=0;for(let r of this.adapter.parse(e))switch(r.type){case"audio":this.state!=="editing"&&(this.state="speaking"),this.sink?.play(r.base64,r.rate);break;case"interrupted":this.sink?.flush();break;case"transcript":r.speaker==="user"?(this.userText=(r.isFinal?r.text:this.userText+r.text).slice(-D),_(this.el,{state:this.state,transcript:this.userText,isFinal:r.isFinal}),r.isFinal&&(this.userText="")):(this.modelText=(r.isFinal?r.text:this.modelText+r.text).slice(-D),_(this.el,{state:this.state,reply:this.modelText,isFinal:r.isFinal}),r.isFinal&&(this.modelText=""));break;case"turnComplete":this.userText&&(_(this.el,{state:this.state,transcript:this.userText,isFinal:!0}),this.userText=""),this.modelText&&(_(this.el,{state:this.state,reply:this.modelText,isFinal:!0}),this.modelText=""),(this.state==="speaking"||this.state==="thinking")&&this.setState("listening");break;case"toolCall":++t<=Vr&&this.runTool(r.id,r.name,r.args);break;case"error":_(this.el,{state:this.state,error:r.message});break}}async runTool(e,t,r){let n;try{if(t==="edit_scene"){let i=S(r.instruction).trim().slice(0,D);if(!i)throw new Error("instruction is empty");if(typeof this.el.editScene!="function")throw new Error("scene editing is not available on this element");this.setState("editing",{transcript:i});let o=await this.el.editScene(i,{provider:this.options.editProvider});n=o.applied?{applied:!0,repairs:o.repairs}:{applied:!1,error:o.error??"edit rejected"},this.setState("thinking",{result:o})}else if(t==="select_meshes"){let i=S(r.mode),o=["replace","add","remove","clear"].includes(i)?i:"replace",a=Array.isArray(r.names)?r.names.filter(l=>typeof l=="string"):[],s=a.length&&o!=="clear"?Ht(this.el,a,o):await se(this.el,S(r.query),{mode:o,provider:this.options.editProvider??this.el.__tlSelectProvider?.()??null});n={changed:s.applied,selected:s.meshes.slice(0,50),say:s.say,...s.applied?{}:{question:s.say,candidates:s.match?.candidates??[]}}}else t==="undo_edit"?n=await this.el.undoEdit?.()??!1?{undone:!0}:{undone:!1,error:"nothing to undo"}:n={error:`unknown tool ${t}`}}catch(i){n={applied:!1,error:i?.message||String(i)},this.state==="editing"&&this.setState("thinking")}if(this.state!=="closed")for(let i of this.adapter.toolResult(e,t,n))this.transport.send(i)}};async function je(e,t){if(!t?.editProvider&&!e.aiProvider&&!e.getAttribute?.("ai-endpoint"))throw new Error("connectVoice: edits need a code model. Set el.aiProvider, the ai-endpoint attribute, or options.editProvider.");e.__tlVoiceSession?.close();let r=new Wt(e,t??{});return e.__tlVoiceSession=r,_(e,{state:"connecting"}),r.start()}function Vt(){let e=globalThis,t=e.SpeechRecognition??e.webkitSpeechRecognition;return t?{listen({lang:r,signal:n,onPartial:i}){return new Promise((o,a)=>{let s=new t;s.lang=r||globalThis.document?.documentElement?.lang||"en-US",s.interimResults=!0,s.continuous=!1,s.maxAlternatives=1;let l="",c="",u=!1,d=h=>{u||(u=!0,n?.removeEventListener("abort",m),h())},m=()=>{try{s.abort()}catch{}d(()=>a(n?.reason??new Error("voice edit aborted")))};if(s.onresult=h=>{let p="";for(let y=h.resultIndex;y<h.results.length;y++){let v=h.results[y];v.isFinal?l+=v[0].transcript:p+=v[0].transcript}c=(l+p).trim(),i?.(c)},s.onerror=h=>{h?.error==="no-speech"||h?.error==="aborted"||d(()=>a(new Error(`Speech recognition failed: ${h?.error||"unknown error"}`)))},s.onend=()=>d(()=>o((l||c).trim())),n?.aborted)return m();n?.addEventListener("abort",m,{once:!0});try{s.start()}catch(h){d(()=>a(h))}})}}:null}function ae(e,t){let r=globalThis;if(!(!r.speechSynthesis||!r.SpeechSynthesisUtterance))try{r.speechSynthesis.cancel();let n=new r.SpeechSynthesisUtterance(e);t&&(n.lang=t),r.speechSynthesis.speak(n)}catch{}}function Ce(e){return String(e).replace(/([a-z])([A-Z])/g,"$1 $2").replace(/([A-Za-z])([0-9])/g,"$1 $2").replace(/[_\-.]+/g," ").replace(/\s+/g," ").trim()}function O(e,t="and"){let r=e.slice(0,5).map(Ce),n=e.length-r.length;return n>0?`${r.join(", ")} ${t} ${n} more`:r.length<2?r.join(""):`${r.slice(0,-1).join(", ")} ${t} ${r[r.length-1]}`}function W(e,t,r,n){if(e==="clear")return"Selection cleared.";if(!t)return"Which object?";if(t.confidence==="high")return e==="remove"?r.length?`Removed ${O(t.names)}. ${r.length} selected.`:`Removed ${O(t.names)}. Nothing is selected.`:e==="add"?`Added ${O(t.names)}. ${r.length} selected.`:t.names.length===1?`Selected ${Ce(t.names[0])}.`:`Selected ${t.names.length}: ${O(t.names)}.`;if(t.confidence==="ambiguous")return t.reason==="count_mismatch"?`I found ${t.candidates.length}: ${O(t.candidates)}. Which ones?`:`Which one: ${O(t.candidates,"or")}?`;let i=String(n).replace(/\s+/g," ").trim().slice(0,80);return t.reason==="relation"?`I can't tell which one "${i}" is yet. Try its name or color, or tap it.`:t.reason==="empty"?"Which object?":`I couldn't find "${i}".`}async function se(e,t,r={}){let n=r.mode??"replace",i=String(t??"").slice(0,D);if(n==="clear")return j(e,[],{mode:"clear",source:"voice",query:i||"clear"}),{mode:n,query:i,match:null,applied:!0,tier:1,meshes:[],say:W(n,null,[],i)};let o=n==="remove"?A(e):null;if(o&&!o.length)return{mode:n,query:i,match:null,applied:!1,tier:1,meshes:[],say:"Nothing is selected."};let a=it(e,i,{within:o}),s=1,l="",c=r.provider===void 0?e.__tlSelectProvider?.()??null:r.provider;if(c&&i.trim()&&a.confidence==="none"&&(a.reason==="relation"||a.reason==="no_match"))try{let h=await st(e,i,c,{within:o,signal:r.signal});s=2,l=h.question,a=h.confidence==="high"?{ids:[],names:h.names,confidence:"high",candidates:[],reason:"model"}:{ids:[],names:[],confidence:h.names.length?"ambiguous":"none",candidates:h.names.slice(0,10),reason:"model"}}catch(h){if(r.signal?.aborted)throw h}let u=!1;a.confidence==="high"&&a.names.length&&(j(e,a.names,{mode:n,source:"voice",query:i}),u=!0);let d=A(e),m=!u&&l?l:W(n,a,d,i);return{mode:n,query:i,match:a,applied:u,tier:s,meshes:d,say:m}}function Ht(e,t,r="replace"){let n=new Set(Y(e).map(l=>l.name)),i=t.filter(l=>typeof l=="string"&&n.has(l)).slice(0,50),o=t.filter(l=>typeof l=="string").join(", ").slice(0,200);if(!i.length){let l={ids:[],names:[],confidence:"none",candidates:[],reason:"no_match"},c=A(e);return{mode:r,query:o,match:l,applied:!1,tier:1,meshes:c,say:W(r,l,c,o)}}j(e,i,{mode:r,source:"voice",query:o});let a={ids:[],names:i,confidence:"high",candidates:[],reason:"exact"},s=A(e);return{mode:r,query:o,match:a,applied:!0,tier:1,meshes:s,say:W(r,a,s,o)}}function Re(e){return e.applied?"Done.":`Sorry, I couldn't apply that: ${String(e.error??"the edit was rejected").replace(/\s+/g," ").slice(0,160)}`}async function rn(e,t,r){if(typeof e.editScene!="function")return;_(e,{state:"editing",transcript:t,isFinal:!0});let n;try{n=await e.editScene(t)}catch(i){n={applied:!1,code:"",repairs:0,warnings:[],error:i?.message||String(i)}}_(e,{state:"idle",transcript:t,isFinal:!0,result:n}),r.speak!==!1&&ae(Re(n),r.lang)}async function Le(e,t={}){if(typeof e.editScene!="function")throw new Error("startVoiceEdit needs three-live-ai.min.js (el.editScene).");let r=t.recognizer??Vt();if(!r)throw new Error("startVoiceEdit: this browser has no speech recognition; use el.connectVoice() (ai-realtime or gemini-live) instead.");_(e,{state:"listening"});let n="";try{n=(await r.listen({lang:t.lang,signal:t.signal,onPartial:l=>_(e,{state:"listening",transcript:l,isFinal:!1})})).trim().slice(0,D)}catch(l){throw _(e,{state:"error",error:l?.message||String(l)}),l}if(!n){let l="I didn't catch that.";throw _(e,{state:"idle",transcript:n,isFinal:!0,error:l}),t.speak!==!1&&ae(l,t.lang),new Error(`startVoiceEdit: ${l}`)}let i=n,o,a=t.talkToSelect===!1?null:rt(n);if(a&&a.kind!=="edit"){_(e,{state:"thinking",transcript:n,isFinal:!0});try{o=await se(e,a.kind==="clear"?"":a.query,{mode:a.mode,signal:t.signal})}catch(d){throw _(e,{state:"error",error:d?.message||String(d)}),d}let l=a.kind==="mixed"?a.instruction:void 0,c=l!==void 0&&o.applied&&o.tier===1,u=t.chips!==!1&&!e.__tlVoiceSession;if(u){let d=o;Dt(e,d.applied||d.mode==="clear"?{pending:c?void 0:l}:d.match?.candidates.length?{question:d.say,candidates:d.match.candidates,mode:d.mode,query:d.query,pending:l}:{},m=>rn(e,m,t))}if(!c){if(_(e,{state:"idle",transcript:n,isFinal:!0,selection:o}),t.speak!==!1){let d=l!==void 0&&o.applied?u&&e.__tlChips?" Tap Apply to make the change.":" Say the change again to apply it.":"";ae(o.say+d,t.lang)}return{transcript:n,result:null,selection:o}}i=l}else t.chips!==!1&&e.__tlChips&&ie(e);_(e,{state:"editing",transcript:n,isFinal:!0,...o?{selection:o}:{}});let s;try{s=await e.editScene(i,{signal:t.signal})}catch(l){s={applied:!1,code:"",repairs:0,warnings:[],error:l?.message||String(l)}}if(_(e,{state:"idle",transcript:n,isFinal:!0,result:s,...o?{selection:o}:{}}),t.speak!==!1){let l=Re(s);ae(o?`${o.say} ${l}`:l,t.lang)}return{transcript:n,result:s,...o?{selection:o}:{}}}function Ie(e,t){if(!t){e.__tlTapHandler&&e.removeEventListener?.("meshclick",e.__tlTapHandler),e.__tlTapDown&&e.removeEventListener?.("pointerdown",e.__tlTapDown),e.__tlTapHandler=null,e.__tlTapDown=null;return}if(e.__tlTapHandler)return;let r=!1,n=0,i=o=>{let a=o;r=!!(a.shiftKey||a.ctrlKey||a.metaKey),n=Date.now()};e.__tlTapDown=i,e.addEventListener?.("pointerdown",i),e.__tlTapHandler=o=>{let a=o.detail?.meshName;if(typeof a!="string"||!a)return;let s=n>0&&Date.now()-n>=Wr,l=A(e);r||s?j(e,[a],{mode:"toggle",source:"tap"}):l.length===1&&l[0]===a?j(e,[],{mode:"clear",source:"tap"}):j(e,[a],{mode:"replace",source:"tap"}),r=!1,n=0},e.addEventListener?.("meshclick",e.__tlTapHandler)}var nn='<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true" fill="currentColor"><path d="M12 14a3 3 0 0 0 3-3V5a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3zm5-3a5 5 0 0 1-10 0H5a7 7 0 0 0 6 6.92V21h2v-3.08A7 7 0 0 0 19 11h-2z"/></svg>';function on(e){if(e.__tlVoiceButton||typeof document>"u")return;let t=e.querySelector?.('[slot="voice-button"]'),r=t??document.createElement("button");t||(r.setAttribute("type","button"),r.setAttribute("aria-label","Edit the scene by voice"),r.innerHTML=nn,Object.assign(r.style,{position:"absolute",left:"12px",bottom:"12px",width:"44px",height:"44px",borderRadius:"22px",border:"none",background:"rgba(32,33,36,0.85)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer",zIndex:"10",boxShadow:"0 1px 4px rgba(0,0,0,0.3)"}),e.appendChild?.(r)),e.__tlVoiceButton=r;let n=null,i=o=>{r.setAttribute("aria-pressed",o?"true":"false"),t||(r.style.background=o?"rgba(217,48,37,0.9)":"rgba(32,33,36,0.85)")};r.addEventListener("click",o=>{o.stopPropagation();let a=e.getAttribute?.("ai-voice-url");if(a){if(e.__tlVoiceSession){e.__tlVoiceSession.close(),i(!1);return}i(!0);let s=e.getAttribute?.("ai-voice-provider")||"ai-realtime";je(e,{provider:s,url:a,model:e.getAttribute?.("ai-voice-model")||void 0}).then(l=>{e.addEventListener?.("three-live-voice",function c(u){u.detail?.state==="closed"&&(e.removeEventListener?.("three-live-voice",c),e.__tlVoiceSession!==l&&i(!1))})}).catch(()=>i(!1));return}if(n){n.abort(new Error("voice edit cancelled"));return}n=new AbortController,i(!0),Le(e,{signal:n.signal}).catch(()=>{}).finally(()=>{n=null,i(!1)})})}var Jt=1048576,Gt=20,Yt=15e3,an=["You edit Three.js scene scripts for Three-Live, which compiles them to glTF for Web, Android, and iOS.","Reply with the COMPLETE updated script in a single ```js code block and nothing else.","Rules:","- Use the pre-defined globals `THREE`, `scene`, and `camera`. No imports, renderer, loaders, DOM, network, or timers.","- Change only what the instruction asks; keep every other object, name, and value as it is.","- Give new meshes and groups descriptive `.name` values; keep existing names stable.","- Keep objects resting on y = 0 and within view of the camera.","- Supported: core geometries and materials (MeshStandardMaterial / MeshPhysicalMaterial with PBR maps, and MeshStandardNodeMaterial with loop-free THREE.TSL colorNode / emissiveNode / roughnessNode / metalnessNode / opacityNode / bumpNode / positionNode), lights, Group, InstancedMesh, THREE.CSG, THREE.CanvasTexture, THREE.ProceduralTexture presets (wood, marble, brick, concrete, brushedMetal, rust, leather, fabric, carbonFiber, checkerboard, grid), and declarative mesh.userData.xr traits. Optional per-frame motion: function animate(t) { ... }.","- Keep every userData.xr.material preset; add a prompt (AI texture) only when the instruction asks for a specific surface."].join(`
|
|
34
|
+
`);function sn(e){try{let t=new URL(e);return t.protocol==="https:"?!0:t.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(t.hostname)}catch{return!1}}function Zt(e){let t=String(e?.endpoint??"");if(!sn(t))throw new Error("ai-chat: endpoint must be an https:// URL (or http:// on localhost).");let r=e.fetch??globalThis.fetch?.bind(globalThis);if(typeof r!="function")throw new Error("ai-chat: no fetch() implementation is available.");let n=Number.isFinite(e.temperature)?Number(e.temperature):.2,i=Math.max(1e3,Number(e.timeoutMs)||12e4);return{async complete({messages:o,signal:a}){let s=new AbortController,l=()=>s.abort(a?.reason);a?.aborted&&l(),a?.addEventListener("abort",l,{once:!0});let c=setTimeout(()=>s.abort(new Error(`ai-chat: timed out after ${i} ms`)),i);try{let u={messages:o,temperature:n,stream:!1};e.model&&(u.model=e.model);let d=await r(t,{method:"POST",headers:{"Content-Type":"application/json",...e.headers},body:JSON.stringify(u),signal:s.signal}),m=await d.text();if(!d.ok)throw new Error(`ai-chat: HTTP ${d.status} from endpoint: ${m.slice(0,300)}`);let h;try{h=JSON.parse(m)}catch{throw new Error("ai-chat: endpoint returned non-JSON content.")}let p=h?.choices?.[0]?.message?.content,y=Array.isArray(p)?p.map(v=>typeof v=="string"?v:String(v?.text??"")).join(""):p;if(typeof y!="string"||!y.trim())throw new Error("ai-chat: response had no message content.");return y}finally{clearTimeout(c),a?.removeEventListener("abort",l)}}}}function Fe(e){let t=String(e??""),r=/```[ \t]*([A-Za-z0-9_+-]*)[^\n]*\n([\s\S]*?)```/g,n,i="";for(;n=r.exec(t);){let a=n[1].toLowerCase();if(i||(i=n[2]),["js","javascript","ts","typescript","mjs"].includes(a))return n[2].trim()}if(i)return i.trim();let o=/```[^\n]*\n([\s\S]*)$/.exec(t);return o?o[1].trim():/\bscene\b/.test(t)?t.trim():""}function Kt(e){let t=new Set,r=e?.gltf;if(!r&&e instanceof ArrayBuffer&&e.byteLength>=20){let o=new DataView(e);if(o.getUint32(0,!0)===1179937895){let a=o.getUint32(12,!0);try{r=JSON.parse(new TextDecoder().decode(new Uint8Array(e,20,a)))}catch{}}}let n=0,i=0;for(let o of r?.nodes??[]){let a=typeof o?.name=="string"?o.name.trim():"",s=/^Node_\d+$/.test(a),l=a.length>0&&!s;l&&t.add(a),o?.mesh!==void 0&&n++,!l&&(o?.extras?.xr!=null||o?.extras?.interactivity!=null)&&i++}return{meshNames:t,meshes:n,unnamedXrNodes:i}}function Xt(e,t,r,n="editScene"){let i=F().scriptRunner;if(!i||typeof i.execute!="function")return Promise.reject(new Error(`${n} needs a script runner (load three-live.min.js or three-live-full.min.js).`));let o=[];return new Promise((a,s)=>{let l,c=!1,u=h=>{if(!c){c=!0,clearTimeout(m),r?.removeEventListener("abort",d);try{l?.terminate?.()}catch{}h()}},d=()=>u(()=>s(r?.reason??new Error("editScene aborted"))),m=setTimeout(()=>u(()=>s(new Error(`Scene did not finish compiling within ${Yt} ms.`))),Yt);if(r?.aborted)return d();r?.addEventListener("abort",d,{once:!0}),Promise.resolve(i.execute(t,e.sandbox??"worker",{onModelReady:h=>u(()=>a({model:h,warnings:o})),onError:h=>u(()=>s(h instanceof Error?h:new Error(String(h)))),onToast:h=>{o.length<20&&o.push(String(h))}},{allowClientScripts:!0})).then(h=>{if(l=h,c)try{h?.terminate?.()}catch{}},h=>u(()=>s(h instanceof Error?h:new Error(String(h)))))})}var ln=50;function cn(e){let t=typeof e=="string"?[e]:Array.isArray(e)?e:[],r=[];for(let n of t){let i=typeof n=="string"?n.slice(0,200):"";if(i&&!r.includes(i)&&r.push(i),r.length>=ln)break}return r}function un(e){return e.length===0?"":e.length===1?`The user selected the mesh named "${e[0]}"; the instruction refers to it.
|
|
35
|
+
`:`The user selected the meshes named ${e.map(t=>`"${t}"`).join(", ")}; the instruction refers to them.
|
|
36
|
+
`}function Ne(e,t,r="editScene"){let n=t.provider??e.aiProvider;if(n&&typeof n.complete=="function")return n;let i=e.getAttribute?.("ai-endpoint");if(!i)throw new Error(`${r}: no AI provider. Set el.aiProvider, or the ai-endpoint (and ai-model) attributes.`);let o=(e.getAttribute?.("ai-provider")||"ai-chat").toLowerCase();if(o!=="ai-chat")throw new Error(`${r}: unknown ai-provider '${o}' (supported: ai-chat, or set el.aiProvider).`);return Zt({endpoint:i,model:e.getAttribute?.("ai-model")||void 0})}function Pe(e){return typeof e.__tlAiSource=="string"?e.__tlAiSource:e.querySelector?.('script[type="text/three"], script[type="text/three-live"]')?.textContent??""}async function Qt(e,t,r={}){let n=String(t??"").trim();if(!n)throw new Error("editScene: instruction is empty.");if(!e?.allowClientScripts)throw new Error("editScene requires client scripts: add the allow-client-scripts attribute.");let i=r.source??Pe(e);if(!i.trim())throw new Error('editScene needs a Three.js scene script (inline <script type="text/three"> or a prior editScene/executeScript).');let o=Ne(e,r);e.__tlAiAbort?.abort(new Error("editScene superseded by a newer call"));let a=new AbortController;e.__tlAiAbort=a;let s=()=>a.abort(r.signal?.reason);r.signal?.aborted&&s(),r.signal?.addEventListener("abort",s,{once:!0});let l=a.signal,c=cn(r.selection===void 0?e.selectedMeshes??e.selectedMesh:r.selection),u=c.length>1?c:c[0]??null,d=Math.max(0,Math.min(3,Math.floor(Number(r.maxRepairs??1))||0)),m=[{role:"system",content:an},{role:"user",content:`Current scene script:
|
|
37
|
+
\`\`\`js
|
|
38
|
+
${i}
|
|
39
|
+
\`\`\`
|
|
40
|
+
`+un(c)+`Instruction: ${n}`}];e.fireEvent?.("three-live-edit-start",{instruction:n,selection:u});let h="",p="",y=[],v=0;try{for(let g=0;g<=d;g++){let x=await o.complete({messages:m,signal:l});if(l.aborted)throw l.reason;if(h=Fe(x),!h)p="The model reply did not contain a scene script.";else if(new TextEncoder().encode(h).length>Jt)p="The generated script exceeds the 1 MB script limit.";else try{let w=await Xt(e,h,l);y=w.warnings;let b=Kt(w.model);if(b.meshes===0)p="The edited scene contains no meshes.";else if(c.some(k=>!b.meshNames.has(k))&&!/\b(remove|delete|rename)\b/i.test(n)){let k=c.filter(T=>!b.meshNames.has(T));p=k.length===1?`The edited scene no longer contains the selected mesh "${k[0]}".`:`The edited scene no longer contains the selected meshes ${k.map(T=>`"${T}"`).join(", ")}.`}else p=""}catch(w){if(l.aborted)throw l.reason;let b=w,k=String(b?.message||w);p=b?.name&&b.name!=="Error"&&!k.startsWith(b.name)?`${b.name}: ${k}`:k}if(!p)break;g<d&&(v++,m.push({role:"assistant",content:x},{role:"user",content:`That script was rejected by the Three-Live sandbox: ${p}
|
|
41
|
+
Return the complete corrected script in a single \`\`\`js code block.`}))}}finally{r.signal?.removeEventListener("abort",s),e.__tlAiAbort===a&&(e.__tlAiAbort=null)}if(!p&&!r.dryRun)try{await e.executeScript(h);let g=e.__tlAiHistory??(e.__tlAiHistory=[]);g.push(i),g.length>Gt&&g.shift(),e.__tlAiSource=h}catch(g){p=`The edit compiled but failed to load: ${g?.message||String(g)}`,e.__tlAiSource=i,e.executeScript(i).catch(()=>{})}let f={applied:!p,code:h,repairs:v,warnings:y,...p?{error:p}:{}};return e.fireEvent?.("three-live-edit",{instruction:n,...f}),f}async function er(e){let t=e?.__tlAiHistory?.pop();return typeof t!="string"?!1:(e.__tlAiSource=t,await e.executeScript(t),!0)}var Ue={name:"ai",onRegister(){typeof document>"u"||document.querySelectorAll?.("three-live, xr-live").forEach(e=>Ue.onElementConnected?.(e))},onElementConnected(e){let t=e;if(!t||t.__tlAiInstalled)return;t.__tlAiInstalled=!0,t.editScene=(a,s)=>Qt(t,a,s),t.compileFromImage=(a,s)=>gt(t,a,s),t.undoEdit=()=>er(t),yr(t);let r=t;t.startVoiceEdit=a=>Le(r,a),r.__tlSelectProvider=()=>{try{return Ne(t,{})}catch{return null}},t.selectByVoice=(a,s)=>se(r,a,s),t.connectVoice=a=>je(r,a),t.setSelectOnTap=a=>Ie(r,a),t.hasAttribute?.("ai-select-on-tap")&&Ie(r,!0),t.hasAttribute?.("ai-voice-button")&&on(r),"sceneSource"in t||Object.defineProperty(t,"sceneSource",{get:()=>Pe(t),configurable:!0});let n=t;zr(n);let i=t.executeScript;typeof i=="function"&&(t.executeScript=a=>(Nt(n,{kind:"script",value:String(a)}),$t(n,()=>i.call(t,a)).then(s=>(t.__tlAiSource=String(a),Ft(n,s),s))));let o=n.loadScriptFromSrc;typeof o=="function"&&(n.loadScriptFromSrc=a=>(Nt(n,{kind:"url",value:String(a)}),$t(n,()=>o.call(t,a)).then(s=>(Ft(n,s),s))))}};tr(Ue);export{Wt as AIVoiceSession,ct as IMAGE_SYSTEM_PROMPT,wt as TEXTURE_PROMPT_SUFFIX,Ue as aiPlugin,ee as browserImageCodec,pt as buildImageMessages,dt as buildImageUserText,de as buildSelectIndex,gt as compileFromImage,je as connectVoice,Zt as createAIChatProvider,St as createAIImageProvider,Vt as createWebSpeechRecognizer,Re as describeEditResult,we as describeSelection,W as describeVoiceSelection,ft as detectImageMime,Qt as editScene,Fe as extractScript,Se as formatSpatialPriors,zt as geminiLiveAdapter,A as getSelection,ie as hideSelectionChips,mt as imageRepairMessage,At as indexedDbTextureCache,C as isAllowedImageUrl,Xe as matchSelection,ke as normalizeImageInput,at as parseSelectReply,Et as parseTexturePack,rt as parseVoiceCommand,_t as prepareAlbedo,qt as realtimeAdapter,it as resolveSelection,st as resolveSelectionWithModel,Y as sceneSelectEntries,se as selectByVoice,xe as selectCamera,ot as selectMessages,Ht as selectNamesByVoice,Tt as serializeTexturePack,Ie as setSelectOnTap,j as setSelection,Dt as showSelectionChips,Ce as speakableName,Le as startVoiceEdit,Te as textureRequests,er as undoEdit};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/var ThreeLiveAI=(()=>{var le=Object.defineProperty,nr=Object.getOwnPropertyDescriptor,ir=Object.getOwnPropertyNames,or=Object.prototype.hasOwnProperty,ar=(e,t)=>{for(var r in t)le(e,r,{get:t[r],enumerable:!0})},sr=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of ir(t))!or.call(e,i)&&i!==r&&le(e,i,{get:()=>t[i],enumerable:!(n=nr(t,i))||n.enumerable});return e},lr=e=>sr(le({},"__esModule",{value:!0}),e),De={};ar(De,{AIVoiceSession:()=>Ht,IMAGE_SYSTEM_PROMPT:()=>ht,TEXTURE_PROMPT_SUFFIX:()=>xt,aiPlugin:()=>Oe,browserImageCodec:()=>ee,buildImageMessages:()=>ft,buildImageUserText:()=>mt,buildSelectIndex:()=>pe,compileFromImage:()=>bt,connectVoice:()=>Ce,createAIChatProvider:()=>Xt,createAIImageProvider:()=>_t,createWebSpeechRecognizer:()=>Jt,describeEditResult:()=>Re,describeSelection:()=>ve,describeVoiceSelection:()=>W,detectImageMime:()=>yt,editScene:()=>tr,extractScript:()=>Ne,formatSpatialPriors:()=>ke,geminiLiveAdapter:()=>Wt,getSelection:()=>A,hideSelectionChips:()=>ie,imageRepairMessage:()=>gt,indexedDbTextureCache:()=>$t,isAllowedImageUrl:()=>C,matchSelection:()=>et,normalizeImageInput:()=>_e,parseSelectReply:()=>lt,parseTexturePack:()=>At,parseVoiceCommand:()=>it,prepareAlbedo:()=>Tt,realtimeAdapter:()=>Bt,resolveSelection:()=>at,resolveSelectionWithModel:()=>ct,sceneSelectEntries:()=>Y,selectByVoice:()=>se,selectCamera:()=>Se,selectMessages:()=>st,selectNamesByVoice:()=>Gt,serializeTexturePack:()=>Mt,setSelectOnTap:()=>Pe,setSelection:()=>j,showSelectionChips:()=>qt,speakableName:()=>Le,startVoiceEdit:()=>Ie,textureRequests:()=>Ae,undoEdit:()=>rr});var V="__THREE_LIVE_REGISTRY__";function P(){let e=typeof globalThis<"u"?globalThis:{};if(!e[V]){let t={plugins:[],decoders:new Map};try{Object.defineProperty(e,V,{value:t,writable:!1,configurable:!1,enumerable:!1})}catch{e[V]=t}}return e[V]}var wn=P().plugins;function cr(e){let t=P().plugins;t.some(r=>r.name===e.name)||t.push(e),e.onRegister?.()}var qe={eye:[0,1.6,5],target:[0,.8,0]},ze=new Set("the a an please can could would you will i want wanna to me my that this those these of one ones object objects thing things item items mesh meshes piece pieces part parts just only also too is are there here which select pick choose highlight grab focus on in at by some most side hand them it its and s now again as well other others".split(" ")),ur=["sofa couch settee","lamp light lantern","door gate doorway","table desk","chair seat stool","rug carpet mat","cup mug","tv television screen monitor","car vehicle automobile","box crate cube","ball sphere orb","floor ground","plant tree bush","cabinet cupboard closet"].map(e=>e.split(" ")),H=[["red",[220,40,40],"red"],["orange",[245,140,30],"orange"],["yellow",[240,220,40],"yellow"],["gold",[212,175,55],"yellow"],["green",[50,170,60],"green"],["teal",[0,128,128],"green"],["cyan",[40,200,220],"blue"],["blue",[40,80,220],"blue"],["navy",[20,30,100],"blue"],["purple",[130,50,180],"purple"],["magenta",[220,40,200],"pink"],["pink",[240,120,180],"pink"],["brown",[130,80,40],"brown"],["beige",[225,205,160],"brown"],["white",[245,245,245],"white"],["silver",[192,192,192],"gray"],["gray",[128,128,128],"gray"],["black",[20,20,20],"black"]],Be={grey:"gray",golden:"gold",crimson:"red",maroon:"red",violet:"purple",aqua:"cyan",turquoise:"cyan",tan:"beige"},ce={};function $(e,t){for(let r of e.split(" "))ce[r]=t}$("left leftmost",["right",!1]),$("right rightmost",["right",!0]),$("front nearest closest frontmost",["depth",!1]),$("back farthest furthest rear far backmost",["depth",!0]),$("top highest upper topmost",["up",!0]),$("bottom lowest lower bottommost",["up",!1]),$("biggest largest big large huge bigger larger",["size",!0]),$("smallest small tiny little smaller",["size",!1]),$("tallest tall taller",["height",!0]),$("shortest short shorter",["height",!1]),$("middle center centre central","middle");var We={first:0,second:1,third:2,fourth:3,fifth:4,last:-1},Ve={two:2,pair:2,three:3,four:4,five:5,six:6,seven:7,eight:8,nine:9,ten:10},He=new Set(["all","every","everything","each","everybody"]),ue=e=>/^[0-9]+$/.test(e),J=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),he=e=>J(ce,e)?ce[e]:null,Je=e=>J(We,e)?We[e]:null;function Ge(e){let t;if(J(Ve,e))t=Ve[e];else if(/^[0-9]{1,3}$/.test(e))t=Number(e);else return null;return t>=1&&t<=50?t:null}function Ye(e){return J(Be,e)?Be[e]:H.some(t=>t[0]===e)?e:null}function hr(e){for(let t=0;t<e.length;t++){let r=e[t],n=e[t+1]??"";if(/^(near|nearby|beside|behind|under|underneath|beneath|above|below|between|inside|within|against|with|beyond|around)$/.test(r)||(r==="next"||r==="close")&&n==="to"||(r==="front"||r==="top")&&n==="of")return!0;if(r==="on"||r==="by"){let i=t+1;for(;i<e.length&&/^(the|a|an|its|my)$/.test(e[i]);)i++;let o=e[i];if(o!==void 0&&!he(o)&&o!=="side"&&o!=="end")return!0}}return!1}function dr(e){return String(e??"").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)}function Ze(e){let t=e.length;if(t>4&&e.endsWith("ies"))return e.slice(0,-3)+"y";if(t>4&&e.endsWith("ves"))return e.slice(0,-3)+"f";for(let r of["ches","shes","xes","sses","zes"])if(t>r.length+1&&e.endsWith(r))return e.slice(0,-2);return t>3&&e.endsWith("s")&&!e.endsWith("ss")&&!e.endsWith("us")?e.slice(0,-1):e}function pr(e){let t=[],r="",n="",i=s=>s>="a"&&s<="z",o=s=>s>="A"&&s<="Z",a=s=>s>="0"&&s<="9";for(let s of String(e)){if(!(i(s)||o(s)||a(s))){r&&t.push(r),r="",n="";continue}n&&r&&(i(n)&&o(s)||a(n)!==a(s))&&(t.push(r),r=""),r+=s.toLowerCase(),n=s}return r&&t.push(r),t.map(Ze)}var mr=e=>e.replace(/[^A-Za-z0-9]/g,"").toLowerCase(),fr=(e,t)=>ur.some(r=>r.includes(e)&&r.includes(t));function Ke(e,t){return e===t||fr(e,t)||t.length>=4&&e.length>=4&&(e.startsWith(t)||t.startsWith(e))}function de(e){let t=Math.min(1,Math.max(0,Number.isFinite(e)?e:0));return(t<=.0031308?12.92*t:1.055*Math.pow(t,.4166666666666667)-.055)*255}function gr(e){let t=[de(e[0]),de(e[1]),de(e[2])],r=H[0][0],n=1/0;for(let[i,o]of H){let a=(t[0]-o[0])**2+(t[1]-o[1])**2+(t[2]-o[2])**2;a<n&&(n=a,r=i)}return r}var yr=e=>H.find(t=>t[0]===e)?.[2]??"";function br(e,t){return e.words.includes(t)?!0:e.colorName!=null&&(e.colorName===t||yr(e.colorName)===t)}function pe(e){return e.map((t,r)=>{let n=[0,1,2].map(i=>Math.abs(t.max[i]-t.min[i]));return{id:r,name:t.name,words:pr(t.name),colorName:t.color?gr(t.color):null,size:Math.max(n[0],.01)*Math.max(n[1],.01)*Math.max(n[2],.01),height:n[1],center:[0,1,2].map(i=>(t.min[i]+t.max[i])/2)}})}function wr(e){let t=[0,1,2].map(a=>e.target[a]-e.eye[a]),r=Math.hypot(t[0],t[1],t[2]),n=r<1e-9?[0,0,-1]:t.map(a=>a/r),i=[-n[2],0,n[0]],o=Math.hypot(i[0],i[1],i[2]);return{eye:e.eye,right:o<1e-9?[1,0,0]:i.map(a=>a/o)}}function N(e,t,r){let n=[0,1,2].map(i=>e.center[i]-r.eye[i]);switch(t){case"right":return n[0]*r.right[0]+n[1]*r.right[1]+n[2]*r.right[2];case"depth":return Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);case"up":return e.center[1];case"size":return e.size;default:return e.height}}var Xe=e=>e.length?e.reduce((t,r)=>t+r,0)/e.length:0;function me(e,t,r){let n;if(t==="middle"){if(e.length<3)return e;let i=e.map(s=>N(s,"right",r)),o=Math.min(...i),a=Math.max(...i);n=e.filter((s,l)=>i[l]>o+1e-6&&i[l]<a-1e-6)}else{let i=e.map(a=>N(a,t[0],r)),o=Xe(i);n=e.filter((a,s)=>t[1]?i[s]>o+1e-6:i[s]<o-1e-6)}return n.length?n:e}function Qe(e,t,r){let n;if(t==="middle"){let i=Xe(e.map(o=>N(o,"right",r)));n=e.map(o=>[o,Math.abs(N(o,"right",r)-i)])}else n=e.map(i=>[i,t[1]?-N(i,t[0],r):N(i,t[0],r)]);return n.sort((i,o)=>i[1]-o[1])}function E(e,t,r){let n=[...e].sort((a,s)=>a.id-s.id),i=[...new Set(n.map(a=>a.id))],o=[];for(let a of n)!o.includes(a.name)&&o.length<50&&o.push(a.name);return t==="high"?{ids:i,names:o,confidence:t,candidates:[],reason:r}:t==="ambiguous"?{ids:[],names:[],confidence:t,candidates:o.slice(0,10),reason:r}:{ids:[],names:[],confidence:t,candidates:[],reason:r}}var I=e=>E([],"none",e);function et(e,t,r=qe,n){let i=n?e.filter(a=>n.includes(a.name)):[...e],o=dr(t);for(let a=0;a<o.length;a++){let s=o[a],l=o[a+1]??"",c=s==="except"||s==="excluding";if(!(c||s==="but"&&l==="not"||s==="other"&&l==="than"))continue;let u=fe(i,o.slice(0,a),r);if(u.confidence!=="high")return u;let d=i.filter(p=>u.ids.includes(p.id)),m=fe(d,o.slice(a+(c?1:2)),r);if(m.confidence!=="high")return E(d,"ambiguous","except");let h=d.filter(p=>!m.ids.includes(p.id));return h.length?E(h,"high","match"):I("no_match")}return fe(i,o,r)}function vr(e){return!(ze.has(e)||He.has(e)||e==="both"||Ge(e)!==null||Je(e)!==null||Ye(e)!==null||he(e)!==null||ue(e))}function fe(e,t,r){let n=[[]];for(let l of t)l==="and"?n.push([]):n[n.length-1].push(l);let i=n.filter(l=>l.length);if(i.length<2||!i.every(l=>l.some(vr)))return tt(e,t,r);let o=[],a=!1;for(let l of i){let c=tt(e,l,r);if(c.confidence==="none")return c;c.confidence==="ambiguous"?(a=!0,o.push(...e.filter(u=>c.candidates.includes(u.name)))):o.push(...e.filter(u=>c.ids.includes(u.id)))}let s=[...new Map(o.map(l=>[l.id,l])).values()];return a?E(s,"ambiguous","multiple"):E(s,"high","match")}function tt(e,t,r){if(!e.length)return I("no_match");let n=0;for(;n<t.length&&/^(select|pick|choose|highlight|grab|the|a|an|just|only)$/.test(t[n]);)n++;let i=t.slice(n).join("");if(i){let f=e.filter(g=>mr(g.name)===i);if(f.length)return E(f,"high","exact")}if(hr(t))return I("relation");let o=[],a=!1,s=!1,l=null,c=null,u=[],d=[],m=!1;for(let f of t){if(m&&ue(f)){o.push(f);continue}m=!1;let g,x,w;if(He.has(f))s=!0,a=!0;else if(f==="both")l=2,a=!0;else if((g=Ge(f))!==null)l=g,a=a||g>1;else if((g=Je(f))!==null)c=g;else if((x=Ye(f))!==null)u.push(x);else if((w=he(f))!==null)d.push(w);else if(!ze.has(f)){let b=Ze(f);b!==f&&(a=!0),o.push(b),m=!0}}if(!o.length&&!u.length&&!d.length&&c===null&&!s&&l===null)return I("empty");let h=wr(r),p=e;if(o.length){let f=p.map(b=>({e:b,n:o.filter(k=>b.words.some(T=>Ke(T,k))).length})),g=Math.max(0,...f.map(b=>b.n));if(g===0)return I("no_match");p=f.filter(b=>b.n===g).map(b=>b.e);let x=b=>b.words.filter(k=>!ue(k)&&!o.some(T=>Ke(k,T))).length,w=Math.min(...p.map(x));p=p.filter(b=>x(b)===w)}if(u.length&&(p=p.filter(f=>u.some(g=>br(f,g)))),!p.length)return I("no_match");if(c!==null){for(let w of d)p=me(p,w,h);let f=Qe(p,["right",!1],h),g=Math.min(l??1,f.length),x;if(c<0)x=f.slice(f.length-g);else{let w=c*(l!==null?g:1);if(w>=f.length)return I("no_match");x=f.slice(w,w+g)}return E(x.map(w=>w[0]),"high","match")}let y=!a&&l===null,v=y?1:s?null:l;if(v!==null&&d.length){for(let x of d.slice(0,-1))p=me(p,x,h);let f=Qe(p,d[d.length-1],h),g=v;if(f.length<g)return E(f.map(x=>x[0]),"ambiguous","count_mismatch");if(f.length>g&&Math.abs(f[g][1]-f[g-1][1])<=1e-6){let x=f[g-1][1];return E(f.filter(w=>Math.abs(w[1]-x)<=1e-6).map(w=>w[0]),"ambiguous","multiple")}return E(f.slice(0,g).map(x=>x[0]),"high","match")}for(let f of d)p=me(p,f,h);return y?p.length===1?E(p,"high","match"):E(p,"ambiguous","multiple"):l!==null&&p.length!==l?E(p,"ambiguous","count_mismatch"):E(p,"high","match")}var rt="make paint color colour turn move rotate scale resize delete remove change set add put give raise lower shrink enlarge hide show swap replace spin flip double halve duplicate copy animate brighten darken".split(" "),G=(e,t)=>{for(let r of t)if(e.startsWith(r))return e.slice(r.length).trim()||null;return null},ge=(e,t)=>{for(let r of t)if(e.endsWith(r))return e.slice(0,-r.length).trim()||null;return null},ye=e=>e.replace(/[.!?,]+$/,"");function xr(e){let t=e.split(" ");for(let r=0;r<t.length;r++){let n=t[r].endsWith(","),i=r>0&&(t[r]==="and"||t[r]==="then");if(!(n||i))continue;let o=r+1;for(;o<t.length&&(t[o]==="then"||t[o]==="and");)o++;if(o>=t.length||!rt.includes(t[o]))continue;let a=(i?t.slice(0,r):t.slice(0,r+1)).join(" ").replace(/,+$/,"").trim();if(a)return[a,t.slice(o).join(" ")]}return[e,null]}function nt(e,t){let r=e.split(/\s+/).filter(Boolean);return r.length&&ye(r[r.length-1]).toLowerCase()==="please"&&r.pop(),ye(r.slice(Math.max(0,r.length-t)).join(" "))}function be(e,t,r){let[n,i]=xr(t),o=ge(n,[" too"," as well"," also"]);if(o!==null&&e==="replace"&&(e="add",n=o),i!==null){let a=i.split(/\s+/).filter(Boolean).length;return{kind:"mixed",mode:e,query:n,instruction:nt(r,a)}}return{kind:"select",mode:e,query:n}}var Sr=["please ","ok ","ok, ","okay ","okay, ","now ","now, ","hey ","um ","um, ","uh ","uh, ","so ","so, ","and "],kr=new Set(["clear selection","clear the selection","reset selection","reset the selection","select nothing","select none","deselect","deselect all","deselect everything","unselect","unselect all","unselect everything","clear","clear all"]),_r=["select only ","select just ","only select ","just select ","select ","pick ","choose ","highlight ","grab ","focus on "];function it(e){let t=String(e??"").trim(),r=t.toLowerCase().replace(/[\u2018\u2019]/g,"'").split(/\s+/).filter(Boolean).join(" ");for(r=ye(r);;){let i=r.length;for(let o of Sr)r.startsWith(o)&&(r=r.slice(o.length));if(r.endsWith(" please")&&(r=r.slice(0,-7).replace(/,+$/,"")),r.length===i)break}if(r=r.trim(),kr.has(r))return{kind:"clear",mode:"clear"};let n=G(r,["deselect ","unselect ","don't select ","do not select "]);if(n!==null)return be("remove",n,t);for(let i of["remove ","take "])if(r.startsWith(i)){let o=ge(r.slice(i.length),[" from the selection"," from selection"," out of the selection"]);if(o!==null)return{kind:"select",mode:"remove",query:o}}if(n=G(r,["also select ","also pick ","also choose ","also add "]),n!==null)return be("add",n,t);if(r.startsWith("add ")){let i=ge(r.slice(4),[" to the selection"," to selection"]);if(i!==null)return{kind:"select",mode:"add",query:i}}return n=G(r,_r),n!==null||(n=G(r,["just ","only "]),n!==null&&!rt.includes(n.split(" ")[0]))?be("replace",n,t):{kind:"edit",instruction:nt(t,1/0)}}var ot=50;function we(e){let t=[];for(let r of e){let n=typeof r=="string"?r.slice(0,200):"";if(n&&!t.includes(n)&&t.push(n),t.length>=ot)break}return t}function A(e){let t=e.__tlSelection??[],r=e.selectedMesh||null;return(t[0]??null)!==r?r?[r]:[]:[...t]}function j(e,t,r={}){let n=r.mode??"replace",i=A(e),o=we(typeof t=="string"?[t]:t??[]),a;switch(n){case"clear":a=[];break;case"add":a=we([...i,...o]);break;case"remove":a=i.filter(u=>!o.includes(u));break;case"toggle":a=we([...i.filter(u=>!o.includes(u)),...o.filter(u=>!i.includes(u))]);break;default:a=o}let s=a.filter(u=>!i.includes(u)),l=i.filter(u=>!a.includes(u));if(!s.length&&!l.length&&a.join(`
|
|
17
|
+
`)===i.join(`
|
|
18
|
+
`))return null;e.__tlSelection=a,e.selectedMesh=a[0]??null,e.viewer?.setSelectedMeshes?.(a);let c={mesh:a[0]??null,meshes:a,added:s,removed:l,source:r.source??"api",...r.query?{query:String(r.query).slice(0,2e3)}:{}};return e.fireEvent?.("three-live-select",c),c}function Er(e){!e||e.__tlSelectionInstalled||(e.__tlSelectionInstalled=!0,"selectedMeshes"in e||Object.defineProperty(e,"selectedMeshes",{get:()=>A(e),set:t=>{j(e,t??[],{mode:"replace",source:"api"})},configurable:!0}),e.addEventListener?.("three-load",()=>{let t=A(e);t.length>1&&e.viewer?.setSelectedMeshes?.(t)}))}function ve(e){if(!e.length)return"The user cleared the selection.";if(e.length===1)return`The user selected the mesh "${e[0]}". "This" or "it" now refers to it.`;let t=e.slice(0,20).map(n=>`"${n}"`),r=e.length>20?` and ${e.length-20} more`:"";return`The user selected ${e.length} meshes: ${t.join(", ")}${r}. "These", "them", or "it" refer to them.`}var xe=new WeakMap;function Tr(e){let t=e;if(xe.has(t))return xe.get(t);let r=[1/0,1/0,1/0],n=[-1/0,-1/0,-1/0];for(let o=0;o+2<e.length;o+=3)for(let a=0;a<3;a++){let s=e[o+a];s<r[a]&&(r[a]=s),s>n[a]&&(n[a]=s)}let i=Number.isFinite(r[0])?[r,n]:null;return xe.set(t,i),i}function Y(e){let t=e.viewer?.meshes??[],r=[];for(let n of t.slice(0,5e3)){if(!n?.name||n.visible===!1)continue;let i=n.transform,o=[1/0,1/0,1/0],a=[-1/0,-1/0,-1/0];for(let l of n.primitives??[]){let c=l.positions?Tr(l.positions):null;if(c)for(let u=0;u<8;u++){let d=c[u&1?1:0][0],m=c[u&2?1:0][1],h=c[u&4?1:0][2];for(let p=0;p<3;p++){let y=i?i[p]*d+i[4+p]*m+i[8+p]*h+i[12+p]:[d,m,h][p];y<o[p]&&(o[p]=y),y>a[p]&&(a[p]=y)}}}if(!Number.isFinite(o[0]))continue;let s=n.primitives?.[0]?.baseColor;r.push({name:n.name,min:o,max:a,color:s?[s[0],s[1],s[2]]:null})}return r}function Se(e){let t=e.viewer,r=t?.getCameraOrbit?.(),n=t?.getCameraTarget?.();if(!r||!n||![r.theta,r.phi,r.radius,n.x,n.y,n.z].every(Number.isFinite))return qe;let i=r.radius*Math.cos(r.phi);return{eye:[n.x+i*Math.sin(r.theta),n.y+r.radius*Math.sin(r.phi),n.z+i*Math.cos(r.theta)],target:[n.x,n.y,n.z]}}function at(e,t,r={}){let n=pe(Y(e));return et(n,t,Se(e),r.within??null)}var Ar=["You find the objects a user means in a 3D scene.",'Reply with JSON only: {"ids": [numbers], "confidence": "high" or "low", "question": "text"}.',"Use only ids from the objects list.",`Left and right are as seen from the camera: "right" is the camera's right direction.`,'If several objects fit equally, or none fits, set confidence to "low" and ask one short question that names the options.'].join(" "),F=e=>Math.round(e*100)/100;function st(e,t,r,n=[]){let i=[0,1,2].map(s=>r.target[s]-r.eye[s]),o=Math.hypot(i[2],i[0])||1,a={camera:{eye:r.eye.map(F),target:r.target.map(F),right:[F(-i[2]/o),0,F(i[0]/o)]},objects:e.slice(0,300).map(s=>({id:s.id,name:s.name,color:s.colorName,center:s.center.map(F),size:F(Math.cbrt(s.size))})),selected:n.slice(0,50),request:String(t).slice(0,500)};return[{role:"system",content:Ar},{role:"user",content:JSON.stringify(a)}]}function lt(e,t){let r=String(e??""),n=r.indexOf("{"),i=r.lastIndexOf("}"),o={};if(n>=0&&i>n)try{o=JSON.parse(r.slice(n,i+1))??{}}catch{}let a=[];for(let l of Array.isArray(o.ids)?o.ids:[]){let c=Number.isInteger(l)?t[l]:void 0;c&&!a.includes(c.name)&&a.length<ot&&a.push(c.name)}let s=(typeof o.question=="string"?o.question:"").replace(/[\u0000-\u001f\u007f]+/g," ").trim().slice(0,300);return{names:a,confidence:o.confidence==="high"&&a.length?"high":"low",question:s}}async function ct(e,t,r,n={}){let i=Y(e);n.within&&(i=i.filter(s=>n.within.includes(s.name)));let o=pe(i);if(!o.length)return{names:[],confidence:"low",question:""};let a=await r.complete({messages:st(o,t,Se(e),A(e)),signal:n.signal});return lt(a,o)}var Mr=32,$r=32,jr=80,ut=2e3,Z=32*1024*1024,Cr=1024,Lr=256,Rr=2048,ht=["You reconstruct a 3D scene from a reference image into a Three.js script for Three-Live, which compiles it to glTF for Web, Android, and iOS.","Reply with the COMPLETE script in a single ```js code block and nothing else.","Rules:","- Use the pre-defined globals `THREE`, `scene`, and `camera`. No imports, renderer, loaders, DOM, network, or timers.","- Decompose visible objects into `THREE.Group` and `THREE.Mesh` hierarchies with a unique, descriptive `.name` on every Mesh and Group (for example `Table`, `Chair_Left`, `Desk_Lamp`) before attaching `userData.xr` or `userData.interactivity`.","- Use real-world meters (1 unit = 1 m), +Y up, with the floor or resting surface at y = 0 (or the given metric floorY).","- When metric spatial priors (floor height, camera pose, planes, or 3D object boxes) are provided, place meshes at those exact metric [x, y, z] coordinates, dimensions, and yaw angles, and set `mesh.userData.xr = { grab: true }` on movable props.","- Supported: core geometries (`BoxGeometry`, `SphereGeometry`, `CylinderGeometry`, `ConeGeometry`, `CapsuleGeometry`, `PlaneGeometry`, `TorusGeometry`, `LatheGeometry`, `ExtrudeGeometry`, `TubeGeometry`, `Shape`, `Path`), `THREE.CSG`, `MeshStandardMaterial` / `MeshPhysicalMaterial` / `MeshStandardNodeMaterial` (loop-free `THREE.TSL`), `THREE.CanvasTexture`, `THREE.ProceduralTexture` presets (`wood`, `marble`, `brick`, `concrete`, `brushedMetal`, `rust`, `leather`, `fabric`, `carbonFiber`, `checkerboard`, `grid`), lights (`AmbientLight`, `HemisphereLight`, `DirectionalLight`, `PointLight`, `SpotLight`), and canonical `node.userData.xr` traits (`grab`, `anchor`, `physics`, `joint`, `handles`, `dimensions`, `billboard`, `orbit`, `follow`).","- Always include balanced lighting (`AmbientLight` or `HemisphereLight` plus a `DirectionalLight` or `PointLight`) and frame `camera.position.set(x, y, z)` and `camera.lookAt(tx, ty, tz)` so 100% of meshes are inside the camera frustum."].join(`
|
|
19
|
+
`);function dt(e){if(typeof e!="string")return"";let t="",r=!0;for(let n of e){let i=n.charCodeAt(0);i<=32||i===127||n==='"'||/\s/.test(n)?r||(t+=" ",r=!0):(t+=n,r=!1)}return Array.from(t.trim()).slice(0,jr).join("")}function R(e){let t=Math.round(e*100)/100;return(Object.is(t,-0)||Math.abs(t)<1e-9?0:t).toFixed(2)}function pt(e){let t=Math.round(e),r=Object.is(t,-0)?0:t;return String(r)}function U(e){if(!Array.isArray(e)||e.length<3)return null;let t=Number(e[0]),r=Number(e[1]),n=Number(e[2]);return!Number.isFinite(t)||!Number.isFinite(r)||!Number.isFinite(n)?null:[t,r,n]}function Ir(e){if(!Array.isArray(e)||e.length<2)return null;let t=Number(e[0]),r=Number(e[1]);return!Number.isFinite(t)||!Number.isFinite(r)||t<=0||r<=0?null:[t,r]}function Pr(e){let t=U(e);return!t||t[0]<=0||t[1]<=0||t[2]<=0?null:t}function q(e){return`[${R(e[0])}, ${R(e[1])}, ${R(e[2])}]`}function ke(e){if(!e||typeof e!="object")return"";let t=[];if(typeof e.floorY=="number"&&Number.isFinite(e.floorY)&&t.push(`- Floor height: y = ${R(e.floorY)} m`),e.camera&&typeof e.camera=="object"){let r=U(e.camera.eye),n=U(e.camera.target);if(r&&n){let i=`- Camera: eye ${q(r)}, target ${q(n)}`,o=Number(e.camera.fovDeg);Number.isFinite(o)&&o>=1&&o<=179&&(i+=`, fov ${pt(o)} deg`),t.push(i)}}if(Array.isArray(e.planes)){let r=[];for(let n of e.planes){if(r.length>=Mr)break;if(!n||typeof n!="object")continue;let i=U(n.center);if(!i)continue;let o=String(n.orientation??"").toLowerCase()==="vertical"?"vertical":"horizontal",a=dt(n.label),s=Ir(n.extent),l=U(n.normal),c=` ${r.length+1}. ${o}`;a&&(c+=` "${a}"`),c+=` at ${q(i)}`,s&&(c+=`, extent ${R(s[0])}m x ${R(s[1])}m`),l&&(c+=`, normal ${q(l)}`),r.push(c)}r.length>0&&t.push("- Planes:",...r)}if(Array.isArray(e.objects)){let r=[];for(let n of e.objects){if(r.length>=$r)break;if(!n||typeof n!="object")continue;let i=dt(n.label),o=U(n.center);if(!i||!o)continue;let a=Pr(n.size),s=typeof n.yawDeg=="number"&&Number.isFinite(n.yawDeg)?n.yawDeg:null,l=` ${r.length+1}. "${i}" at ${q(o)}`;a&&(l+=`, size ${R(a[0])}m x ${R(a[1])}m x ${R(a[2])}m`),s!==null&&(l+=`, yaw ${pt(s)} deg`),r.push(l)}r.length>0&&t.push("- Detected 3D objects (place meshes at these metric bounds):",...r)}return t.length===0?"":`Metric spatial priors (world meters, +Y up):
|
|
20
|
+
${t.join(`
|
|
21
|
+
`)}
|
|
22
|
+
`}function C(e){let t=String(e??"").trim();if(!t)return!1;if(/^data:image\/(png|jpeg|jpg|webp);base64,[A-Za-z0-9+/=\s]+$/i.test(t)){let r=t.indexOf(",");return r>0&&t.slice(r+1).trim().length>0}if(/[\s\x00-\x1f\x7f]/.test(t))return!1;try{let r=new URL(t);return r.protocol==="https:"?!!r.hostname:r.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(r.hostname)}catch{return!1}}function mt(e,t){let r=`Reconstruct the 3D scene shown in this reference image as a complete Three-Live script.
|
|
23
|
+
`;r+=ke(t);let n=Array.from(String(e??"").trim()).slice(0,ut).join("");return n&&(r+=`Additional instructions: ${n}
|
|
24
|
+
`),r+="Return only the complete script in a single ```js code block.",r}function ft(e,t,r){let n=String(e??"").trim();if(!C(n))throw new Error("compileFromImage: imageUrl must be a base64 data:image/(png|jpeg|webp) URI or an https:// URL.");let i=mt(t,r);return[{role:"system",content:ht},{role:"user",content:[{type:"image_url",image_url:{url:n}},{type:"text",text:i}]}]}function gt(e,t){let r=String(e??"").trim(),n=(t??[]).map(i=>String(i??"").trim()).filter(Boolean).slice(0,20);if(r){let i=`That script was rejected by the Three-Live sandbox: ${r}
|
|
25
|
+
`;return n.length>0&&(i+=`Additional diagnostics:
|
|
26
|
+
${n.map(o=>`- ${o}`).join(`
|
|
27
|
+
`)}
|
|
28
|
+
`),i+="Return the complete corrected script in a single ```js code block.",i}return n.length>0?`The script compiled, but the Three-Live linter reported warnings that should be fixed:
|
|
29
|
+
${n.map(i=>`- ${i}`).join(`
|
|
30
|
+
`)}
|
|
31
|
+
Fix every warning above (assign unique \`.name\` values before attaching \`userData.xr\`, use only canonical \`userData.xr\` traits, replace unsupported classes with supported Three-Live primitives, and frame \`camera.position\` and \`camera.lookAt\` so all meshes are inside the frustum) while keeping the full scene intact.
|
|
32
|
+
Return the complete corrected script in a single \`\`\`js code block.`:"Return the complete corrected script in a single ```js code block."}function yt(e){return e.length>=8&&e[0]===137&&e[1]===80&&e[2]===78&&e[3]===71&&e[4]===13&&e[5]===10&&e[6]===26&&e[7]===10?"image/png":e.length>=3&&e[0]===255&&e[1]===216&&e[2]===255?"image/jpeg":e.length>=12&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70&&e[8]===87&&e[9]===69&&e[10]===66&&e[11]===80?"image/webp":null}function Nr(e){let t=globalThis;if(t.Buffer&&typeof t.Buffer.from=="function")return t.Buffer.from(e.buffer,e.byteOffset,e.byteLength).toString("base64");let r="",n=8192;for(let i=0;i<e.length;i+=n){let o=e.subarray(i,Math.min(i+n,e.length));r+=String.fromCharCode(...o)}return btoa(r)}function Fr(e){let t=Number(e);return Number.isFinite(t)?Math.max(Lr,Math.min(Rr,Math.round(t))):Cr}function K(e){if(e.byteLength===0)throw new Error("compileFromImage: image buffer is empty.");if(e.byteLength>Z)throw new Error("compileFromImage: image exceeds the 32 MB limit.");let t=yt(e);if(!t)throw new Error("compileFromImage: unsupported image format (expected PNG, JPEG, or WebP magic bytes).");return`data:${t};base64,${Nr(e)}`}function Ur(e,t,r,n){let i=Math.max(t,r),o=i>n?n/i:1,a=Math.max(1,Math.round(t*o)),s=Math.max(1,Math.round(r*o)),l=e;if(o===1&&typeof l.toDataURL=="function")try{let c=l.toDataURL("image/jpeg",.88);if(C(c))return c}catch{}if(typeof document<"u"&&typeof document.createElement=="function")try{let c=document.createElement("canvas");if(c&&typeof c.getContext=="function"){c.width=a,c.height=s;let u=c.getContext("2d");if(u&&typeof u.drawImage=="function"&&(u.drawImage(e,0,0,a,s),typeof c.toDataURL=="function")){let d=c.toDataURL("image/jpeg",.88);if(C(d))return d}}}catch{}if(typeof l.toDataURL=="function")try{let c=l.toDataURL("image/jpeg",.88);if(C(c))return c}catch{}return null}async function _e(e,t={}){if(t.signal?.aborted)throw t.signal.reason??new Error("compileFromImage aborted");if(e==null)throw new Error("compileFromImage: image input is required.");let r=Fr(t.maxEdge);if(typeof e=="string"){let a=e.trim();if(!a)throw new Error("compileFromImage: image input is empty.");if(/^data:/i.test(a)){if(!C(a))throw new Error("compileFromImage: data URI must be a base64-encoded image/png, image/jpeg, or image/webp.");let h=a.indexOf(","),p=a.slice(0,h).toLowerCase().replace("image/jpg","image/jpeg"),y=a.slice(h+1).replace(/\s+/g,"");if(Math.floor(y.length*3/4)>Z)throw new Error("compileFromImage: image exceeds the 32 MB limit.");return`${p},${y}`}if(/^(javascript|vbscript|file|ftp|ws|wss):/i.test(a))throw new Error("compileFromImage: unsupported or unsafe image URL scheme.");if(/^http:/i.test(a)&&!C(a))throw new Error("compileFromImage: remote image URLs must use https:// (or http:// on localhost).");let s=/^https?:/i.test(a)&&C(a);if(s&&!t.fetch)return a;let l=a;if(!/^https?:|^blob:/i.test(a)){let h=globalThis.location?.href;if(!h)throw new Error("compileFromImage: imageUrl must be a base64 data:image/(png|jpeg|webp) URI or an https:// URL.");if(l=new URL(a,h).href,!/^blob:/i.test(l)&&!C(l))throw new Error("compileFromImage: resolved image URL must use https:// (or http:// on localhost).")}let c=t.fetch??globalThis.fetch?.bind(globalThis);if(typeof c!="function"){if(s)return a;throw new Error("compileFromImage: no fetch() implementation is available.")}let u=await c(l,{mode:"cors",credentials:"omit",signal:t.signal});if(!u.ok)throw new Error(`compileFromImage: failed to fetch image (HTTP ${u.status}).`);let d=Number(u.headers?.get?.("content-length"));if(Number.isFinite(d)&&d>Z)throw new Error("compileFromImage: image exceeds the 32 MB limit.");let m=await u.arrayBuffer();if(t.signal?.aborted)throw t.signal.reason??new Error("compileFromImage aborted");return K(new Uint8Array(m))}if(e instanceof ArrayBuffer||Object.prototype.toString.call(e)==="[object ArrayBuffer]")return K(new Uint8Array(e));if(ArrayBuffer.isView(e))return K(new Uint8Array(e.buffer,e.byteOffset,e.byteLength));if(typeof Blob<"u"&&e instanceof Blob||typeof e.arrayBuffer=="function"){let a=e;if(typeof a.size=="number"&&a.size>Z)throw new Error("compileFromImage: image exceeds the 32 MB limit.");let s=await a.arrayBuffer();if(t.signal?.aborted)throw t.signal.reason??new Error("compileFromImage aborted");return K(new Uint8Array(s))}let n=e,i=Number(n.videoWidth||n.naturalWidth||n.width||0),o=Number(n.videoHeight||n.naturalHeight||n.height||0);if(i>0&&o>0){let a=Ur(e,i,o,r);if(a)return a}if(typeof n.toDataURL=="function"){let a=n.toDataURL("image/jpeg",.88);if(C(a))return a}if(typeof n.src=="string"&&n.src.trim())return _e(n.src,t);throw new Error("compileFromImage: unsupported or unloaded image source.")}async function bt(e,t,r={}){if(!e?.allowClientScripts)throw new Error("compileFromImage requires client scripts: add the allow-client-scripts attribute.");let n=Fe(e,r,"compileFromImage"),i=Ue(e);e.__tlAiAbort?.abort(new Error("compileFromImage superseded by a newer call"));let o=new AbortController;e.__tlAiAbort=o;let a=()=>o.abort(r.signal?.reason);r.signal?.aborted&&a(),r.signal?.addEventListener("abort",a,{once:!0});let s=o.signal,l=Array.from(String(r.prompt??"").trim()).slice(0,ut).join(""),c=ke(r.priors).length>0,u=Math.max(0,Math.min(3,Math.floor(Number(r.maxRepairs??1))||0)),d="",m="",h=[],p=0,y=null;try{let f=await _e(t,{maxEdge:r.maxEdge,signal:s,fetch:r.fetch});if(s.aborted)throw s.reason;let g=ft(f,l,r.priors);e.fireEvent?.("three-live-image-start",{prompt:l,hasPriors:c});for(let x=0;x<=u;x++){let w=await n.complete({messages:g,signal:s});if(s.aborted)throw s.reason;if(d=Ne(w),h=[],!d)m="The model reply did not contain a scene script.";else if(new TextEncoder().encode(d).length>Yt)m="The generated script exceeds the 1 MB script limit.";else try{let b=await er(e,d,s,"compileFromImage");h=[...b.warnings];let k=Qt(b.model);k.unnamedXrNodes>0&&!h.some(T=>T.includes("TL204"))&&h.push("[TL204] Node with userData.xr is missing a unique .name."),k.meshes===0?m="The generated scene contains no meshes.":(m="",y={code:d,warnings:[...h]})}catch(b){if(s.aborted)throw s.reason;let k=b,T=String(k?.message||b);m=k?.name&&k.name!=="Error"&&!T.startsWith(k.name)?`${k.name}: ${T}`:T}if(!m&&h.length===0)break;x<u&&(p++,g.push({role:"assistant",content:w},{role:"user",content:gt(m,h)}))}}finally{r.signal?.removeEventListener("abort",a),e.__tlAiAbort===o&&(e.__tlAiAbort=null)}if(m&&y&&(d=y.code,h=y.warnings,m=""),!m&&!r.dryRun)try{if(await e.executeScript(d),i.trim()){let f=e.__tlAiHistory??(e.__tlAiHistory=[]);f.push(i),f.length>Zt&&f.shift()}e.__tlAiSource=d,j(e,[],{mode:"clear",source:"api"})}catch(f){m=`The generated scene compiled but failed to load: ${f?.message||String(f)}`,e.__tlAiSource=i,i.trim()&&e.executeScript(i).catch(()=>{})}let v={applied:!m,code:d,repairs:p,warnings:h,...m?{error:m}:{}};return e.fireEvent?.("three-live-image",{prompt:l,...v}),v}var Ee="three-live-texture-pack",Te=1,wt=64,vt=6*1024*1024,X=16*1024*1024,Or=64*1024*1024,xt=" Seamless tileable PBR albedo texture, flat even lighting, orthographic top-down view, no perspective, no shadows, no text.";function St(){return P()}function kt(e){let t=St().canonicalTextureSpec;if(typeof t!="function")throw new Error("AI textures need the procedural plugin (three-live-procedural.min.js or a full bundle).");return t(e)}function Dr(e){try{let t=new URL(e);return t.protocol==="https:"?!0:t.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(t.hostname)}catch{return!1}}function Q(e,t){let r=e.replace(/\s+/g,"");if(r.length>Math.ceil(t*4/3)+4)throw new Error("image exceeds the size limit");let n=atob(r),i=new Uint8Array(n.length);for(let o=0;o<n.length;o++)i[o]=n.charCodeAt(o);return i}function qr(e){let t="";for(let r=0;r<e.length;r+=32768)t+=String.fromCharCode(...e.subarray(r,r+32768));return btoa(t)}async function zr(e){if(e instanceof Uint8Array)return e;if(e instanceof ArrayBuffer)return new Uint8Array(e);if(e&&typeof e.arrayBuffer=="function")return new Uint8Array(await e.arrayBuffer());throw new Error("image provider returned no image bytes")}function _t(e){let t=String(e?.endpoint??"");if(!Dr(t))throw new Error("ai-image: endpoint must be an https:// URL (or http:// on localhost).");let r=e.fetch??globalThis.fetch?.bind(globalThis);if(typeof r!="function")throw new Error("ai-image: no fetch() implementation is available.");let n=Math.max(1e3,Number(e.timeoutMs)||12e4),i=new URL(t).origin;return{name:"openai-images",model:e.model,async generateImage({prompt:o,signal:a}){let s=new AbortController,l=()=>s.abort(a?.reason);a?.aborted&&l(),a?.addEventListener("abort",l,{once:!0});let c=setTimeout(()=>s.abort(new Error(`ai-image: timed out after ${n} ms`)),n);try{let u={prompt:o+xt,n:1,size:e.imageSize||"1024x1024"};e.model&&(u.model=e.model),e.responseFormat!==null&&(u.response_format=e.responseFormat??"b64_json");let d=await r(t,{method:"POST",headers:{"Content-Type":"application/json",...e.headers},body:JSON.stringify(u),signal:s.signal}),m=await d.text();if(!d.ok)throw new Error(`ai-image: HTTP ${d.status} from endpoint: ${m.slice(0,300)}`);let h;try{h=JSON.parse(m)}catch{throw new Error("ai-image: endpoint returned non-JSON content.")}let p=h?.data?.[0];if(typeof p?.b64_json=="string")return Q(p.b64_json,X);let y=typeof p?.url=="string"?p.url:"";if(y.startsWith("data:image/"))return Q(y.slice(y.indexOf(",")+1),X);if(y){let v=!1;try{v=new URL(y,t).origin===i}catch{}if(!e.allowResponseUrls||!v)throw new Error('ai-image: the endpoint returned an image URL; use response_format "b64_json" (or allowResponseUrls for same-origin URLs).');let f=await r(new URL(y,t).href,{signal:s.signal});if(!f.ok)throw new Error(`ai-image: HTTP ${f.status} fetching the image.`);let g=new Uint8Array(await f.arrayBuffer());if(g.length>X)throw new Error("ai-image: image exceeds 16 MB.");return g}throw new Error("ai-image: response had no image.")}finally{clearTimeout(c),a?.removeEventListener("abort",l)}}}}function Et(e){return e.length>=8&&e[0]===137&&e[1]===80&&e[2]===78&&e[3]===71?"png":e.length>=3&&e[0]===255&&e[1]===216&&e[2]===255?"jpeg":e.length>=12&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70&&e[8]===87&&e[9]===69&&e[10]===66&&e[11]===80?"webp":null}var ee={async decode(e){let t=Et(e);if(!t)throw new Error("image must be PNG, JPEG, or WebP");let r=new Blob([e],{type:`image/${t}`}),n=await createImageBitmap(r,{premultiplyAlpha:"none",colorSpaceConversion:"none"}),{width:i,height:o}=n,a=(typeof OffscreenCanvas<"u"?new OffscreenCanvas(i,o):Object.assign(document.createElement("canvas"),{width:i,height:o})).getContext("2d",{willReadFrequently:!0});if(!a)throw new Error("2D canvas unavailable for image decoding");a.drawImage(n,0,0),n.close?.();let s=a.getImageData(0,0,i,o).data;return{width:i,height:o,rgba:new Uint8Array(s.buffer,s.byteOffset,s.byteLength)}},async encodePng(e,t){let r=St().encodeRgbaPngDataUri;if(typeof r=="function"){let n=r(e,t,t),i=Q(n.slice(n.indexOf(",")+1),1/0),o=P().makePngDeflater?.(globalThis.CompressionStream);return o?o.png(i):i}throw new Error("PNG encoding needs the procedural plugin")}};function Br(e,t,r,n,i){let o=new Uint8Array(n*i*4);for(let a=0;a<i;a++){let s=Math.floor(a*r/i),l=Math.min(r,Math.max(s+1,Math.floor((a+1)*r/i)));for(let c=0;c<n;c++){let u=Math.floor(c*t/n),d=Math.min(t,Math.max(u+1,Math.floor((c+1)*t/n))),m=0,h=0,p=0,y=0,v=0;for(let g=s;g<l;g++)for(let x=u;x<d;x++){let w=(g*t+x)*4;m+=e[w],h+=e[w+1],p+=e[w+2],y+=e[w+3],v++}let f=(a*n+c)*4;o[f]=Math.floor((m+v/2)/v),o[f+1]=Math.floor((h+v/2)/v),o[f+2]=Math.floor((p+v/2)/v),o[f+3]=Math.floor((y+v/2)/v)}}return o}function Wr(e,t){let r=Math.max(1,Math.floor(t/8)),n=Math.floor(t/2),i=a=>{let s=Math.max(0,Math.min(1,a));return s*s*(3-2*s)},o=new Uint8Array(t*t*4);for(let a=0;a<t;a++){let s=(Math.min(a,t-1-a)+.5)/r;for(let l=0;l<t;l++){let c=(Math.min(l,t-1-l)+.5)/r,u=1-i(Math.min(c,s)),d=(a*t+l)*4,m=((a+n)%t*t+(l+n)%t)*4;for(let h=0;h<3;h++){let p=e[d+h];o[d+h]=Math.max(0,Math.min(255,Math.floor(p+(e[m+h]-p)*u+.5)))}o[d+3]=255}}return o}function Tt(e,t){let r=Math.min(e.width,e.height);if(!(r>0))throw new Error("generated image is empty");let n=Math.floor((e.width-r)/2),i=Math.floor((e.height-r)/2),o=new Uint8Array(r*r*4);for(let s=0;s<r;s++){let l=((i+s)*e.width+n)*4;o.set(e.rgba.subarray(l,l+r*4),s*r*4)}let a=r===t?o:Br(o,r,r,t,t);for(let s=3;s<a.length;s+=4)a[s]=255;return Wr(a,t)}async function At(e,t=ee){let r=typeof e=="string"?JSON.parse(e):e;if(!r||r.format!==Ee)throw new Error(`texture pack: format must be "${Ee}"`);if(r.version!==Te)throw new Error(`texture pack: version must be ${Te}`);if(!r.entries||typeof r.entries!="object"||Array.isArray(r.entries))throw new Error("texture pack: entries must be an object");let n=Object.keys(r.entries);if(n.length>wt)throw new Error(`texture pack: more than ${wt} entries`);let i=new Map;for(let o of n){let a=r.entries[o];if(!/^[0-9a-f]{16}$/.test(o))throw new Error(`texture pack: invalid key '${o}'`);let s=kt(a?.spec);if(!s)throw new Error(`texture pack: entry ${o}: invalid spec`);if(s.key!==o)throw new Error(`texture pack: entry ${o}: key does not match its spec (${s.key})`);let l=typeof a?.albedo=="string"?a.albedo:"",c="data:image/png;base64,";if(!l.startsWith(c))throw new Error(`texture pack: entry ${o}: albedo must be a data:image/png;base64 URL`);let u=Q(l.slice(c.length),vt);if(u.length>vt||Et(u)!=="png")throw new Error(`texture pack: entry ${o}: albedo is not a PNG within 6 MB`);let d=await t.decode(u);if(d.width!==s.size||d.height!==s.size)throw new Error(`texture pack: entry ${o}: albedo is ${d.width}x${d.height}, expected ${s.size}x${s.size}`);let m=new Uint8Array(d.rgba);for(let p=3;p<m.length;p+=4)m[p]=255;let h=a.provenance&&typeof a.provenance=="object"?a.provenance:void 0;i.set(o,{key:o,spec:{prompt:s.prompt,size:s.size,seed:s.seed},rgba:m,...h?{provenance:h}:{}})}return i}async function Mt(e,t=ee){let r={};for(let n of[...e.keys()].sort()){let i=e.get(n),o=await t.encodePng(i.rgba,i.spec.size);r[n]={spec:i.spec,albedo:`data:image/png;base64,${qr(o)}`,...i.provenance?{provenance:i.provenance}:{}}}return{format:Ee,version:Te,entries:r}}function Vr(e){let t=e?.gltf;if(t)return t;if(e instanceof ArrayBuffer&&e.byteLength>=20){let r=new DataView(e);if(r.getUint32(0,!0)===1179937895)try{return JSON.parse(new TextDecoder().decode(new Uint8Array(e,20,r.getUint32(12,!0))))}catch{}}return null}function Ae(e){let t=new Map;for(let r of Vr(e)?.nodes??[]){let n=r?.extras?.xr?.material;if(!n||typeof n!="object"||n.prompt==null)continue;let i=kt(n);if(!i)continue;let o=t.get(i.key)??{spec:{prompt:i.prompt,size:i.size,seed:i.seed},meshes:[]};typeof r.name=="string"&&r.name&&o.meshes.push(r.name),t.set(i.key,o)}return[...t].map(([r,n])=>({key:r,spec:n.spec,meshes:n.meshes}))}function $t(e=Or){if(typeof indexedDB>"u")return null;let t=null,r=()=>t??(t=new Promise((i,o)=>{let a=indexedDB.open("three-live-textures",1);a.onupgradeneeded=()=>{a.result.createObjectStore("textures",{keyPath:"key"})},a.onsuccess=()=>i(a.result),a.onerror=()=>o(a.error)})),n=(i,o)=>r().then(a=>new Promise((s,l)=>{let c=a.transaction("textures",i),u=o(c.objectStore("textures"));c.oncomplete=()=>s(u?u.result:void 0),c.onerror=()=>l(c.error)}));return{async get(i){try{let o=await n("readonly",a=>a.get(i));return!o||!(o.rgba instanceof Uint8Array)?null:(n("readwrite",a=>a.put({...o,t:Date.now()})).catch(()=>{}),o)}catch{return null}},async put(i){try{await n("readwrite",l=>l.put({...i,t:Date.now()}));let o=await n("readonly",l=>l.getAll())??[],a=o.reduce((l,c)=>l+(c.rgba?.byteLength??0),0);o.sort((l,c)=>l.t-c.t);let s=[];for(let l of o){if(a<=e)break;a-=l.rgba?.byteLength??0,s.push(l.key)}s.length&&await n("readwrite",l=>{for(let c of s)l.delete(c)})}catch{}}}}var z=null,jt=!1;function L(e){return e.__tlTex??(e.__tlTex={pack:new Map,packJson:null,failed:new Set,inflight:new Map,lastModel:null,lastRun:null,cache:void 0,loadSeq:0})}function Hr(e){let t=e.__tlTex;if(!t||t.pack.size===0)return null;let r={};for(let[n,i]of t.pack)r[n]={size:i.spec.size,rgba:i.rgba};return r}function Jr(){let e=P(),t=e.scriptRunner;if(!t||t.__tlTexWrapped)return;let r=n=>z&&!n?.texturePack?{...n,texturePack:z}:n;e.scriptRunner={__tlTexWrapped:!0,execute:(n,i,o,a)=>t.execute(n,i,o,r(a)),...typeof t.executeUrl=="function"?{executeUrl:(n,i,o,a)=>t.executeUrl(n,i,o,r(a))}:{}}}function Ct(e,t){Jr();let r=z;z=Hr(e);try{return t()}finally{z=r}}function Lt(e,t){let r=t??e.aiImageProvider;if(r&&typeof r.generateImage=="function")return r;let n=e.getAttribute?.("ai-image-endpoint");return n?_t({endpoint:n,model:e.getAttribute?.("ai-image-model")||void 0}):null}function Rt(e){if(e.textureCache!==void 0)return e.textureCache;let t=L(e);return t.cache===void 0&&(t.cache=$t()),t.cache}function Me(e){return e.textureCodec??ee}function B(e){let t=e.querySelector?.('script[type="text/three"], script[type="text/three-live"]')?.textContent,r=L(e).lastRun??(t?{kind:"script",value:t}:null);return r?r.kind==="url"&&typeof e.loadScriptFromSrc=="function"?e.loadScriptFromSrc(r.value):e.executeScript(r.value):Promise.resolve()}function It(e,t){let r=L(e);r.pack.set(t.key,t),r.packJson=null,r.failed.delete(t.key)}async function Pt(e,t){let r=Rt(e);if(!r)return[];let n=[];for(let i of t){let o=await r.get(i.key);o&&o.spec?.size===i.spec.size&&o.rgba?.length===i.spec.size*i.spec.size*4&&(It(e,{key:i.key,spec:i.spec,rgba:o.rgba,provenance:o.provenance}),e.fireEvent?.("three-live-texture",{key:i.key,meshes:i.meshes,source:"cache"}),n.push(i.key))}return n}async function Nt(e,t,r,n){let i=L(e),o=i.inflight.get(r.key);if(o)return o;let a=(async()=>{try{let s=await zr(await t.generateImage({...r.spec,signal:n}));if(s.length>X)throw new Error("generated image exceeds 16 MB");let l=Tt(await Me(e).decode(s),r.spec.size),c={provider:t.name??"custom",...t.model?{model:t.model}:{},createdAt:new Date().toISOString()};return It(e,{key:r.key,spec:r.spec,rgba:l,provenance:c}),Rt(e)?.put({key:r.key,spec:r.spec,rgba:l,provenance:c}),e.fireEvent?.("three-live-texture",{key:r.key,meshes:r.meshes,source:"provider"}),!0}catch(s){return i.failed.add(r.key),e.fireEvent?.("three-live-texture-error",{key:r.key,meshes:r.meshes,error:String(s?.message||s)}),!1}finally{i.inflight.delete(r.key)}})();return i.inflight.set(r.key,a),a}async function Gr(e,t,r){let n=0,i=Array.from({length:Math.min(t,e.length)},async()=>{for(;n<e.length;)await r(e[n++])});await Promise.all(i)}function Ft(e,t){let r=L(e);r.lastModel=t;let n;try{n=Ae(t).filter(o=>!r.pack.has(o.key))}catch{return}let i=n.filter(o=>!r.failed.has(o.key)&&!r.inflight.has(o.key));i.length&&(async()=>{if((await Pt(e,i)).length){await B(e).catch(()=>{});return}if((e.getAttribute?.("ai-textures")||"auto").toLowerCase()==="manual")return;let o=Lt(e);if(!o)return;e.fireEvent?.("three-live-texture-start",{keys:i.map(s=>s.key)});let a=(await Promise.all(i.map(s=>Nt(e,o,s)))).filter(Boolean).length;a&&(jt||(jt=!0,console.info(`[three-live] Generated ${a} AI texture(s) at runtime. Each visitor without a cached copy triggers new (and different) generations. For consistent results, save el.exportTexturePack() and load it via texture-pack.`)),await B(e).catch(()=>{}))})()}function Ut(e,t){L(e).lastRun=t}async function $e(e,t){let r=L(e),n=++r.loadSeq;if(t==null){r.pack=new Map,r.packJson=null,await B(e).catch(()=>{});return}try{let i=await At(t,Me(e));if(n!==r.loadSeq)return;r.pack=i,r.packJson=typeof t=="string"?JSON.parse(t):t,r.failed.clear(),await B(e).catch(()=>{})}catch(i){e.fireEvent?.("three-live-texture-error",{key:null,meshes:[],error:String(i?.message||i)})}}async function Ot(e,t){try{let r=typeof document<"u"?document.baseURI:void 0,n=await fetch(new URL(t,r).href,{credentials:"same-origin"});if(!n.ok)throw new Error(`HTTP ${n.status} loading texture-pack "${t}"`);await $e(e,await n.json())}catch(r){e.fireEvent?.("three-live-texture-error",{key:null,meshes:[],error:String(r?.message||r)})}}async function Yr(e,t={}){let r=L(e),n=t.meshes?new Set(t.meshes):null,i=Ae(r.lastModel).filter(c=>(t.force||!r.pack.has(c.key))&&(!n||c.meshes.some(u=>n.has(u)))),o=[],a=[],s=[],l=i;if(!t.force){let c=new Set(await Pt(e,i));o.push(...c),l=i.filter(u=>!c.has(u.key))}if(l.length){let c=Lt(e,t.provider);if(!c)throw new Error("generateTextures: no image provider. Set el.aiImageProvider, or the ai-image-endpoint (and ai-image-model) attributes.");e.fireEvent?.("three-live-texture-start",{keys:l.map(m=>m.key)});let u=Math.max(1,Math.min(4,Math.floor(Number(t.concurrency??2))||2)),d=m=>s.push(m);await Gr(l,u,async m=>{r.failed.delete(m.key),t.force&&r.pack.delete(m.key),await Nt(e,c,m,t.signal)?a.push(m.key):d({key:m.key,error:"generation failed (see three-live-texture-error)"})})}return(o.length||a.length)&&await B(e).catch(()=>{}),{generated:a,cached:o,failed:s,pack:await Dt(e)}}async function Dt(e){let t=L(e);return t.packJson??(t.packJson=await Mt(t.pack,Me(e)))}function Zr(e){if(!e||e.__tlTexInstalled)return;e.__tlTexInstalled=!0,L(e),Object.defineProperty(e,"texturePack",{get:()=>e.__tlTex?.packJson??null,set:i=>{$e(e,i)},configurable:!0}),e.generateTextures=i=>Yr(e,i),e.exportTexturePack=()=>Dt(e);let t=e.querySelector?.('script[type="application/three-live-texture-pack"]');t?.textContent?.trim()&&$e(e,t.textContent);let r=e.getAttribute?.("texture-pack");r&&Ot(e,r);let n=e;typeof MutationObserver<"u"&&n&&typeof n.nodeType=="number"&&new MutationObserver(()=>{let i=e.getAttribute?.("texture-pack");i&&Ot(e,i)}).observe(n,{attributes:!0,attributeFilter:["texture-pack"]})}var te=12;function re(e){return String(e).replace(/([a-z])([A-Z])/g,"$1 $2").replace(/([A-Za-z])([0-9])/g,"$1 $2").replace(/[_\-.]+/g," ").trim().slice(0,60)}function Kr(e){if(typeof document>"u")return!1;let t=(e.getAttribute?.("ai-select-chips")??"").toLowerCase();return t!=="off"&&t!=="false"}function ne(e,t,r,n=!1){let i=document.createElement("button");return i.type="button",i.textContent=e,i.setAttribute("aria-label",t),Object.assign(i.style,{border:"none",borderRadius:"14px",padding:"4px 10px",font:"13px system-ui, sans-serif",cursor:"pointer",background:n?"#1a73e8":"rgba(255,255,255,0.92)",color:n?"#fff":"#202124"}),i.addEventListener("click",o=>{o.stopPropagation(),r()}),i}function ie(e){let t=e.__tlChips;t&&(t.off(),t.root.remove(),e.__tlChips=null)}function qt(e,t,r){if(!Kr(e))return null;let n=e.__tlChips;if(!n){let i=document.createElement("div");i.setAttribute("role","status"),i.setAttribute("aria-live","polite"),i.setAttribute("part","selection-chips"),Object.assign(i.style,{position:"absolute",left:"64px",bottom:"12px",maxWidth:"calc(100% - 76px)",boxSizing:"border-box",display:"flex",flexWrap:"wrap",alignItems:"center",gap:"6px",padding:"6px 8px",borderRadius:"18px",background:"rgba(32,33,36,0.85)",boxShadow:"0 1px 4px rgba(0,0,0,0.3)",zIndex:"10",font:"13px system-ui, sans-serif",color:"#fff",pointerEvents:"auto"});let o=()=>{e.__tlChips&&!e.__tlChips.state.question&&oe(e,e.__tlChips.state,r)};e.addEventListener?.("three-live-select",o),e.appendChild?.(i),n={root:i,off:()=>e.removeEventListener?.("three-live-select",o),state:t},e.__tlChips=n}return oe(e,t,r),n.root}function oe(e,t,r){let n=e.__tlChips;if(!n)return;n.state=t;let i=n.root;i.replaceChildren();let o=a=>{let s=document.createElement("span");s.textContent=a,i.appendChild(s)};if(t.question){o(t.question);for(let a of(t.candidates??[]).slice(0,te))i.appendChild(ne(re(a),`Select ${re(a)}`,()=>{j(e,[a],{mode:t.mode==="add"||t.mode==="remove"?t.mode:"replace",source:"tap",query:t.query}),oe(e,{pending:t.pending},r)}))}else{let a=A(e);if(!a.length&&!t.pending){ie(e);return}o(a.length?"Selected:":"Nothing selected");for(let s of a.slice(0,te))i.appendChild(ne(`${re(s)} \xD7`,`Remove ${re(s)} from the selection`,()=>j(e,[s],{mode:"remove",source:"tap"})));if(a.length>te&&o(`+${a.length-te} more`),t.pending&&r&&a.length){let s=t.pending;i.appendChild(ne(`Apply: ${s.slice(0,40)}`,`Apply: ${s.slice(0,200)}`,()=>{oe(e,{},r),r(s)},!0))}}i.appendChild(ne("\u2715","Dismiss",()=>ie(e)))}var Xr=500,O=2e3,Qr=8,en=[{name:"edit_scene",description:'Change the 3D scene the user is looking at. Call this whenever the user asks to add, remove, move, resize, recolor, or otherwise modify anything. Pass their request as a clear, self-contained instruction. If they selected a mesh, "this" or "it" refers to that mesh; the tool knows which one.',parameters:{type:"object",properties:{instruction:{type:"string",description:'The requested change, e.g. "make the roof red".'}},required:["instruction"]}},{name:"undo_edit",description:"Undo the last scene change.",parameters:{type:"object",properties:{}}},{name:"select_meshes",description:'Select objects in the scene so the user can see what an edit will change. Call this when the user refers to objects by name, color, or position ("the two red chairs on the left"). Pass their words as query, or exact mesh names as names. The result lists the selection, or a question to ask when the reference was unclear.',parameters:{type:"object",properties:{query:{type:"string",description:`The user's words for the objects, e.g. "the lamp next to the sofa".`},names:{type:"array",items:{type:"string"},description:"Exact mesh names, when known."},mode:{type:"string",enum:["replace","add","remove","clear"],description:"replace (default), add, remove, or clear."}}}}],tn=["You are a voice assistant inside a live 3D scene editor.","Keep spoken replies short: one sentence.","To change the scene, call edit_scene with the request; never describe code.","After a tool result, say briefly what changed, or why it could not be done.","When the user refers to objects by name, color, or position, call select_meshes with their words first.","If its result has a question, ask it and wait; otherwise say what is selected, and if they also asked for a change, call edit_scene."].join(" ");function rn(e){try{let t=new URL(e);return t.protocol==="wss:"?!0:t.protocol==="ws:"&&["localhost","127.0.0.1","[::1]"].includes(t.hostname)}catch{return!1}}function nn(e){if(!rn(e))throw new Error("connectVoice: url must be wss:// (or ws:// on localhost).");let t=new WebSocket(e),r=[],n={send(i){let o=JSON.stringify(i);t.readyState===1?t.send(o):t.readyState===0&&r.length<256&&r.push(o)},close(){r.length=0,t.onopen=t.onmessage=t.onclose=null,t.close(),n.onClose?.()}};return t.onopen=()=>{for(let i of r.splice(0))t.send(i)},t.onmessage=async i=>{let o=n.onMessage;try{let a=typeof i.data=="string"?i.data:await i.data.text(),s=JSON.parse(a);s&&typeof s=="object"&&o?.(s)}catch{}},t.onclose=()=>n.onClose?.(),n}function S(e){return typeof e=="string"?e:""}function M(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function je(e,t){let r={...e};for(let[n,i]of Object.entries(t??{})){if(n==="__proto__"||n==="constructor"||n==="prototype")continue;let o=r[n];r[n]=i&&typeof i=="object"&&!Array.isArray(i)&&o&&typeof o=="object"&&!Array.isArray(o)?je(o,i):i}return r}function zt(e){if(e&&typeof e=="object")return M(e);try{return M(JSON.parse(S(e)||"{}"))}catch{return{}}}var Bt={name:"ai-realtime",inputRate:24e3,setup(e){let t={type:"realtime",instructions:e.instructions,audio:{input:{format:{type:"audio/pcm",rate:24e3},turn_detection:{type:"server_vad"}},output:{format:{type:"audio/pcm",rate:24e3},...e.voice?{voice:e.voice}:{}}},tools:e.tools.map(r=>({type:"function",...r})),tool_choice:"auto",...e.model?{model:e.model}:{}};return[{type:"session.update",session:je(t,e.overrides)}]},audio(e){return{type:"input_audio_buffer.append",audio:e}},text(e,t){let r=[{type:"conversation.item.create",item:{type:"message",role:t?"user":"system",content:[{type:"input_text",text:e}]}}];return t&&r.push({type:"response.create"}),r},toolResult(e,t,r){return[{type:"conversation.item.create",item:{type:"function_call_output",call_id:e,output:JSON.stringify(r)}},{type:"response.create"}]},parse(e){switch(S(e.type)){case"response.output_audio.delta":case"response.audio.delta":return S(e.delta)?[{type:"audio",base64:S(e.delta),rate:24e3}]:[];case"response.output_audio_transcript.delta":case"response.audio_transcript.delta":return[{type:"transcript",speaker:"model",text:S(e.delta),isFinal:!1}];case"response.output_audio_transcript.done":case"response.audio_transcript.done":return[{type:"transcript",speaker:"model",text:S(e.transcript),isFinal:!0}];case"conversation.item.input_audio_transcription.delta":return[{type:"transcript",speaker:"user",text:S(e.delta),isFinal:!1}];case"conversation.item.input_audio_transcription.completed":return[{type:"transcript",speaker:"user",text:S(e.transcript),isFinal:!0}];case"response.function_call_arguments.done":return[{type:"toolCall",id:S(e.call_id)||S(e.item_id),name:S(e.name),args:zt(e.arguments)}];case"input_audio_buffer.speech_started":return[{type:"interrupted"}];case"response.done":return[{type:"turnComplete"}];case"error":return[{type:"error",message:S(M(e.error).message)||"Realtime server error"}];default:return[]}}},Wt={name:"gemini-live",inputRate:16e3,setup(e){let t={...e.model?{model:e.model}:{},generationConfig:{responseModalities:["AUDIO"],...e.voice?{speechConfig:{voiceConfig:{prebuiltVoiceConfig:{voiceName:e.voice}}}}:{}},systemInstruction:{parts:[{text:e.instructions}]},tools:[{functionDeclarations:e.tools.map(r=>({...r}))}],inputAudioTranscription:{},outputAudioTranscription:{}};return[{setup:je(t,e.overrides)}]},audio(e){return{realtimeInput:{audio:{mimeType:"audio/pcm;rate=16000",data:e}}}},text(e,t){return[{clientContent:{turns:[{role:"user",parts:[{text:e}]}],turnComplete:t}}]},toolResult(e,t,r){return[{toolResponse:{functionResponses:[{id:e,name:t,response:r}]}}]},parse(e){let t=[],r=M(e.serverContent);r.interrupted&&t.push({type:"interrupted"});let n=M(r.inputTranscription);S(n.text)&&t.push({type:"transcript",speaker:"user",text:S(n.text),isFinal:!1});let i=M(r.outputTranscription);S(i.text)&&t.push({type:"transcript",speaker:"model",text:S(i.text),isFinal:!1});let o=M(r.modelTurn).parts;for(let l of Array.isArray(o)?o:[]){let c=M(M(l).inlineData),u=S(c.mimeType);if(u.startsWith("audio/pcm")&&S(c.data)){let d=Number(/rate=(\d+)/.exec(u)?.[1])||24e3;t.push({type:"audio",base64:S(c.data),rate:d})}}r.turnComplete&&t.push({type:"turnComplete"});let a=M(e.toolCall).functionCalls;for(let l of Array.isArray(a)?a:[]){let c=M(l);t.push({type:"toolCall",id:S(c.id),name:S(c.name),args:zt(c.args)})}let s=M(e.error);return S(s.message)&&t.push({type:"error",message:S(s.message)}),t}},on={"ai-realtime":Bt,"gemini-live":Wt};function an(e){let t=new Uint8Array(e.buffer,e.byteOffset,e.byteLength),r="";for(let n=0;n<t.length;n+=32768)r+=String.fromCharCode(...t.subarray(n,n+32768));return btoa(r)}function sn(e,t,r){let n=t/r,i=Math.max(0,Math.floor(e.length/n)),o=new Int16Array(i);for(let a=0;a<i;a++){let s=a*n,l=Math.floor(s),c=Math.min(l+1,e.length-1),u=e[l]+(e[c]-e[l])*(s-l),d=Math.max(-1,Math.min(1,u));o[a]=d<0?d*32768:d*32767}return o}function ln(e){let t=atob(e),r=t.length>>1,n=new Float32Array(r);for(let i=0;i<r;i++){let o=t.charCodeAt(i*2)|t.charCodeAt(i*2+1)<<8;o&32768&&(o-=65536),n[i]=o/32768}return n}function Vt(){let e=globalThis;return e.AudioContext??e.webkitAudioContext}function cn(){let e=null,t=null,r=null;return{async start(n,i){let o=Vt(),a=globalThis.navigator?.mediaDevices;if(!o||!a?.getUserMedia)throw new Error("connectVoice: microphone capture is not available here.");e=await a.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,channelCount:1}}),t=new o;let s=t.createMediaStreamSource(e);r=t.createScriptProcessor(4096,1,1);let l=t.sampleRate;r.onaudioprocess=c=>{let u=sn(c.inputBuffer.getChannelData(0),l,n);u.length&&i(an(u))},s.connect(r),r.connect(t.destination)},stop(){r&&(r.onaudioprocess=null),r?.disconnect();for(let n of e?.getTracks()??[])n.stop();t?.close(),r=null,e=null,t=null}}}function un(){let e=null,t=0,r=new Set,n={play(i,o){let a=Vt();if(!e&&a)try{e=new a}catch{return}if(!e)return;e.state==="suspended"&&e.resume?.();let s=ln(i);if(!s.length)return;let l=e.createBuffer(1,s.length,o);l.getChannelData(0).set(s);let c=e.createBufferSource();c.buffer=l,c.connect(e.destination);let u=Math.max(e.currentTime,t);c.start(u),t=u+l.duration,r.add(c),c.onended=()=>r.delete(c)},flush(){for(let i of r)try{i.stop()}catch{}r.clear(),t=0},close(){n.flush(),e?.close(),e=null}};return n}function _(e,t){e.fireEvent?.("three-live-voice",t)}var Ht=class{constructor(e,t){this.el=e,this.options=t,this.state="connecting",this.userText="",this.modelText="",this.onSelect=i=>{let o=i.detail;if(o?.source==="voice")return;let a=Array.isArray(o?.meshes)?o.meshes:o?.mesh?[o.mesh]:[];this.sendContext(ve(a))};let r=t.provider??"ai-realtime",n=on[r];if(!n)throw new Error(`connectVoice: unknown provider '${r}' (use ai-realtime or gemini-live).`);if(this.adapter=n,t.transport)this.transport=t.transport;else if(t.url)this.transport=nn(t.url);else throw new Error("connectVoice: pass a wss:// url or a transport.");this.mic=t.microphone===!1?null:t.microphoneSource??cn(),this.sink=t.playAudio===!1?null:t.audioSink??un()}async start(){this.transport.onMessage=r=>this.handle(r),this.transport.onClose=()=>this.shutdown();let e=[tn,this.options.instructions].filter(Boolean).join(`
|
|
33
|
+
`);for(let r of this.adapter.setup({instructions:e,tools:en,model:this.options.model,voice:this.options.voice,overrides:this.options.session}))this.transport.send(r);this.sink?.play("",24e3),this.el.addEventListener?.("three-live-select",this.onSelect);let t=A(this.el);t.length&&this.sendContext(ve(t));try{await this.mic?.start(this.adapter.inputRate,r=>{this.state!=="closed"&&this.transport.send(this.adapter.audio(r))})}catch(r){throw this.setState("error",{error:r?.message||String(r)}),this.close(),r}return this.setState("listening"),this}sendText(e){let t=String(e??"").trim().slice(0,O);if(!(!t||this.state==="closed"))for(let r of this.adapter.text(t,!0))this.transport.send(r)}close(){this.state!=="closed"&&(this.transport.onClose=void 0,this.transport.close(),this.shutdown())}shutdown(){this.state!=="closed"&&(this.mic?.stop(),this.sink?.close(),this.el.removeEventListener?.("three-live-select",this.onSelect),this.el.__tlVoiceSession===this&&(this.el.__tlVoiceSession=null),this.setState("closed"))}sendContext(e){if(this.state!=="closed")for(let t of this.adapter.text(e,!1))this.transport.send(t)}setState(e,t={}){this.state=e,_(this.el,{state:e,...t})}handle(e){if(this.state==="closed")return;let t=0;for(let r of this.adapter.parse(e))switch(r.type){case"audio":this.state!=="editing"&&(this.state="speaking"),this.sink?.play(r.base64,r.rate);break;case"interrupted":this.sink?.flush();break;case"transcript":r.speaker==="user"?(this.userText=(r.isFinal?r.text:this.userText+r.text).slice(-O),_(this.el,{state:this.state,transcript:this.userText,isFinal:r.isFinal}),r.isFinal&&(this.userText="")):(this.modelText=(r.isFinal?r.text:this.modelText+r.text).slice(-O),_(this.el,{state:this.state,reply:this.modelText,isFinal:r.isFinal}),r.isFinal&&(this.modelText=""));break;case"turnComplete":this.userText&&(_(this.el,{state:this.state,transcript:this.userText,isFinal:!0}),this.userText=""),this.modelText&&(_(this.el,{state:this.state,reply:this.modelText,isFinal:!0}),this.modelText=""),(this.state==="speaking"||this.state==="thinking")&&this.setState("listening");break;case"toolCall":++t<=Qr&&this.runTool(r.id,r.name,r.args);break;case"error":_(this.el,{state:this.state,error:r.message});break}}async runTool(e,t,r){let n;try{if(t==="edit_scene"){let i=S(r.instruction).trim().slice(0,O);if(!i)throw new Error("instruction is empty");if(typeof this.el.editScene!="function")throw new Error("scene editing is not available on this element");this.setState("editing",{transcript:i});let o=await this.el.editScene(i,{provider:this.options.editProvider});n=o.applied?{applied:!0,repairs:o.repairs}:{applied:!1,error:o.error??"edit rejected"},this.setState("thinking",{result:o})}else if(t==="select_meshes"){let i=S(r.mode),o=["replace","add","remove","clear"].includes(i)?i:"replace",a=Array.isArray(r.names)?r.names.filter(l=>typeof l=="string"):[],s=a.length&&o!=="clear"?Gt(this.el,a,o):await se(this.el,S(r.query),{mode:o,provider:this.options.editProvider??this.el.__tlSelectProvider?.()??null});n={changed:s.applied,selected:s.meshes.slice(0,50),say:s.say,...s.applied?{}:{question:s.say,candidates:s.match?.candidates??[]}}}else t==="undo_edit"?n=await this.el.undoEdit?.()??!1?{undone:!0}:{undone:!1,error:"nothing to undo"}:n={error:`unknown tool ${t}`}}catch(i){n={applied:!1,error:i?.message||String(i)},this.state==="editing"&&this.setState("thinking")}if(this.state!=="closed")for(let i of this.adapter.toolResult(e,t,n))this.transport.send(i)}};async function Ce(e,t){if(!t?.editProvider&&!e.aiProvider&&!e.getAttribute?.("ai-endpoint"))throw new Error("connectVoice: edits need a code model. Set el.aiProvider, the ai-endpoint attribute, or options.editProvider.");e.__tlVoiceSession?.close();let r=new Ht(e,t??{});return e.__tlVoiceSession=r,_(e,{state:"connecting"}),r.start()}function Jt(){let e=globalThis,t=e.SpeechRecognition??e.webkitSpeechRecognition;return t?{listen({lang:r,signal:n,onPartial:i}){return new Promise((o,a)=>{let s=new t;s.lang=r||globalThis.document?.documentElement?.lang||"en-US",s.interimResults=!0,s.continuous=!1,s.maxAlternatives=1;let l="",c="",u=!1,d=h=>{u||(u=!0,n?.removeEventListener("abort",m),h())},m=()=>{try{s.abort()}catch{}d(()=>a(n?.reason??new Error("voice edit aborted")))};if(s.onresult=h=>{let p="";for(let y=h.resultIndex;y<h.results.length;y++){let v=h.results[y];v.isFinal?l+=v[0].transcript:p+=v[0].transcript}c=(l+p).trim(),i?.(c)},s.onerror=h=>{h?.error==="no-speech"||h?.error==="aborted"||d(()=>a(new Error(`Speech recognition failed: ${h?.error||"unknown error"}`)))},s.onend=()=>d(()=>o((l||c).trim())),n?.aborted)return m();n?.addEventListener("abort",m,{once:!0});try{s.start()}catch(h){d(()=>a(h))}})}}:null}function ae(e,t){let r=globalThis;if(!(!r.speechSynthesis||!r.SpeechSynthesisUtterance))try{r.speechSynthesis.cancel();let n=new r.SpeechSynthesisUtterance(e);t&&(n.lang=t),r.speechSynthesis.speak(n)}catch{}}function Le(e){return String(e).replace(/([a-z])([A-Z])/g,"$1 $2").replace(/([A-Za-z])([0-9])/g,"$1 $2").replace(/[_\-.]+/g," ").replace(/\s+/g," ").trim()}function D(e,t="and"){let r=e.slice(0,5).map(Le),n=e.length-r.length;return n>0?`${r.join(", ")} ${t} ${n} more`:r.length<2?r.join(""):`${r.slice(0,-1).join(", ")} ${t} ${r[r.length-1]}`}function W(e,t,r,n){if(e==="clear")return"Selection cleared.";if(!t)return"Which object?";if(t.confidence==="high")return e==="remove"?r.length?`Removed ${D(t.names)}. ${r.length} selected.`:`Removed ${D(t.names)}. Nothing is selected.`:e==="add"?`Added ${D(t.names)}. ${r.length} selected.`:t.names.length===1?`Selected ${Le(t.names[0])}.`:`Selected ${t.names.length}: ${D(t.names)}.`;if(t.confidence==="ambiguous")return t.reason==="count_mismatch"?`I found ${t.candidates.length}: ${D(t.candidates)}. Which ones?`:`Which one: ${D(t.candidates,"or")}?`;let i=String(n).replace(/\s+/g," ").trim().slice(0,80);return t.reason==="relation"?`I can't tell which one "${i}" is yet. Try its name or color, or tap it.`:t.reason==="empty"?"Which object?":`I couldn't find "${i}".`}async function se(e,t,r={}){let n=r.mode??"replace",i=String(t??"").slice(0,O);if(n==="clear")return j(e,[],{mode:"clear",source:"voice",query:i||"clear"}),{mode:n,query:i,match:null,applied:!0,tier:1,meshes:[],say:W(n,null,[],i)};let o=n==="remove"?A(e):null;if(o&&!o.length)return{mode:n,query:i,match:null,applied:!1,tier:1,meshes:[],say:"Nothing is selected."};let a=at(e,i,{within:o}),s=1,l="",c=r.provider===void 0?e.__tlSelectProvider?.()??null:r.provider;if(c&&i.trim()&&a.confidence==="none"&&(a.reason==="relation"||a.reason==="no_match"))try{let h=await ct(e,i,c,{within:o,signal:r.signal});s=2,l=h.question,a=h.confidence==="high"?{ids:[],names:h.names,confidence:"high",candidates:[],reason:"model"}:{ids:[],names:[],confidence:h.names.length?"ambiguous":"none",candidates:h.names.slice(0,10),reason:"model"}}catch(h){if(r.signal?.aborted)throw h}let u=!1;a.confidence==="high"&&a.names.length&&(j(e,a.names,{mode:n,source:"voice",query:i}),u=!0);let d=A(e),m=!u&&l?l:W(n,a,d,i);return{mode:n,query:i,match:a,applied:u,tier:s,meshes:d,say:m}}function Gt(e,t,r="replace"){let n=new Set(Y(e).map(l=>l.name)),i=t.filter(l=>typeof l=="string"&&n.has(l)).slice(0,50),o=t.filter(l=>typeof l=="string").join(", ").slice(0,200);if(!i.length){let l={ids:[],names:[],confidence:"none",candidates:[],reason:"no_match"},c=A(e);return{mode:r,query:o,match:l,applied:!1,tier:1,meshes:c,say:W(r,l,c,o)}}j(e,i,{mode:r,source:"voice",query:o});let a={ids:[],names:i,confidence:"high",candidates:[],reason:"exact"},s=A(e);return{mode:r,query:o,match:a,applied:!0,tier:1,meshes:s,say:W(r,a,s,o)}}function Re(e){return e.applied?"Done.":`Sorry, I couldn't apply that: ${String(e.error??"the edit was rejected").replace(/\s+/g," ").slice(0,160)}`}async function hn(e,t,r){if(typeof e.editScene!="function")return;_(e,{state:"editing",transcript:t,isFinal:!0});let n;try{n=await e.editScene(t)}catch(i){n={applied:!1,code:"",repairs:0,warnings:[],error:i?.message||String(i)}}_(e,{state:"idle",transcript:t,isFinal:!0,result:n}),r.speak!==!1&&ae(Re(n),r.lang)}async function Ie(e,t={}){if(typeof e.editScene!="function")throw new Error("startVoiceEdit needs three-live-ai.min.js (el.editScene).");let r=t.recognizer??Jt();if(!r)throw new Error("startVoiceEdit: this browser has no speech recognition; use el.connectVoice() (ai-realtime or gemini-live) instead.");_(e,{state:"listening"});let n="";try{n=(await r.listen({lang:t.lang,signal:t.signal,onPartial:l=>_(e,{state:"listening",transcript:l,isFinal:!1})})).trim().slice(0,O)}catch(l){throw _(e,{state:"error",error:l?.message||String(l)}),l}if(!n){let l="I didn't catch that.";throw _(e,{state:"idle",transcript:n,isFinal:!0,error:l}),t.speak!==!1&&ae(l,t.lang),new Error(`startVoiceEdit: ${l}`)}let i=n,o,a=t.talkToSelect===!1?null:it(n);if(a&&a.kind!=="edit"){_(e,{state:"thinking",transcript:n,isFinal:!0});try{o=await se(e,a.kind==="clear"?"":a.query,{mode:a.mode,signal:t.signal})}catch(d){throw _(e,{state:"error",error:d?.message||String(d)}),d}let l=a.kind==="mixed"?a.instruction:void 0,c=l!==void 0&&o.applied&&o.tier===1,u=t.chips!==!1&&!e.__tlVoiceSession;if(u){let d=o;qt(e,d.applied||d.mode==="clear"?{pending:c?void 0:l}:d.match?.candidates.length?{question:d.say,candidates:d.match.candidates,mode:d.mode,query:d.query,pending:l}:{},m=>hn(e,m,t))}if(!c){if(_(e,{state:"idle",transcript:n,isFinal:!0,selection:o}),t.speak!==!1){let d=l!==void 0&&o.applied?u&&e.__tlChips?" Tap Apply to make the change.":" Say the change again to apply it.":"";ae(o.say+d,t.lang)}return{transcript:n,result:null,selection:o}}i=l}else t.chips!==!1&&e.__tlChips&&ie(e);_(e,{state:"editing",transcript:n,isFinal:!0,...o?{selection:o}:{}});let s;try{s=await e.editScene(i,{signal:t.signal})}catch(l){s={applied:!1,code:"",repairs:0,warnings:[],error:l?.message||String(l)}}if(_(e,{state:"idle",transcript:n,isFinal:!0,result:s,...o?{selection:o}:{}}),t.speak!==!1){let l=Re(s);ae(o?`${o.say} ${l}`:l,t.lang)}return{transcript:n,result:s,...o?{selection:o}:{}}}function Pe(e,t){if(!t){e.__tlTapHandler&&e.removeEventListener?.("meshclick",e.__tlTapHandler),e.__tlTapDown&&e.removeEventListener?.("pointerdown",e.__tlTapDown),e.__tlTapHandler=null,e.__tlTapDown=null;return}if(e.__tlTapHandler)return;let r=!1,n=0,i=o=>{let a=o;r=!!(a.shiftKey||a.ctrlKey||a.metaKey),n=Date.now()};e.__tlTapDown=i,e.addEventListener?.("pointerdown",i),e.__tlTapHandler=o=>{let a=o.detail?.meshName;if(typeof a!="string"||!a)return;let s=n>0&&Date.now()-n>=Xr,l=A(e);r||s?j(e,[a],{mode:"toggle",source:"tap"}):l.length===1&&l[0]===a?j(e,[],{mode:"clear",source:"tap"}):j(e,[a],{mode:"replace",source:"tap"}),r=!1,n=0},e.addEventListener?.("meshclick",e.__tlTapHandler)}var dn='<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true" fill="currentColor"><path d="M12 14a3 3 0 0 0 3-3V5a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3zm5-3a5 5 0 0 1-10 0H5a7 7 0 0 0 6 6.92V21h2v-3.08A7 7 0 0 0 19 11h-2z"/></svg>';function pn(e){if(e.__tlVoiceButton||typeof document>"u")return;let t=e.querySelector?.('[slot="voice-button"]'),r=t??document.createElement("button");t||(r.setAttribute("type","button"),r.setAttribute("aria-label","Edit the scene by voice"),r.innerHTML=dn,Object.assign(r.style,{position:"absolute",left:"12px",bottom:"12px",width:"44px",height:"44px",borderRadius:"22px",border:"none",background:"rgba(32,33,36,0.85)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer",zIndex:"10",boxShadow:"0 1px 4px rgba(0,0,0,0.3)"}),e.appendChild?.(r)),e.__tlVoiceButton=r;let n=null,i=o=>{r.setAttribute("aria-pressed",o?"true":"false"),t||(r.style.background=o?"rgba(217,48,37,0.9)":"rgba(32,33,36,0.85)")};r.addEventListener("click",o=>{o.stopPropagation();let a=e.getAttribute?.("ai-voice-url");if(a){if(e.__tlVoiceSession){e.__tlVoiceSession.close(),i(!1);return}i(!0);let s=e.getAttribute?.("ai-voice-provider")||"ai-realtime";Ce(e,{provider:s,url:a,model:e.getAttribute?.("ai-voice-model")||void 0}).then(l=>{e.addEventListener?.("three-live-voice",function c(u){u.detail?.state==="closed"&&(e.removeEventListener?.("three-live-voice",c),e.__tlVoiceSession!==l&&i(!1))})}).catch(()=>i(!1));return}if(n){n.abort(new Error("voice edit cancelled"));return}n=new AbortController,i(!0),Ie(e,{signal:n.signal}).catch(()=>{}).finally(()=>{n=null,i(!1)})})}var Yt=1048576,Zt=20,Kt=15e3,mn=["You edit Three.js scene scripts for Three-Live, which compiles them to glTF for Web, Android, and iOS.","Reply with the COMPLETE updated script in a single ```js code block and nothing else.","Rules:","- Use the pre-defined globals `THREE`, `scene`, and `camera`. No imports, renderer, loaders, DOM, network, or timers.","- Change only what the instruction asks; keep every other object, name, and value as it is.","- Give new meshes and groups descriptive `.name` values; keep existing names stable.","- Keep objects resting on y = 0 and within view of the camera.","- Supported: core geometries and materials (MeshStandardMaterial / MeshPhysicalMaterial with PBR maps, and MeshStandardNodeMaterial with loop-free THREE.TSL colorNode / emissiveNode / roughnessNode / metalnessNode / opacityNode / bumpNode / positionNode), lights, Group, InstancedMesh, THREE.CSG, THREE.CanvasTexture, THREE.ProceduralTexture presets (wood, marble, brick, concrete, brushedMetal, rust, leather, fabric, carbonFiber, checkerboard, grid), and declarative mesh.userData.xr traits. Optional per-frame motion: function animate(t) { ... }.","- Keep every userData.xr.material preset; add a prompt (AI texture) only when the instruction asks for a specific surface."].join(`
|
|
34
|
+
`);function fn(e){try{let t=new URL(e);return t.protocol==="https:"?!0:t.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(t.hostname)}catch{return!1}}function Xt(e){let t=String(e?.endpoint??"");if(!fn(t))throw new Error("ai-chat: endpoint must be an https:// URL (or http:// on localhost).");let r=e.fetch??globalThis.fetch?.bind(globalThis);if(typeof r!="function")throw new Error("ai-chat: no fetch() implementation is available.");let n=Number.isFinite(e.temperature)?Number(e.temperature):.2,i=Math.max(1e3,Number(e.timeoutMs)||12e4);return{async complete({messages:o,signal:a}){let s=new AbortController,l=()=>s.abort(a?.reason);a?.aborted&&l(),a?.addEventListener("abort",l,{once:!0});let c=setTimeout(()=>s.abort(new Error(`ai-chat: timed out after ${i} ms`)),i);try{let u={messages:o,temperature:n,stream:!1};e.model&&(u.model=e.model);let d=await r(t,{method:"POST",headers:{"Content-Type":"application/json",...e.headers},body:JSON.stringify(u),signal:s.signal}),m=await d.text();if(!d.ok)throw new Error(`ai-chat: HTTP ${d.status} from endpoint: ${m.slice(0,300)}`);let h;try{h=JSON.parse(m)}catch{throw new Error("ai-chat: endpoint returned non-JSON content.")}let p=h?.choices?.[0]?.message?.content,y=Array.isArray(p)?p.map(v=>typeof v=="string"?v:String(v?.text??"")).join(""):p;if(typeof y!="string"||!y.trim())throw new Error("ai-chat: response had no message content.");return y}finally{clearTimeout(c),a?.removeEventListener("abort",l)}}}}function Ne(e){let t=String(e??""),r=/```[ \t]*([A-Za-z0-9_+-]*)[^\n]*\n([\s\S]*?)```/g,n,i="";for(;n=r.exec(t);){let a=n[1].toLowerCase();if(i||(i=n[2]),["js","javascript","ts","typescript","mjs"].includes(a))return n[2].trim()}if(i)return i.trim();let o=/```[^\n]*\n([\s\S]*)$/.exec(t);return o?o[1].trim():/\bscene\b/.test(t)?t.trim():""}function Qt(e){let t=new Set,r=e?.gltf;if(!r&&e instanceof ArrayBuffer&&e.byteLength>=20){let o=new DataView(e);if(o.getUint32(0,!0)===1179937895){let a=o.getUint32(12,!0);try{r=JSON.parse(new TextDecoder().decode(new Uint8Array(e,20,a)))}catch{}}}let n=0,i=0;for(let o of r?.nodes??[]){let a=typeof o?.name=="string"?o.name.trim():"",s=/^Node_\d+$/.test(a),l=a.length>0&&!s;l&&t.add(a),o?.mesh!==void 0&&n++,!l&&(o?.extras?.xr!=null||o?.extras?.interactivity!=null)&&i++}return{meshNames:t,meshes:n,unnamedXrNodes:i}}function er(e,t,r,n="editScene"){let i=P().scriptRunner;if(!i||typeof i.execute!="function")return Promise.reject(new Error(`${n} needs a script runner (load three-live.min.js or three-live-full.min.js).`));let o=[];return new Promise((a,s)=>{let l,c=!1,u=h=>{if(!c){c=!0,clearTimeout(m),r?.removeEventListener("abort",d);try{l?.terminate?.()}catch{}h()}},d=()=>u(()=>s(r?.reason??new Error("editScene aborted"))),m=setTimeout(()=>u(()=>s(new Error(`Scene did not finish compiling within ${Kt} ms.`))),Kt);if(r?.aborted)return d();r?.addEventListener("abort",d,{once:!0}),Promise.resolve(i.execute(t,e.sandbox??"worker",{onModelReady:h=>u(()=>a({model:h,warnings:o})),onError:h=>u(()=>s(h instanceof Error?h:new Error(String(h)))),onToast:h=>{o.length<20&&o.push(String(h))}},{allowClientScripts:!0})).then(h=>{if(l=h,c)try{h?.terminate?.()}catch{}},h=>u(()=>s(h instanceof Error?h:new Error(String(h)))))})}var gn=50;function yn(e){let t=typeof e=="string"?[e]:Array.isArray(e)?e:[],r=[];for(let n of t){let i=typeof n=="string"?n.slice(0,200):"";if(i&&!r.includes(i)&&r.push(i),r.length>=gn)break}return r}function bn(e){return e.length===0?"":e.length===1?`The user selected the mesh named "${e[0]}"; the instruction refers to it.
|
|
35
|
+
`:`The user selected the meshes named ${e.map(t=>`"${t}"`).join(", ")}; the instruction refers to them.
|
|
36
|
+
`}function Fe(e,t,r="editScene"){let n=t.provider??e.aiProvider;if(n&&typeof n.complete=="function")return n;let i=e.getAttribute?.("ai-endpoint");if(!i)throw new Error(`${r}: no AI provider. Set el.aiProvider, or the ai-endpoint (and ai-model) attributes.`);let o=(e.getAttribute?.("ai-provider")||"ai-chat").toLowerCase();if(o!=="ai-chat")throw new Error(`${r}: unknown ai-provider '${o}' (supported: ai-chat, or set el.aiProvider).`);return Xt({endpoint:i,model:e.getAttribute?.("ai-model")||void 0})}function Ue(e){return typeof e.__tlAiSource=="string"?e.__tlAiSource:e.querySelector?.('script[type="text/three"], script[type="text/three-live"]')?.textContent??""}async function tr(e,t,r={}){let n=String(t??"").trim();if(!n)throw new Error("editScene: instruction is empty.");if(!e?.allowClientScripts)throw new Error("editScene requires client scripts: add the allow-client-scripts attribute.");let i=r.source??Ue(e);if(!i.trim())throw new Error('editScene needs a Three.js scene script (inline <script type="text/three"> or a prior editScene/executeScript).');let o=Fe(e,r);e.__tlAiAbort?.abort(new Error("editScene superseded by a newer call"));let a=new AbortController;e.__tlAiAbort=a;let s=()=>a.abort(r.signal?.reason);r.signal?.aborted&&s(),r.signal?.addEventListener("abort",s,{once:!0});let l=a.signal,c=yn(r.selection===void 0?e.selectedMeshes??e.selectedMesh:r.selection),u=c.length>1?c:c[0]??null,d=Math.max(0,Math.min(3,Math.floor(Number(r.maxRepairs??1))||0)),m=[{role:"system",content:mn},{role:"user",content:`Current scene script:
|
|
37
|
+
\`\`\`js
|
|
38
|
+
${i}
|
|
39
|
+
\`\`\`
|
|
40
|
+
`+bn(c)+`Instruction: ${n}`}];e.fireEvent?.("three-live-edit-start",{instruction:n,selection:u});let h="",p="",y=[],v=0;try{for(let g=0;g<=d;g++){let x=await o.complete({messages:m,signal:l});if(l.aborted)throw l.reason;if(h=Ne(x),!h)p="The model reply did not contain a scene script.";else if(new TextEncoder().encode(h).length>Yt)p="The generated script exceeds the 1 MB script limit.";else try{let w=await er(e,h,l);y=w.warnings;let b=Qt(w.model);if(b.meshes===0)p="The edited scene contains no meshes.";else if(c.some(k=>!b.meshNames.has(k))&&!/\b(remove|delete|rename)\b/i.test(n)){let k=c.filter(T=>!b.meshNames.has(T));p=k.length===1?`The edited scene no longer contains the selected mesh "${k[0]}".`:`The edited scene no longer contains the selected meshes ${k.map(T=>`"${T}"`).join(", ")}.`}else p=""}catch(w){if(l.aborted)throw l.reason;let b=w,k=String(b?.message||w);p=b?.name&&b.name!=="Error"&&!k.startsWith(b.name)?`${b.name}: ${k}`:k}if(!p)break;g<d&&(v++,m.push({role:"assistant",content:x},{role:"user",content:`That script was rejected by the Three-Live sandbox: ${p}
|
|
41
|
+
Return the complete corrected script in a single \`\`\`js code block.`}))}}finally{r.signal?.removeEventListener("abort",s),e.__tlAiAbort===a&&(e.__tlAiAbort=null)}if(!p&&!r.dryRun)try{await e.executeScript(h);let g=e.__tlAiHistory??(e.__tlAiHistory=[]);g.push(i),g.length>Zt&&g.shift(),e.__tlAiSource=h}catch(g){p=`The edit compiled but failed to load: ${g?.message||String(g)}`,e.__tlAiSource=i,e.executeScript(i).catch(()=>{})}let f={applied:!p,code:h,repairs:v,warnings:y,...p?{error:p}:{}};return e.fireEvent?.("three-live-edit",{instruction:n,...f}),f}async function rr(e){let t=e?.__tlAiHistory?.pop();return typeof t!="string"?!1:(e.__tlAiSource=t,await e.executeScript(t),!0)}var Oe={name:"ai",onRegister(){typeof document>"u"||document.querySelectorAll?.("three-live, xr-live").forEach(e=>Oe.onElementConnected?.(e))},onElementConnected(e){let t=e;if(!t||t.__tlAiInstalled)return;t.__tlAiInstalled=!0,t.editScene=(a,s)=>tr(t,a,s),t.compileFromImage=(a,s)=>bt(t,a,s),t.undoEdit=()=>rr(t),Er(t);let r=t;t.startVoiceEdit=a=>Ie(r,a),r.__tlSelectProvider=()=>{try{return Fe(t,{})}catch{return null}},t.selectByVoice=(a,s)=>se(r,a,s),t.connectVoice=a=>Ce(r,a),t.setSelectOnTap=a=>Pe(r,a),t.hasAttribute?.("ai-select-on-tap")&&Pe(r,!0),t.hasAttribute?.("ai-voice-button")&&pn(r),"sceneSource"in t||Object.defineProperty(t,"sceneSource",{get:()=>Ue(t),configurable:!0});let n=t;Zr(n);let i=t.executeScript;typeof i=="function"&&(t.executeScript=a=>(Ut(n,{kind:"script",value:String(a)}),Ct(n,()=>i.call(t,a)).then(s=>(t.__tlAiSource=String(a),Ft(n,s),s))));let o=n.loadScriptFromSrc;typeof o=="function"&&(n.loadScriptFromSrc=a=>(Ut(n,{kind:"url",value:String(a)}),Ct(n,()=>o.call(t,a)).then(s=>(Ft(n,s),s))))}};return cr(Oe),lr(De)})();
|