diff --git a/core/wrapper-inline.js b/core/wrapper-inline.js new file mode 100644 index 0000000..16f634c --- /dev/null +++ b/core/wrapper-inline.js @@ -0,0 +1,240 @@ +// core/wrapper-inline.js +// iframe 内部注入脚本,同步执行,避免外部加载的时序问题 + +/** + * 基础脚本:高度测量 + STscript + * 两个渲染器共用 + */ +export function getIframeBaseScript() { + return ` +(function(){ + function measureVisibleHeight(){ + try{ + var doc=document,target=doc.body; + if(!target)return 0; + var minTop=Infinity,maxBottom=0; + var addRect=function(el){ + try{ + var r=el.getBoundingClientRect(); + if(r&&r.height>0){ + if(minTop>r.top)minTop=r.top; + if(maxBottom0?Math.ceil(maxBottom-Math.min(minTop,0)):(target.scrollHeight||0); + }catch(e){ + return(document.body&&document.body.scrollHeight)||0; + } + } + + function post(m){try{parent.postMessage(m,'*')}catch(e){}} + var rafPending=false,lastH=0,HYSTERESIS=2; + + function send(force){ + if(rafPending&&!force)return; + rafPending=true; + requestAnimationFrame(function(){ + rafPending=false; + var h=measureVisibleHeight(); + if(force||Math.abs(h-lastH)>=HYSTERESIS){ + lastH=h; + post({height:h,force:!!force}); + } + }); + } + + try{send(true)}catch(e){} + document.addEventListener('DOMContentLoaded',function(){send(true)},{once:true}); + window.addEventListener('load',function(){send(true)},{once:true}); + + try{ + if(document.fonts){ + document.fonts.ready.then(function(){send(true)}).catch(function(){}); + if(document.fonts.addEventListener){ + document.fonts.addEventListener('loadingdone',function(){send(true)}); + document.fonts.addEventListener('loadingerror',function(){send(true)}); + } + } + }catch(e){} + + ['transitionend','animationend'].forEach(function(evt){ + document.addEventListener(evt,function(){send(false)},{passive:true,capture:true}); + }); + + try{ + var root=document.body||document.documentElement; + var ro=new ResizeObserver(function(){send(false)}); + ro.observe(root); + }catch(e){ + try{ + var rootMO=document.body||document.documentElement; + new MutationObserver(function(){send(false)}) + .observe(rootMO,{childList:true,subtree:true,attributes:true,characterData:true}); + }catch(e){} + window.addEventListener('resize',function(){send(false)},{passive:true}); + } + + window.addEventListener('message',function(e){ + var d=e&&e.data||{}; + if(d&&d.type==='probe')setTimeout(function(){send(true)},10); + }); + + window.STscript=function(command){ + return new Promise(function(resolve,reject){ + try{ + if(!command){reject(new Error('empty'));return} + if(command[0]!=='/')command='/'+command; + var id=Date.now().toString(36)+Math.random().toString(36).slice(2); + function onMessage(e){ + var d=e&&e.data||{}; + if(d.source!=='xiaobaix-host')return; + if((d.type==='commandResult'||d.type==='commandError')&&d.id===id){ + try{window.removeEventListener('message',onMessage)}catch(e){} + if(d.type==='commandResult')resolve(d.result); + else reject(new Error(d.error||'error')); + } + } + try{window.addEventListener('message',onMessage)}catch(e){} + post({type:'runCommand',id:id,command:command}); + setTimeout(function(){ + try{window.removeEventListener('message',onMessage)}catch(e){} + reject(new Error('Command timeout')); + },180000); + }catch(e){reject(e)} + }); + }; + try{if(typeof window['stscript']!=='function')window['stscript']=window.STscript}catch(e){} +})();`; +} + +/** + * CallGenerate + Avatar + * 提供 callGenerate() 函数供角色卡调用 + */ +export function getWrapperScript() { + return ` +(function(){ + function sanitizeOptions(options){ + try{ + return JSON.parse(JSON.stringify(options,function(k,v){return(typeof v==='function')?undefined:v})) + }catch(_){ + try{ + var seen=new WeakSet(); + var clone=function(val){ + if(val===null||val===undefined)return val; + var t=typeof val; + if(t==='function')return undefined; + if(t!=='object')return val; + if(seen.has(val))return undefined; + seen.add(val); + if(Array.isArray(val)){ + var arr=[];for(var i=0;i