mirage-engine 0.2.13 → 0.2.14

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.
@@ -477,7 +477,8 @@ class P {
477
477
  }
478
478
  }
479
479
  class Y {
480
- constructor(t, e = {}) {
480
+ // private _engine: Engine | Traveler;
481
+ constructor(t, e) {
481
482
  c(this, "_engine");
482
483
  if (!t)
483
484
  throw new Error("[Mirage] Target element is required.");
@@ -489,9 +490,6 @@ class Y {
489
490
  stop() {
490
491
  this._engine.stop();
491
492
  }
492
- /**
493
- * 엔진 종료 및 메모리 해제
494
- */
495
493
  destroy() {
496
494
  this._engine.dispose();
497
495
  }
@@ -1,5 +1,5 @@
1
1
  (function(p,f){typeof exports=="object"&&typeof module!="undefined"?f(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],f):(p=typeof globalThis!="undefined"?globalThis:p||self,f(p.MirageEngine={},p.THREE))})(this,function(p,f){"use strict";var Y=Object.defineProperty;var U=(p,f,w)=>f in p?Y(p,f,{enumerable:!0,configurable:!0,writable:!0,value:w}):p[f]=w;var c=(p,f,w)=>(U(p,typeof f!="symbol"?f+"":f,w),w);function w(r){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(r){for(const e in r)if(e!=="default"){const i=Object.getOwnPropertyDescriptor(r,e);Object.defineProperty(t,e,i.get?i:{enumerable:!0,get:()=>r[e]})}}return t.default=r,Object.freeze(t)}const h=w(f),T=0,C=1,S=2,z=4,E=8,B=16;function F(r,t,e){const i=t.split(`
2
- `),s=[];return i.forEach(n=>{const a=n.split(" ");let o=a[0];for(let l=1;l<a.length;l++){const d=a[l];r.measureText(o+" "+d).width<e?o+=" "+d:(s.push(o),o=d)}s.push(o)}),s}function O(r,t,e,i,s=2){const n=document.createElement("canvas"),a=n.getContext("2d");if(!a)throw new Error("[Mirage] Failed to create canvas context");const l=(window.devicePixelRatio||1)*s;n.width=e*l,n.height=i*l,a.scale(l,l),a.font=t.font,a.fillStyle=t.color,a.textBaseline="top",a.globalAlpha=1;const d=F(a,r,e),m=t.lineHeight;d.forEach((g,y)=>{const x=y*m+2;let v=0;t.textAlign==="center"?v=e/2:t.textAlign==="right"&&(v=e),a.textAlign=t.textAlign,a.fillText(g,v,x)});const u=new h.CanvasTexture(n);return u.colorSpace=h.SRGBColorSpace,u.minFilter=h.LinearFilter,u.magFilter=h.LinearFilter,u.needsUpdate=!0,u}function b(r){return typeof r=="number"?r:parseFloat(r)||0}const A=`
2
+ `),s=[];return i.forEach(n=>{const o=n.split(" ");let a=o[0];for(let l=1;l<o.length;l++){const d=o[l];r.measureText(a+" "+d).width<e?a+=" "+d:(s.push(a),a=d)}s.push(a)}),s}function O(r,t,e,i,s=2){const n=document.createElement("canvas"),o=n.getContext("2d");if(!o)throw new Error("[Mirage] Failed to create canvas context");const l=(window.devicePixelRatio||1)*s;n.width=e*l,n.height=i*l,o.scale(l,l),o.font=t.font,o.fillStyle=t.color,o.textBaseline="top",o.globalAlpha=1;const d=F(o,r,e),m=t.lineHeight;d.forEach((g,y)=>{const x=y*m+2;let v=0;t.textAlign==="center"?v=e/2:t.textAlign==="right"&&(v=e),o.textAlign=t.textAlign,o.fillText(g,v,x)});const u=new h.CanvasTexture(n);return u.colorSpace=h.SRGBColorSpace,u.minFilter=h.LinearFilter,u.magFilter=h.LinearFilter,u.needsUpdate=!0,u}function b(r){return typeof r=="number"?r:parseFloat(r)||0}const A=`
3
3
  varying vec2 vUv;
4
4
  void main() {
5
5
  vUv = uv;
@@ -52,4 +52,4 @@
52
52
 
53
53
  gl_FragColor = vec4(color, finalOpacity);
54
54
  }
55
- `;function R(r){if(!r||r==="transparent")return{color:new h.Color(16777215),alpha:0};const t=r.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(t){const e=parseInt(t[1],10),i=parseInt(t[2],10),s=parseInt(t[3],10),n=t[4]!==void 0?parseFloat(t[4]):1;return{color:new h.Color(`rgb(${e}, ${i}, ${s})`),alpha:n}}return{color:new h.Color(r),alpha:1}}function L(r,t,e){var o;const i=R(r.backgroundColor),s=R(r.borderColor),n={uSize:{value:new h.Vector2(t,e)},uRadius:{value:b(r.borderRadius)},uBorderWidth:{value:b(r.borderWidth)},uColor:{value:i.color},uBorderColor:{value:s.color},uOpacity:{value:(o=r.opacity)!=null?o:1},uBgOpacity:{value:i.alpha}};return new h.ShaderMaterial({uniforms:n,vertexShader:A,fragmentShader:I,transparent:!0,side:h.FrontSide})}function X(r,t,e,i){var a;const s=R(t.backgroundColor),n=R(t.borderColor);r.uniforms.uSize.value.set(e,i),r.uniforms.uRadius.value=b(t.borderRadius),r.uniforms.uBorderWidth.value=b(t.borderWidth),r.uniforms.uColor.value.copy(s.color),r.uniforms.uBorderColor.value.copy(n.color),r.uniforms.uOpacity.value=(a=t.opacity)!=null?a:1,r.uniforms.uBgOpacity.value=s.alpha}const M={create(r,t,e,i,s,n=2){if(r==="BOX")return L(t,i,s);if(r==="TEXT"){const a=O(e||"",t,i,s,n);return new h.MeshBasicMaterial({map:a,transparent:!0,side:h.FrontSide,color:16777215})}return new h.MeshBasicMaterial({visible:!1})},update(r,t,e,i,s,n,a=2){if(t==="BOX")X(r,e,s,n);else if(t==="TEXT"){const o=r;o.map&&o.map.dispose();const l=O(i||"",e,s,n,a);o.map=l,o.needsUpdate=!0}}};class N{constructor(t,e,i){c(this,"canvas");c(this,"scene");c(this,"camera");c(this,"renderer");c(this,"renderOrder",0);c(this,"textQualityFactor",2);c(this,"mode","overlay");c(this,"customZIndex","9999");c(this,"target");c(this,"mountContainer");c(this,"targetRect");c(this,"meshMap",new Map);var a,o,l;this.target=t,this.mountContainer=i,this.mode=(a=e.mode)!=null?a:"overlay",(o=e.style)!=null&&o.zIndex&&(this.customZIndex=e.style.zIndex),this.canvas=document.createElement("canvas"),this.scene=new h.Scene,this.targetRect=this.target.getBoundingClientRect();const s=this.targetRect.width,n=this.targetRect.height;this.camera=new h.OrthographicCamera(s/-2,s/2,n/2,n/-2,1,1e3),this.camera.position.z=100,this.renderer=new h.WebGLRenderer({canvas:this.canvas,alpha:!0,antialias:!0}),this.renderer.setPixelRatio(window.devicePixelRatio),this.renderer.setSize(s,n),this.applyTextQuality((l=e.textQuality)!=null?l:"medium")}applyTextQuality(t){if(typeof t=="number"){this.textQualityFactor=Math.max(.1,t);return}switch(t){case"low":this.textQualityFactor=1;break;case"high":this.textQualityFactor=4;break;case"medium":default:this.textQualityFactor=2;break}}mount(){this.mountContainer.appendChild(this.canvas),this.canvas.style.zIndex=this.customZIndex,this.canvas.style.pointerEvents=this.mode==="overlay"?"none":"auto",this.updateCanvasLayout()}updateCanvasLayout(){this.canvas.style.width=`${this.targetRect.width}px`,this.canvas.style.height=`${this.targetRect.height}px`,this.mode==="duplicate"?(this.canvas.style.position="",this.canvas.style.top="",this.canvas.style.left="",this.canvas.style.display="block"):(this.canvas.style.position="absolute",this.canvas.style.top=`${this.target.offsetTop}px`,this.canvas.style.left=`${this.target.offsetLeft}px`,this.canvas.style.display="block")}dispose(){this.renderer.dispose(),this.canvas.remove()}setSize(t,e){this.renderer.setSize(t,e),this.camera.left=t/-2,this.camera.right=t/2,this.camera.top=e/2,this.camera.bottom=e/-2,this.camera.updateProjectionMatrix()}syncScene(t){const e=this.target.getBoundingClientRect(),i=Math.abs(e.width-this.targetRect.width)>.1||Math.abs(e.height-this.targetRect.height)>.1,s=this.mode==="overlay"&&(Math.abs(e.top-this.targetRect.top)>.1||Math.abs(e.left-this.targetRect.left)>.1);i?(this.targetRect=e,this.renderer.setSize(this.targetRect.width,this.targetRect.height),this.camera.left=this.targetRect.width/-2,this.camera.right=this.targetRect.width/2,this.camera.top=this.targetRect.height/2,this.camera.bottom=this.targetRect.height/-2,this.camera.updateProjectionMatrix(),this.updateCanvasLayout()):s?(this.targetRect=e,this.updateCanvasLayout()):this.targetRect=e,this.renderOrder=0;const n=new Set;this.reconcileNode(t,n);for(const[a,o]of this.meshMap.entries())n.has(a)||(this.scene.remove(o),o.geometry.dispose(),o.material instanceof h.Material&&o.material.dispose(),this.meshMap.delete(a))}reconcileNode(t,e){e.add(t.element);let i=this.meshMap.get(t.element);if(!i){const s=new h.PlaneGeometry(1,1),n=M.create("BOX",t.styles,"",t.rect.width,t.rect.height);i=new h.Mesh(s,n),t.type==="TEXT"&&(i.name="BG_MESH"),this.scene.add(i),this.meshMap.set(t.element,i)}if(i.userData.domRect=t.rect,this.updateMeshProperties(i,t),t.type==="BOX")for(const s of t.children)this.reconcileNode(s,e);else t.type==="TEXT"&&this.reconcileTextChild(i,t)}reconcileTextChild(t,e){var o,l;let i=t.children.find(d=>d.name==="TEXT_CHILD");const s=JSON.stringify(e.textStyles),n=(o=i==null?void 0:i.userData)==null?void 0:o.styleHash;if(!i||e.dirtyMask&B||s!==n){i&&((l=i.material.map)==null||l.dispose(),i.geometry.dispose(),t.remove(i));const d=M.create("TEXT",e.textStyles,e.textContent||"",e.rect.width,e.rect.height,this.textQualityFactor),m=new h.PlaneGeometry(1,1);i=new h.Mesh(m,d),i.name="TEXT_CHILD",i.userData={styleHash:s},i.position.z=.005,t.add(i)}if(i){const d=t.userData.domRect,m=d.x+d.width/2,u=d.y+d.height/2,g=e.rect.x+e.rect.width/2,y=e.rect.y+e.rect.height/2,x=g-m,v=-(y-u);i.position.set(x,v,.005)}}updateMeshProperties(t,e){const{rect:i,styles:s}=e,n=this.renderer.getPixelRatio(),a=this.renderer.domElement.width/n,o=this.renderer.domElement.height/n;t.scale.set(i.width,i.height,1);const l=.001;this.renderOrder++;const d=this.targetRect.left+window.scrollX,m=this.targetRect.top+window.scrollY,u=i.x-d,g=i.y-m;t.position.set(u-a/2+i.width/2,-g+o/2-i.height/2,s.zIndex+this.renderOrder*l),M.update(t.material,"BOX",e.styles,"",e.rect.width,e.rect.height)}render(){this.renderer.render(this.scene,this.camera)}}function _(r){const t=document.createRange();t.selectNodeContents(r);const e=t.getBoundingClientRect();return{left:e.left,top:e.top,width:e.width,height:e.height}}function k(r){const t=parseFloat(r.fontSize);let e=parseFloat(r.lineHeight);isNaN(e)&&(e=t*1.2);let i=parseFloat(r.letterSpacing);return isNaN(i)&&(i=0),{font:`${r.fontStyle} ${r.fontWeight} ${r.fontSize} ${r.fontFamily}`,color:r.color,textAlign:r.textAlign||"start",textBaseline:"alphabetic",direction:r.direction||"inherit",lineHeight:e,letterSpacing:i}}function D(r,t=C|S|z|B|E){if(r.nodeType===Node.TEXT_NODE){const u=r;if(!u.textContent||!u.textContent.trim())return null;const g=u.textContent.replace(/\s+/g," ").trim();if(g.length===0)return null;const y=_(u);if(y.width===0||y.height===0)return null;const x=u.parentElement,v=x?window.getComputedStyle(x):null;return v?{id:Math.random().toString(36).substring(2,9),type:"TEXT",element:u,rect:{x:y.left+window.scrollX,y:y.top+window.scrollY,width:y.width,height:y.height},styles:{backgroundColor:"transparent",opacity:parseFloat(v.opacity),zIndex:0,borderRadius:"0px",borderColor:"transparent",borderWidth:"0px"},textContent:g,textStyles:k(v),dirtyMask:t,children:[]}:null}const e=r,i=e.getBoundingClientRect(),s=window.getComputedStyle(e);if(i.width===0||i.height===0||s.display==="none")return null;let n=e.getAttribute("data-mid");n||(n=Math.random().toString(36).substring(2,11),e.setAttribute("data-mid",n));const a=parseInt(s.zIndex),o={backgroundColor:s.backgroundColor,opacity:parseFloat(s.opacity),zIndex:isNaN(a)?0:a,borderRadius:s.borderRadius,borderColor:s.borderColor,borderWidth:s.borderWidth};let l,d;const m=[];return Array.from(e.childNodes).forEach(u=>{const g=D(u,t);g&&m.push(g)}),{id:n,type:"BOX",element:e,rect:{x:i.left+window.scrollX,y:i.top+window.scrollY,width:i.width,height:i.height},styles:o,textContent:l,textStyles:d,dirtyMask:t,children:m}}class P{constructor(t,e){c(this,"target");c(this,"renderer");c(this,"observer");c(this,"isDomDirty",!1);c(this,"isRunning",!1);c(this,"pendingMask",T);c(this,"mutationTimer",null);c(this,"cssTimer",null);c(this,"onTransitionFinished",t=>{this.target.contains(t.target)&&this.mutationTimer===null&&(this.cssTimer&&clearTimeout(this.cssTimer),this.pendingMask|=C|S,this.cssTimer=window.setTimeout(()=>{this.isDomDirty=!0,this.cssTimer=null},50))});c(this,"onWindowResize",()=>{this.renderer.setSize(window.innerWidth,window.innerHeight),this.isDomDirty=!0});c(this,"renderLoop",()=>{this.isRunning&&(this.isDomDirty&&this.forceUpdateScene(),this.renderer.render(),requestAnimationFrame(this.renderLoop))});this.target=t,this.renderer=e,this.observer=new MutationObserver(i=>{let s=T;for(const n of i)n.type==="childList"?s|=E:n.type==="attributes"&&(n.attributeName==="style"||n.attributeName==="class")&&(s|=C|S);if(s!==T){if(this.pendingMask|=s,s&E){this.clearTimers(),console.log("Structural Change detected"),this.isDomDirty=!0;return}this.mutationTimer&&clearTimeout(this.mutationTimer),this.mutationTimer=window.setTimeout(()=>{this.mutationTimer=null,this.isDomDirty=!0},200)}})}start(){this.isRunning||(this.isRunning=!0,this.observer.observe(this.target,{childList:!0,subtree:!0,attributes:!0,characterData:!0}),this.target.addEventListener("transitionend",this.onTransitionFinished),this.target.addEventListener("animationend",this.onTransitionFinished),window.addEventListener("resize",this.onWindowResize),this.forceUpdateScene(),this.renderLoop())}stop(){this.isRunning=!1,this.observer.disconnect(),this.clearTimers(),this.target.removeEventListener("transitionend",this.onTransitionFinished),this.target.removeEventListener("animationend",this.onTransitionFinished),window.removeEventListener("resize",this.onWindowResize)}clearTimers(){this.mutationTimer&&(clearTimeout(this.mutationTimer),this.mutationTimer=null),this.cssTimer&&(clearTimeout(this.cssTimer),this.cssTimer=null)}forceUpdateScene(){this.isDomDirty=!1;const t=D(this.target,this.pendingMask);t&&this.renderer.syncScene(t),this.pendingMask=T}}class H{constructor(t,e){c(this,"renderer");c(this,"syncer");c(this,"target");var s,n,a;this.target=t;let i;if(e.mode==="duplicate"?i=(n=(s=e.container)!=null?s:this.target.parentElement)!=null?n:void 0:i=(a=this.target.parentElement)!=null?a:void 0,!i)throw new Error("[Mirage] Cannot find a container (parent or option).");this.renderer=new N(this.target,e,i),this.renderer.mount(),this.syncer=new P(this.target,this.renderer)}start(){this.syncer.start()}stop(){this.syncer.stop()}dispose(){this.syncer.stop(),this.renderer.dispose()}}class W{constructor(t,e={}){c(this,"_engine");if(!t)throw new Error("[Mirage] Target element is required.");this._engine=new H(t,e)}start(){this._engine.start()}stop(){this._engine.stop()}destroy(){this._engine.dispose()}}p.Mirage=W,Object.defineProperty(p,Symbol.toStringTag,{value:"Module"})});
55
+ `;function R(r){if(!r||r==="transparent")return{color:new h.Color(16777215),alpha:0};const t=r.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(t){const e=parseInt(t[1],10),i=parseInt(t[2],10),s=parseInt(t[3],10),n=t[4]!==void 0?parseFloat(t[4]):1;return{color:new h.Color(`rgb(${e}, ${i}, ${s})`),alpha:n}}return{color:new h.Color(r),alpha:1}}function L(r,t,e){var a;const i=R(r.backgroundColor),s=R(r.borderColor),n={uSize:{value:new h.Vector2(t,e)},uRadius:{value:b(r.borderRadius)},uBorderWidth:{value:b(r.borderWidth)},uColor:{value:i.color},uBorderColor:{value:s.color},uOpacity:{value:(a=r.opacity)!=null?a:1},uBgOpacity:{value:i.alpha}};return new h.ShaderMaterial({uniforms:n,vertexShader:A,fragmentShader:I,transparent:!0,side:h.FrontSide})}function X(r,t,e,i){var o;const s=R(t.backgroundColor),n=R(t.borderColor);r.uniforms.uSize.value.set(e,i),r.uniforms.uRadius.value=b(t.borderRadius),r.uniforms.uBorderWidth.value=b(t.borderWidth),r.uniforms.uColor.value.copy(s.color),r.uniforms.uBorderColor.value.copy(n.color),r.uniforms.uOpacity.value=(o=t.opacity)!=null?o:1,r.uniforms.uBgOpacity.value=s.alpha}const M={create(r,t,e,i,s,n=2){if(r==="BOX")return L(t,i,s);if(r==="TEXT"){const o=O(e||"",t,i,s,n);return new h.MeshBasicMaterial({map:o,transparent:!0,side:h.FrontSide,color:16777215})}return new h.MeshBasicMaterial({visible:!1})},update(r,t,e,i,s,n,o=2){if(t==="BOX")X(r,e,s,n);else if(t==="TEXT"){const a=r;a.map&&a.map.dispose();const l=O(i||"",e,s,n,o);a.map=l,a.needsUpdate=!0}}};class N{constructor(t,e,i){c(this,"canvas");c(this,"scene");c(this,"camera");c(this,"renderer");c(this,"renderOrder",0);c(this,"textQualityFactor",2);c(this,"mode","overlay");c(this,"customZIndex","9999");c(this,"target");c(this,"mountContainer");c(this,"targetRect");c(this,"meshMap",new Map);var o,a,l;this.target=t,this.mountContainer=i,this.mode=(o=e.mode)!=null?o:"overlay",(a=e.style)!=null&&a.zIndex&&(this.customZIndex=e.style.zIndex),this.canvas=document.createElement("canvas"),this.scene=new h.Scene,this.targetRect=this.target.getBoundingClientRect();const s=this.targetRect.width,n=this.targetRect.height;this.camera=new h.OrthographicCamera(s/-2,s/2,n/2,n/-2,1,1e3),this.camera.position.z=100,this.renderer=new h.WebGLRenderer({canvas:this.canvas,alpha:!0,antialias:!0}),this.renderer.setPixelRatio(window.devicePixelRatio),this.renderer.setSize(s,n),this.applyTextQuality((l=e.textQuality)!=null?l:"medium")}applyTextQuality(t){if(typeof t=="number"){this.textQualityFactor=Math.max(.1,t);return}switch(t){case"low":this.textQualityFactor=1;break;case"high":this.textQualityFactor=4;break;case"medium":default:this.textQualityFactor=2;break}}mount(){this.mountContainer.appendChild(this.canvas),this.canvas.style.zIndex=this.customZIndex,this.canvas.style.pointerEvents=this.mode==="overlay"?"none":"auto",this.updateCanvasLayout()}updateCanvasLayout(){this.canvas.style.width=`${this.targetRect.width}px`,this.canvas.style.height=`${this.targetRect.height}px`,this.mode==="duplicate"?(this.canvas.style.position="",this.canvas.style.top="",this.canvas.style.left="",this.canvas.style.display="block"):(this.canvas.style.position="absolute",this.canvas.style.top=`${this.target.offsetTop}px`,this.canvas.style.left=`${this.target.offsetLeft}px`,this.canvas.style.display="block")}dispose(){this.renderer.dispose(),this.canvas.remove()}setSize(t,e){this.renderer.setSize(t,e),this.camera.left=t/-2,this.camera.right=t/2,this.camera.top=e/2,this.camera.bottom=e/-2,this.camera.updateProjectionMatrix()}syncScene(t){const e=this.target.getBoundingClientRect(),i=Math.abs(e.width-this.targetRect.width)>.1||Math.abs(e.height-this.targetRect.height)>.1,s=this.mode==="overlay"&&(Math.abs(e.top-this.targetRect.top)>.1||Math.abs(e.left-this.targetRect.left)>.1);i?(this.targetRect=e,this.renderer.setSize(this.targetRect.width,this.targetRect.height),this.camera.left=this.targetRect.width/-2,this.camera.right=this.targetRect.width/2,this.camera.top=this.targetRect.height/2,this.camera.bottom=this.targetRect.height/-2,this.camera.updateProjectionMatrix(),this.updateCanvasLayout()):s?(this.targetRect=e,this.updateCanvasLayout()):this.targetRect=e,this.renderOrder=0;const n=new Set;this.reconcileNode(t,n);for(const[o,a]of this.meshMap.entries())n.has(o)||(this.scene.remove(a),a.geometry.dispose(),a.material instanceof h.Material&&a.material.dispose(),this.meshMap.delete(o))}reconcileNode(t,e){e.add(t.element);let i=this.meshMap.get(t.element);if(!i){const s=new h.PlaneGeometry(1,1),n=M.create("BOX",t.styles,"",t.rect.width,t.rect.height);i=new h.Mesh(s,n),t.type==="TEXT"&&(i.name="BG_MESH"),this.scene.add(i),this.meshMap.set(t.element,i)}if(i.userData.domRect=t.rect,this.updateMeshProperties(i,t),t.type==="BOX")for(const s of t.children)this.reconcileNode(s,e);else t.type==="TEXT"&&this.reconcileTextChild(i,t)}reconcileTextChild(t,e){var a,l;let i=t.children.find(d=>d.name==="TEXT_CHILD");const s=JSON.stringify(e.textStyles),n=(a=i==null?void 0:i.userData)==null?void 0:a.styleHash;if(!i||e.dirtyMask&B||s!==n){i&&((l=i.material.map)==null||l.dispose(),i.geometry.dispose(),t.remove(i));const d=M.create("TEXT",e.textStyles,e.textContent||"",e.rect.width,e.rect.height,this.textQualityFactor),m=new h.PlaneGeometry(1,1);i=new h.Mesh(m,d),i.name="TEXT_CHILD",i.userData={styleHash:s},i.position.z=.005,t.add(i)}if(i){const d=t.userData.domRect,m=d.x+d.width/2,u=d.y+d.height/2,g=e.rect.x+e.rect.width/2,y=e.rect.y+e.rect.height/2,x=g-m,v=-(y-u);i.position.set(x,v,.005)}}updateMeshProperties(t,e){const{rect:i,styles:s}=e,n=this.renderer.getPixelRatio(),o=this.renderer.domElement.width/n,a=this.renderer.domElement.height/n;t.scale.set(i.width,i.height,1);const l=.001;this.renderOrder++;const d=this.targetRect.left+window.scrollX,m=this.targetRect.top+window.scrollY,u=i.x-d,g=i.y-m;t.position.set(u-o/2+i.width/2,-g+a/2-i.height/2,s.zIndex+this.renderOrder*l),M.update(t.material,"BOX",e.styles,"",e.rect.width,e.rect.height)}render(){this.renderer.render(this.scene,this.camera)}}function _(r){const t=document.createRange();t.selectNodeContents(r);const e=t.getBoundingClientRect();return{left:e.left,top:e.top,width:e.width,height:e.height}}function k(r){const t=parseFloat(r.fontSize);let e=parseFloat(r.lineHeight);isNaN(e)&&(e=t*1.2);let i=parseFloat(r.letterSpacing);return isNaN(i)&&(i=0),{font:`${r.fontStyle} ${r.fontWeight} ${r.fontSize} ${r.fontFamily}`,color:r.color,textAlign:r.textAlign||"start",textBaseline:"alphabetic",direction:r.direction||"inherit",lineHeight:e,letterSpacing:i}}function D(r,t=C|S|z|B|E){if(r.nodeType===Node.TEXT_NODE){const u=r;if(!u.textContent||!u.textContent.trim())return null;const g=u.textContent.replace(/\s+/g," ").trim();if(g.length===0)return null;const y=_(u);if(y.width===0||y.height===0)return null;const x=u.parentElement,v=x?window.getComputedStyle(x):null;return v?{id:Math.random().toString(36).substring(2,9),type:"TEXT",element:u,rect:{x:y.left+window.scrollX,y:y.top+window.scrollY,width:y.width,height:y.height},styles:{backgroundColor:"transparent",opacity:parseFloat(v.opacity),zIndex:0,borderRadius:"0px",borderColor:"transparent",borderWidth:"0px"},textContent:g,textStyles:k(v),dirtyMask:t,children:[]}:null}const e=r,i=e.getBoundingClientRect(),s=window.getComputedStyle(e);if(i.width===0||i.height===0||s.display==="none")return null;let n=e.getAttribute("data-mid");n||(n=Math.random().toString(36).substring(2,11),e.setAttribute("data-mid",n));const o=parseInt(s.zIndex),a={backgroundColor:s.backgroundColor,opacity:parseFloat(s.opacity),zIndex:isNaN(o)?0:o,borderRadius:s.borderRadius,borderColor:s.borderColor,borderWidth:s.borderWidth};let l,d;const m=[];return Array.from(e.childNodes).forEach(u=>{const g=D(u,t);g&&m.push(g)}),{id:n,type:"BOX",element:e,rect:{x:i.left+window.scrollX,y:i.top+window.scrollY,width:i.width,height:i.height},styles:a,textContent:l,textStyles:d,dirtyMask:t,children:m}}class P{constructor(t,e){c(this,"target");c(this,"renderer");c(this,"observer");c(this,"isDomDirty",!1);c(this,"isRunning",!1);c(this,"pendingMask",T);c(this,"mutationTimer",null);c(this,"cssTimer",null);c(this,"onTransitionFinished",t=>{this.target.contains(t.target)&&this.mutationTimer===null&&(this.cssTimer&&clearTimeout(this.cssTimer),this.pendingMask|=C|S,this.cssTimer=window.setTimeout(()=>{this.isDomDirty=!0,this.cssTimer=null},50))});c(this,"onWindowResize",()=>{this.renderer.setSize(window.innerWidth,window.innerHeight),this.isDomDirty=!0});c(this,"renderLoop",()=>{this.isRunning&&(this.isDomDirty&&this.forceUpdateScene(),this.renderer.render(),requestAnimationFrame(this.renderLoop))});this.target=t,this.renderer=e,this.observer=new MutationObserver(i=>{let s=T;for(const n of i)n.type==="childList"?s|=E:n.type==="attributes"&&(n.attributeName==="style"||n.attributeName==="class")&&(s|=C|S);if(s!==T){if(this.pendingMask|=s,s&E){this.clearTimers(),console.log("Structural Change detected"),this.isDomDirty=!0;return}this.mutationTimer&&clearTimeout(this.mutationTimer),this.mutationTimer=window.setTimeout(()=>{this.mutationTimer=null,this.isDomDirty=!0},200)}})}start(){this.isRunning||(this.isRunning=!0,this.observer.observe(this.target,{childList:!0,subtree:!0,attributes:!0,characterData:!0}),this.target.addEventListener("transitionend",this.onTransitionFinished),this.target.addEventListener("animationend",this.onTransitionFinished),window.addEventListener("resize",this.onWindowResize),this.forceUpdateScene(),this.renderLoop())}stop(){this.isRunning=!1,this.observer.disconnect(),this.clearTimers(),this.target.removeEventListener("transitionend",this.onTransitionFinished),this.target.removeEventListener("animationend",this.onTransitionFinished),window.removeEventListener("resize",this.onWindowResize)}clearTimers(){this.mutationTimer&&(clearTimeout(this.mutationTimer),this.mutationTimer=null),this.cssTimer&&(clearTimeout(this.cssTimer),this.cssTimer=null)}forceUpdateScene(){this.isDomDirty=!1;const t=D(this.target,this.pendingMask);t&&this.renderer.syncScene(t),this.pendingMask=T}}class H{constructor(t,e){c(this,"renderer");c(this,"syncer");c(this,"target");var s,n,o;this.target=t;let i;if(e.mode==="duplicate"?i=(n=(s=e.container)!=null?s:this.target.parentElement)!=null?n:void 0:i=(o=this.target.parentElement)!=null?o:void 0,!i)throw new Error("[Mirage] Cannot find a container (parent or option).");this.renderer=new N(this.target,e,i),this.renderer.mount(),this.syncer=new P(this.target,this.renderer)}start(){this.syncer.start()}stop(){this.syncer.stop()}dispose(){this.syncer.stop(),this.renderer.dispose()}}class W{constructor(t,e){c(this,"_engine");if(!t)throw new Error("[Mirage] Target element is required.");this._engine=new H(t,e)}start(){this._engine.start()}stop(){this._engine.stop()}destroy(){this._engine.dispose()}}p.Mirage=W,Object.defineProperty(p,Symbol.toStringTag,{value:"Module"})});
@@ -1,11 +1,8 @@
1
1
  import { MirageConfig } from '../types';
2
2
  export declare class Mirage {
3
3
  private _engine;
4
- constructor(element: HTMLElement, config?: MirageConfig);
4
+ constructor(target: HTMLElement, config: MirageConfig);
5
5
  start(): void;
6
6
  stop(): void;
7
- /**
8
- * 엔진 종료 및 메모리 해제
9
- */
10
7
  destroy(): void;
11
8
  }
@@ -1,18 +1,2 @@
1
- export type TextQuality = "low" | "medium" | "high" | number;
2
- export type MirageMode = "overlay" | "duplicate";
3
- interface BaseConfig {
4
- debug?: boolean;
5
- textQuality?: TextQuality;
6
- style?: {
7
- zIndex?: string;
8
- };
9
- }
10
- export interface OverlayConfig extends BaseConfig {
11
- mode?: "overlay";
12
- }
13
- export interface DuplicateConfig extends BaseConfig {
14
- mode: "duplicate";
15
- container?: HTMLElement;
16
- }
17
- export type MirageConfig = OverlayConfig | DuplicateConfig;
18
- export {};
1
+ import { CoreConfig } from '../../packages/core/src/index.ts';
2
+ export type MirageConfig = CoreConfig;
@@ -1,3 +1 @@
1
1
  export * from './config';
2
- export * from './common';
3
- export * from './flags';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mirage-engine",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "An engine that mirrors HTML DOM elements to a WebGL scene in real-time.",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -21,7 +21,8 @@
21
21
  "dist"
22
22
  ],
23
23
  "dependencies": {
24
- "@mirage-engine/painter": "0.2.2"
24
+ "@mirage-engine/painter": "0.2.3",
25
+ "@mirage-engine/core": "0.0.2"
25
26
  },
26
27
  "peerDependencies": {
27
28
  "three": "^0.150.0"
@@ -1,10 +0,0 @@
1
- import { MirageConfig } from '../types';
2
- export declare class Engine {
3
- private renderer;
4
- private syncer;
5
- private target;
6
- constructor(target: HTMLElement, config: MirageConfig);
7
- start(): void;
8
- stop(): void;
9
- dispose(): void;
10
- }
@@ -1,19 +0,0 @@
1
- import { Renderer } from '../renderer/Renderer';
2
- export declare class Syncer {
3
- private target;
4
- private renderer;
5
- private observer;
6
- private isDomDirty;
7
- private isRunning;
8
- private pendingMask;
9
- private mutationTimer;
10
- private cssTimer;
11
- constructor(target: HTMLElement, renderer: Renderer);
12
- start(): void;
13
- stop(): void;
14
- private clearTimers;
15
- private onTransitionFinished;
16
- private onWindowResize;
17
- private forceUpdateScene;
18
- private renderLoop;
19
- }
@@ -1,2 +0,0 @@
1
- import { SceneNode } from '../types';
2
- export declare function extractSceneGraph(sourceNode: HTMLElement | Node, initialMask?: number): SceneNode | null;
@@ -1,26 +0,0 @@
1
- import { SceneNode, MirageConfig } from '../types';
2
- export declare class Renderer {
3
- readonly canvas: HTMLCanvasElement;
4
- private readonly scene;
5
- private readonly camera;
6
- private readonly renderer;
7
- private renderOrder;
8
- private textQualityFactor;
9
- private mode;
10
- private customZIndex;
11
- private target;
12
- private mountContainer;
13
- private targetRect;
14
- private meshMap;
15
- constructor(target: HTMLElement, config: MirageConfig, mountContainer: HTMLElement);
16
- private applyTextQuality;
17
- mount(): void;
18
- private updateCanvasLayout;
19
- dispose(): void;
20
- setSize(width: number, height: number): void;
21
- syncScene(graphNode: SceneNode): void;
22
- private reconcileNode;
23
- private reconcileTextChild;
24
- private updateMeshProperties;
25
- render(): void;
26
- }
@@ -1,19 +0,0 @@
1
- import { TextStyles, BoxStyles } from '../../packages/painter/src/index.ts';
2
- export type NodeType = "BOX" | "TEXT";
3
- export interface NodeRect {
4
- x: number;
5
- y: number;
6
- width: number;
7
- height: number;
8
- }
9
- export interface SceneNode {
10
- id: string;
11
- type: NodeType;
12
- element: HTMLElement;
13
- rect: NodeRect;
14
- styles: BoxStyles;
15
- textContent?: string;
16
- textStyles?: TextStyles;
17
- dirtyMask: number;
18
- children: SceneNode[];
19
- }
@@ -1,6 +0,0 @@
1
- export declare const DIRTY_NONE = 0;
2
- export declare const DIRTY_RECT: number;
3
- export declare const DIRTY_STYLE: number;
4
- export declare const DIRTY_ZINDEX: number;
5
- export declare const DIRTY_STRUCTURE: number;
6
- export declare const DIRTY_CONTENT: number;