mirage-engine 0.2.14 → 0.2.16

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.
@@ -1,223 +1,302 @@
1
- var B = Object.defineProperty;
2
- var z = (r, t, e) => t in r ? B(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
3
- var c = (r, t, e) => (z(r, typeof t != "symbol" ? t + "" : t, e), e);
4
- import * as h from "three";
5
- const v = 0, R = 1, b = 2, D = 4, C = 8, E = 16;
6
- function F(r, t, e) {
7
- const i = t.split(`
1
+ var X = Object.defineProperty;
2
+ var P = (i, e, t) => e in i ? X(i, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[e] = t;
3
+ var h = (i, e, t) => (P(i, typeof e != "symbol" ? e + "" : e, t), t);
4
+ import * as d from "three";
5
+ const E = 0, O = 1, z = 2, q = 4, D = 8, _ = 16, x = 1, R = 2, k = [
6
+ "include-tree",
7
+ "exclude-tree",
8
+ "include-self",
9
+ "exclude-self",
10
+ "end"
11
+ ];
12
+ function Y(i, e, t) {
13
+ const r = e.split(`
8
14
  `), s = [];
9
- return i.forEach((n) => {
15
+ return r.forEach((n) => {
10
16
  const a = n.split(" ");
11
17
  let o = a[0];
12
18
  for (let l = 1; l < a.length; l++) {
13
- const d = a[l];
14
- r.measureText(o + " " + d).width < e ? o += " " + d : (s.push(o), o = d);
19
+ const c = a[l];
20
+ i.measureText(o + " " + c).width < t ? o += " " + c : (s.push(o), o = c);
15
21
  }
16
22
  s.push(o);
17
23
  }), s;
18
24
  }
19
- function S(r, t, e, i, s = 2) {
25
+ function I(i, e, t, r, s = 2) {
20
26
  const n = document.createElement("canvas"), a = n.getContext("2d");
21
27
  if (!a)
22
28
  throw new Error("[Mirage] Failed to create canvas context");
23
29
  const l = (window.devicePixelRatio || 1) * s;
24
- 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;
25
- const d = F(a, r, e), p = t.lineHeight;
26
- d.forEach((m, g) => {
27
- const y = g * p + 2;
28
- let f = 0;
29
- t.textAlign === "center" ? f = e / 2 : t.textAlign === "right" && (f = e), a.textAlign = t.textAlign, a.fillText(m, f, y);
30
+ n.width = t * l, n.height = r * l, a.scale(l, l), a.font = e.font, a.fillStyle = e.color, a.textBaseline = "top", a.globalAlpha = 1;
31
+ const c = Y(a, i, t), g = e.lineHeight;
32
+ c.forEach((p, m) => {
33
+ const T = m * g + 2;
34
+ let v = 0;
35
+ e.textAlign === "center" ? v = t / 2 : e.textAlign === "right" && (v = t), a.textAlign = e.textAlign, a.fillText(p, v, T);
30
36
  });
31
- const u = new h.CanvasTexture(n);
32
- return u.colorSpace = h.SRGBColorSpace, u.minFilter = h.LinearFilter, u.magFilter = h.LinearFilter, u.needsUpdate = !0, u;
37
+ const u = new d.CanvasTexture(n);
38
+ return u.colorSpace = d.SRGBColorSpace, u.minFilter = d.LinearFilter, u.magFilter = d.LinearFilter, u.needsUpdate = !0, u;
33
39
  }
34
- function w(r) {
35
- return typeof r == "number" ? r : parseFloat(r) || 0;
40
+ function w(i) {
41
+ return typeof i == "number" ? i : parseFloat(i) || 0;
36
42
  }
37
- const O = `
43
+ function N(i, e) {
44
+ var o, l, c, g;
45
+ if (!e) {
46
+ i.set(0, 0, 0, 0);
47
+ return;
48
+ }
49
+ const t = e.split("/")[0].trim().split(/\s+/), r = w(t[0]), s = w((o = t[1]) != null ? o : t[0]), n = w((l = t[2]) != null ? l : t[0]), a = w((g = (c = t[3]) != null ? c : t[1]) != null ? g : t[0]);
50
+ i.set(r, s, n, a);
51
+ }
52
+ const U = (
53
+ /* glsl */
54
+ `
38
55
  varying vec2 vUv;
56
+ varying vec4 vScreenPos;
39
57
  void main() {
40
58
  vUv = uv;
41
59
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
60
+ vScreenPos = gl_Position;
42
61
  }
43
- `, A = `
62
+ `
63
+ ), W = (
64
+ /* glsl */
65
+ `
66
+
44
67
  varying vec2 vUv;
45
68
 
46
69
  uniform vec2 uSize;
47
- uniform float uRadius;
70
+ uniform vec4 uBorderRadius;
48
71
  uniform float uBorderWidth;
49
- uniform vec3 uColor;
72
+ uniform vec3 uBgColor;
50
73
  uniform vec3 uBorderColor;
51
74
  uniform float uOpacity;
52
75
  uniform float uBgOpacity;
76
+ uniform float uBorderOpacity;
77
+
78
+ #INJECT_DECLARATIONS
53
79
 
54
- // SDF 박스 함수
55
80
  float sdRoundedBox(vec2 p, vec2 b, float r) {
56
81
  vec2 q = abs(p) - b + r;
82
+
57
83
  return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
58
84
  }
59
85
 
60
86
  void main() {
61
87
  vec2 p = (vUv - 0.5) * uSize;
62
88
  vec2 halfSize = uSize * 0.5;
63
-
64
- float d = sdRoundedBox(p, halfSize, uRadius);
65
-
66
- float smoothEdge = 1.0;
67
89
 
68
- float fillAlpha = 1.0 - smoothstep(-uBorderWidth - smoothEdge, -uBorderWidth, d);
69
-
70
- float borderAlpha = 0.0;
90
+ #INJECT_UV_MODIFIER
71
91
 
72
- if (uBorderWidth > 0.01) {
73
- borderAlpha = (1.0 - smoothstep(0.0, smoothEdge, d)) - fillAlpha;
74
- }
92
+ // color decision pipeline
93
+ vec4 baseColor = vec4(uBgColor, uBgOpacity);
75
94
 
76
- vec3 color = uColor;
77
- float totalAlpha = borderAlpha + fillAlpha;
78
-
79
- if (totalAlpha > 0.001) {
80
- color = mix(uColor, uBorderColor, borderAlpha / totalAlpha);
95
+ #INJECT_BASE_COLOR
96
+
97
+ // sdf shape pipeline
98
+ vec2 xRadii = mix(uBorderRadius.xw, uBorderRadius.yz, step(0.0, p.x));
99
+ float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
100
+ float d = sdRoundedBox(p, halfSize, r);
101
+
102
+ float aa = 1.0;
103
+ float bgMask = 1.0 - smoothstep(0.0, aa, d);
104
+
105
+ float halfBorder = uBorderWidth * 0.5;
106
+ float borderD = abs(d + halfBorder) - halfBorder;
107
+ float borderAlpha = (1.0 - smoothstep(0.0, aa, borderD)) * uBorderOpacity;
108
+ if (uBorderWidth <= 0.01) {
109
+ borderAlpha = 0.0;
81
110
  }
82
-
83
- float shapeAlpha = borderAlpha + (fillAlpha * uBgOpacity);
84
- float finalOpacity = shapeAlpha * uOpacity;
85
-
111
+
112
+ // final blending (border + background)
113
+ float aFront = borderAlpha;
114
+ float aBack = baseColor.a;
115
+ float aOut = aFront + aBack * (1.0 - aFront);
116
+
117
+ float safeAlpha = max(aOut, 0.0001);
118
+ vec3 cOut = (uBorderColor * aFront + baseColor.rgb * aBack * (1.0 - aFront)) / safeAlpha;
119
+ vec4 finalColor = vec4(cOut, aOut);
120
+
121
+ // final color control (Tint, Noise)
122
+ #INJECT_COLOR_MODIFIER
123
+
124
+ float finalOpacity = finalColor.a * bgMask * uOpacity;
86
125
  if (finalOpacity < 0.001) discard;
87
126
 
88
- gl_FragColor = vec4(color, finalOpacity);
127
+ gl_FragColor = vec4(finalColor.rgb, finalOpacity);
128
+
129
+ #include <colorspace_fragment>
89
130
  }
90
- `;
91
- function x(r) {
92
- if (!r || r === "transparent")
93
- return { color: new h.Color(16777215), alpha: 0 };
94
- const t = r.match(
131
+ `
132
+ );
133
+ function S(i) {
134
+ if (!i || i === "transparent")
135
+ return { color: new d.Color(16777215), alpha: 0 };
136
+ const e = i.match(
95
137
  /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/
96
138
  );
97
- if (t) {
98
- 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;
99
- return { color: new h.Color(`rgb(${e}, ${i}, ${s})`), alpha: n };
139
+ if (e) {
140
+ const t = parseInt(e[1], 10), r = parseInt(e[2], 10), s = parseInt(e[3], 10), n = e[4] !== void 0 ? parseFloat(e[4]) : 1;
141
+ return { color: new d.Color(`rgb(${t}, ${r}, ${s})`), alpha: n };
100
142
  }
101
- return { color: new h.Color(r), alpha: 1 };
143
+ return { color: new d.Color(i), alpha: 1 };
102
144
  }
103
- function I(r, t, e) {
104
- var o;
105
- const i = x(r.backgroundColor), s = x(r.borderColor), n = {
106
- uSize: { value: new h.Vector2(t, e) },
107
- uRadius: { value: w(r.borderRadius) },
108
- uBorderWidth: { value: w(r.borderWidth) },
109
- uColor: { value: i.color },
110
- uBorderColor: { value: s.color },
111
- uOpacity: { value: (o = r.opacity) != null ? o : 1 },
112
- uBgOpacity: { value: i.alpha }
145
+ function H(i, e, t, r = null, s) {
146
+ var v;
147
+ const n = r !== null, a = n ? (
148
+ /* glsl */
149
+ `
150
+ uniform sampler2D uTexture;
151
+ varying vec4 vScreenPos;
152
+ `
153
+ ) : "", o = n ? (
154
+ /* glsl */
155
+ `
156
+ vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
157
+ vec2 resultUv = screenUv;
158
+ ${(s == null ? void 0 : s.uvModifier) || ""}
159
+ `
160
+ ) : "", l = n ? (
161
+ /* glsl */
162
+ `
163
+ baseColor = texture2D(uTexture, resultUv);
164
+ `
165
+ ) : "", c = (s == null ? void 0 : s.colorModifier) || "", g = W.replace("#INJECT_DECLARATIONS", a).replace("#INJECT_UV_MODIFIER", o).replace("#INJECT_BASE_COLOR", l).replace("#INJECT_COLOR_MODIFIER", c), u = S(i.backgroundColor), p = S(i.borderColor), m = {
166
+ uSize: { value: new d.Vector2(e, t) },
167
+ uBorderRadius: { value: new d.Vector4(0, 0, 0, 0) },
168
+ uBorderWidth: { value: w(i.borderWidth) },
169
+ uBgColor: { value: u.color },
170
+ uBorderColor: { value: p.color },
171
+ uOpacity: { value: (v = i.opacity) != null ? v : 1 },
172
+ uBgOpacity: { value: u.alpha },
173
+ uBorderOpacity: { value: p.alpha },
174
+ uTexture: { value: null }
113
175
  };
114
- return new h.ShaderMaterial({
115
- uniforms: n,
116
- vertexShader: O,
117
- fragmentShader: A,
176
+ return N(m.uBorderRadius.value, i.borderRadius), n && (m.uTexture.value = r), new d.ShaderMaterial({
177
+ uniforms: m,
178
+ vertexShader: U,
179
+ fragmentShader: g,
118
180
  transparent: !0,
119
- side: h.FrontSide
181
+ side: d.FrontSide
120
182
  // for better performance
121
183
  });
122
184
  }
123
- function L(r, t, e, i) {
124
- var a;
125
- const s = x(t.backgroundColor), n = x(t.borderColor);
126
- r.uniforms.uSize.value.set(e, i), r.uniforms.uRadius.value = w(t.borderRadius), r.uniforms.uBorderWidth.value = w(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;
185
+ function $(i, e, t, r, s) {
186
+ var o;
187
+ const n = S(e.backgroundColor), a = S(e.borderColor);
188
+ i.uniforms.uSize.value.set(t, r), N(i.uniforms.uBorderRadius.value, e.borderRadius), i.uniforms.uBorderWidth.value = w(e.borderWidth), i.uniforms.uBgColor.value.copy(n.color), i.uniforms.uBorderColor.value.copy(a.color), i.uniforms.uOpacity.value = (o = e.opacity) != null ? o : 1, i.uniforms.uBgOpacity.value = n.alpha, i.uniforms.uBorderOpacity.value = a.alpha, i.uniforms.uTexture && s !== void 0 && (i.uniforms.uTexture.value = s);
127
189
  }
128
- const T = {
129
- create(r, t, e, i, s, n = 2) {
130
- if (r === "BOX")
131
- return I(t, i, s);
132
- if (r === "TEXT") {
133
- const a = S(
134
- e || "",
135
- t,
136
- i,
190
+ const F = {
191
+ create(i, e, t, r, s, n = 2, a = null) {
192
+ if (i === "BOX")
193
+ return H(e, r, s, a);
194
+ if (i === "TEXT") {
195
+ const o = I(
196
+ t || "",
197
+ e,
198
+ r,
137
199
  s,
138
200
  n
139
201
  );
140
- return new h.MeshBasicMaterial({
141
- map: a,
202
+ return new d.MeshBasicMaterial({
203
+ map: o,
142
204
  transparent: !0,
143
- side: h.FrontSide,
205
+ side: d.FrontSide,
144
206
  color: 16777215
145
207
  });
146
208
  }
147
- return new h.MeshBasicMaterial({ visible: !1 });
209
+ return new d.MeshBasicMaterial({ visible: !1 });
148
210
  },
149
- update(r, t, e, i, s, n, a = 2) {
150
- if (t === "BOX")
151
- L(
152
- r,
153
- e,
211
+ update(i, e, t, r, s, n, a = 2, o) {
212
+ if (e === "BOX")
213
+ $(
214
+ i,
215
+ t,
154
216
  s,
155
- n
217
+ n,
218
+ o
156
219
  );
157
- else if (t === "TEXT") {
158
- const o = r;
159
- o.map && o.map.dispose();
160
- const l = S(
161
- i || "",
162
- e,
220
+ else if (e === "TEXT") {
221
+ const l = i;
222
+ l.map && l.map.dispose();
223
+ const c = I(
224
+ r || "",
225
+ t,
163
226
  s,
164
227
  n,
165
228
  a
166
229
  );
167
- o.map = l, o.needsUpdate = !0;
230
+ l.map = c, l.needsUpdate = !0;
168
231
  }
169
232
  }
170
233
  };
171
- class X {
172
- constructor(t, e, i) {
173
- c(this, "canvas");
174
- c(this, "scene");
175
- c(this, "camera");
176
- c(this, "renderer");
177
- c(this, "renderOrder", 0);
178
- c(this, "textQualityFactor", 2);
179
- c(this, "mode", "overlay");
180
- c(this, "customZIndex", "9999");
181
- c(this, "target");
182
- c(this, "mountContainer");
183
- c(this, "targetRect");
184
- c(this, "meshMap", /* @__PURE__ */ new Map());
185
- var a, o, l;
186
- 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();
234
+ class G {
235
+ constructor(e, t, r) {
236
+ h(this, "canvas");
237
+ h(this, "scene");
238
+ h(this, "camera");
239
+ h(this, "renderer");
240
+ h(this, "renderTarget", null);
241
+ h(this, "renderOrder", 0);
242
+ h(this, "qualityFactor", 2);
243
+ h(this, "mode", "overlay");
244
+ h(this, "target");
245
+ h(this, "mountContainer");
246
+ h(this, "targetRect");
247
+ h(this, "meshMap", /* @__PURE__ */ new Map());
248
+ var a, o;
249
+ this.target = e, this.mountContainer = r, this.mode = (a = t.mode) != null ? a : "overlay", this.canvas = document.createElement("canvas"), this.scene = new d.Scene(), this.targetRect = this.target.getBoundingClientRect();
187
250
  const s = this.targetRect.width, n = this.targetRect.height;
188
- this.camera = new h.OrthographicCamera(
251
+ this.camera = new d.OrthographicCamera(
189
252
  s / -2,
190
253
  s / 2,
191
254
  n / 2,
192
255
  n / -2,
193
256
  1,
194
257
  1e3
195
- ), this.camera.position.z = 100, this.renderer = new h.WebGLRenderer({
258
+ ), this.camera.position.z = 100, this.camera.layers.set(0), this.renderer = new d.WebGLRenderer({
196
259
  canvas: this.canvas,
197
260
  alpha: !0,
198
261
  antialias: !0
199
- }), this.renderer.setPixelRatio(window.devicePixelRatio), this.renderer.setSize(s, n), this.applyTextQuality((l = e.textQuality) != null ? l : "medium");
262
+ // [new]
263
+ // premultipliedAlpha: true
264
+ }), this.renderer.setPixelRatio(window.devicePixelRatio), this.renderer.setSize(s, n), this.applyTextQuality((o = t.quality) != null ? o : "medium");
265
+ }
266
+ createRenderTarget() {
267
+ this.renderTarget = new d.WebGLRenderTarget(
268
+ this.targetRect.width * this.qualityFactor,
269
+ this.targetRect.height * this.qualityFactor,
270
+ {
271
+ minFilter: d.LinearFilter,
272
+ magFilter: d.LinearFilter,
273
+ format: d.RGBAFormat,
274
+ stencilBuffer: !1,
275
+ depthBuffer: !0,
276
+ samples: 4
277
+ }
278
+ );
200
279
  }
201
- applyTextQuality(t) {
202
- if (typeof t == "number") {
203
- this.textQualityFactor = Math.max(0.1, t);
280
+ applyTextQuality(e) {
281
+ if (typeof e == "number") {
282
+ this.qualityFactor = Math.max(0.1, e);
204
283
  return;
205
284
  }
206
- switch (t) {
285
+ switch (e) {
207
286
  case "low":
208
- this.textQualityFactor = 1;
287
+ this.qualityFactor = 1;
209
288
  break;
210
289
  case "high":
211
- this.textQualityFactor = 4;
290
+ this.qualityFactor = 4;
212
291
  break;
213
292
  case "medium":
214
293
  default:
215
- this.textQualityFactor = 2;
294
+ this.qualityFactor = 2;
216
295
  break;
217
296
  }
218
297
  }
219
298
  mount() {
220
- this.mountContainer.appendChild(this.canvas), this.canvas.style.zIndex = this.customZIndex, this.canvas.style.pointerEvents = this.mode === "overlay" ? "none" : "auto", this.updateCanvasLayout();
299
+ this.mountContainer.prepend(this.canvas), this.canvas.style.pointerEvents = this.mode === "overlay" ? "none" : "auto", this.updateCanvasLayout();
221
300
  }
222
301
  updateCanvasLayout() {
223
302
  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");
@@ -225,206 +304,300 @@ class X {
225
304
  dispose() {
226
305
  this.renderer.dispose(), this.canvas.remove();
227
306
  }
228
- setSize(t, e) {
229
- 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();
307
+ setSize(e, t) {
308
+ this.renderer.setSize(e, t), this.renderTarget && this.renderTarget.setSize(
309
+ e * this.qualityFactor,
310
+ t * this.qualityFactor
311
+ ), this.camera.left = e / -2, this.camera.right = e / 2, this.camera.top = t / 2, this.camera.bottom = t / -2, this.camera.updateProjectionMatrix();
230
312
  }
231
- syncScene(t) {
232
- const e = this.target.getBoundingClientRect(), i = Math.abs(e.width - this.targetRect.width) > 0.1 || Math.abs(e.height - this.targetRect.height) > 0.1, s = this.mode === "overlay" && (Math.abs(e.top - this.targetRect.top) > 0.1 || Math.abs(e.left - this.targetRect.left) > 0.1);
233
- 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;
313
+ syncScene(e) {
314
+ const t = this.target.getBoundingClientRect(), r = Math.abs(t.width - this.targetRect.width) > 0.1 || Math.abs(t.height - this.targetRect.height) > 0.1, s = this.mode === "overlay" && (Math.abs(t.top - this.targetRect.top) > 0.1 || Math.abs(t.left - this.targetRect.left) > 0.1);
315
+ r ? (this.targetRect = t, this.setSize(this.targetRect.width, this.targetRect.height), this.updateCanvasLayout()) : s ? (this.targetRect = t, this.updateCanvasLayout()) : this.targetRect = t, this.renderOrder = 0;
234
316
  const n = /* @__PURE__ */ new Set();
235
- this.reconcileNode(t, n);
317
+ this.reconcileNode(e, n);
236
318
  for (const [a, o] of this.meshMap.entries())
237
- n.has(a) || (this.scene.remove(o), o.geometry.dispose(), o.material instanceof h.Material && o.material.dispose(), this.meshMap.delete(a));
319
+ n.has(a) || (this.scene.remove(o), o.geometry.dispose(), o.material instanceof d.Material && o.material.dispose(), this.meshMap.delete(a));
238
320
  }
239
- reconcileNode(t, e) {
240
- e.add(t.element);
241
- let i = this.meshMap.get(t.element);
242
- if (!i) {
243
- const s = new h.PlaneGeometry(1, 1), n = T.create(
321
+ reconcileNode(e, t) {
322
+ var s;
323
+ t.add(e.element);
324
+ let r = this.meshMap.get(e.element);
325
+ if (!r) {
326
+ const n = new d.PlaneGeometry(1, 1);
327
+ let a;
328
+ a = F.create(
244
329
  "BOX",
245
- t.styles,
330
+ e.styles,
246
331
  "",
247
- t.rect.width,
248
- t.rect.height
249
- );
250
- i = new h.Mesh(s, n), t.type === "TEXT" && (i.name = "BG_MESH"), this.scene.add(i), this.meshMap.set(t.element, i);
332
+ e.rect.width,
333
+ e.rect.height,
334
+ this.qualityFactor,
335
+ e.isTraveler ? (s = this.renderTarget) == null ? void 0 : s.texture : void 0
336
+ ), r = new d.Mesh(n, a), e.type === "TEXT" && (r.name = "BG_MESH"), this.scene.add(r), this.meshMap.set(e.element, r);
251
337
  }
252
- if (i.userData.domRect = t.rect, this.updateMeshProperties(i, t), t.type === "BOX")
253
- for (const s of t.children)
254
- this.reconcileNode(s, e);
338
+ if (r.userData.domRect = e.rect, this.updateMeshProperties(r, e), this.updateMeshLayers(r, e), e.isTraveler && r.layers.enable(28), e.type === "BOX")
339
+ for (const n of e.children)
340
+ this.reconcileNode(n, t);
255
341
  else
256
- t.type === "TEXT" && this.reconcileTextChild(i, t);
342
+ e.type === "TEXT" && this.reconcileTextChild(r, e);
257
343
  }
258
- reconcileTextChild(t, e) {
344
+ reconcileTextChild(e, t) {
259
345
  var o, l;
260
- let i = t.children.find(
261
- (d) => d.name === "TEXT_CHILD"
346
+ let r = e.children.find(
347
+ (c) => c.name === "TEXT_CHILD"
262
348
  );
263
- const s = JSON.stringify(e.textStyles), n = (o = i == null ? void 0 : i.userData) == null ? void 0 : o.styleHash;
264
- if (!i || e.dirtyMask & E || s !== n) {
265
- i && ((l = i.material.map) == null || l.dispose(), i.geometry.dispose(), t.remove(i));
266
- const d = T.create(
349
+ const s = JSON.stringify(t.textStyles), n = (o = r == null ? void 0 : r.userData) == null ? void 0 : o.styleHash;
350
+ if (!r || t.dirtyMask & _ || s !== n) {
351
+ r && ((l = r.material.map) == null || l.dispose(), r.geometry.dispose(), e.remove(r));
352
+ const c = F.create(
267
353
  "TEXT",
268
- e.textStyles,
269
- e.textContent || "",
270
- e.rect.width,
271
- e.rect.height,
272
- this.textQualityFactor
273
- ), p = new h.PlaneGeometry(1, 1);
274
- i = new h.Mesh(p, d), i.name = "TEXT_CHILD", i.userData = { styleHash: s }, i.position.z = 5e-3, t.add(i);
354
+ t.textStyles,
355
+ t.textContent || "",
356
+ t.rect.width,
357
+ t.rect.height,
358
+ this.qualityFactor
359
+ ), g = new d.PlaneGeometry(1, 1);
360
+ r = new d.Mesh(g, c), r.name = "TEXT_CHILD", r.userData = { styleHash: s }, this.updateMeshLayers(r, t), r.position.z = 5e-3, e.add(r);
275
361
  }
276
- if (i) {
277
- const d = t.userData.domRect, p = d.x + d.width / 2, u = d.y + d.height / 2, m = e.rect.x + e.rect.width / 2, g = e.rect.y + e.rect.height / 2, y = m - p, f = -(g - u);
278
- i.position.set(y, f, 5e-3);
362
+ if (r) {
363
+ const c = e.userData.domRect, g = c.x + c.width / 2, u = c.y + c.height / 2, p = t.rect.x + t.rect.width / 2, m = t.rect.y + t.rect.height / 2, T = p - g, v = -(m - u);
364
+ r.position.set(T, v, 5e-3);
279
365
  }
280
366
  }
281
- updateMeshProperties(t, e) {
282
- const { rect: i, styles: s } = e, n = this.renderer.getPixelRatio(), a = this.renderer.domElement.width / n, o = this.renderer.domElement.height / n;
283
- t.scale.set(i.width, i.height, 1);
367
+ updateMeshProperties(e, t) {
368
+ var m;
369
+ const { rect: r, styles: s } = t, n = this.renderer.getPixelRatio(), a = this.renderer.domElement.width / n, o = this.renderer.domElement.height / n;
370
+ e.scale.set(r.width, r.height, 1);
284
371
  const l = 1e-3;
285
372
  this.renderOrder++;
286
- const d = this.targetRect.left + window.scrollX, p = this.targetRect.top + window.scrollY, u = i.x - d, m = i.y - p;
287
- t.position.set(
288
- u - a / 2 + i.width / 2,
289
- -m + o / 2 - i.height / 2,
373
+ const c = this.targetRect.left + window.scrollX, g = this.targetRect.top + window.scrollY, u = r.x - c, p = r.y - g;
374
+ e.position.set(
375
+ u - a / 2 + r.width / 2,
376
+ -p + o / 2 - r.height / 2,
290
377
  s.zIndex + this.renderOrder * l
291
- ), T.update(
292
- t.material,
378
+ ), F.update(
379
+ e.material,
293
380
  "BOX",
294
- e.styles,
381
+ t.styles,
295
382
  "",
296
- e.rect.width,
297
- e.rect.height
383
+ t.rect.width,
384
+ t.rect.height,
385
+ this.qualityFactor,
386
+ t.isTraveler ? (m = this.renderTarget) == null ? void 0 : m.texture : void 0
298
387
  );
299
388
  }
389
+ updateMeshLayers(e, t) {
390
+ const r = (1 - (t.visibility & x)) * 30;
391
+ e.layers.set(r), t.visibility === (x | R) && e.layers.enable(29);
392
+ }
393
+ captureRenderTarget() {
394
+ const e = [];
395
+ for (const l of this.meshMap.values())
396
+ l.layers.mask & 1 << 28 && e.push(l);
397
+ if (e.length === 0)
398
+ return;
399
+ const t = new d.Color(), r = this.renderer.getClearAlpha();
400
+ this.renderer.getClearColor(t), this.renderer.setClearColor(0, 0), this.renderer.setRenderTarget(this.renderTarget), this.renderer.clear(), this.renderer.autoClear = !1, this.renderer.setScissorTest(!0), this.camera.layers.set(29);
401
+ const s = new d.Vector3(), n = this.targetRect.width, a = this.targetRect.height, o = this.renderer.getPixelRatio();
402
+ for (const l of e) {
403
+ s.setFromMatrixPosition(l.matrixWorld), s.project(this.camera);
404
+ const c = (s.x + 1) / 2 * n, g = (s.y + 1) / 2 * a, u = l.scale.x, p = l.scale.y, m = c - u / 2, T = g - p / 2, v = m * this.qualityFactor / o, B = T * this.qualityFactor / o, b = u * this.qualityFactor / o, f = p * this.qualityFactor / o;
405
+ this.renderer.setScissor(v, B, b, f), this.renderer.render(this.scene, this.camera);
406
+ }
407
+ this.renderer.setScissorTest(!1), this.renderer.autoClear = !0, this.renderer.setRenderTarget(null), this.camera.layers.set(28), this.renderer.setClearColor(t, r);
408
+ }
300
409
  render() {
301
- this.renderer.render(this.scene, this.camera);
410
+ this.renderTarget && this.captureRenderTarget(), this.renderer.render(this.scene, this.camera);
411
+ }
412
+ // for debugging
413
+ showScissoredRenderTarget() {
414
+ if (!this.renderTarget)
415
+ return;
416
+ const e = this.targetRect.width, t = this.targetRect.height, r = new d.PlaneGeometry(e, t), s = new d.MeshBasicMaterial({
417
+ map: this.renderTarget.texture,
418
+ side: d.DoubleSide,
419
+ transparent: !0,
420
+ opacity: 0.8
421
+ }), n = new d.Mesh(r, s);
422
+ n.position.set(0, 0, 90), n.layers.set(28), this.scene.add(n);
302
423
  }
303
424
  }
304
- function N(r) {
305
- const t = document.createRange();
306
- t.selectNodeContents(r);
307
- const e = t.getBoundingClientRect();
425
+ function J(i) {
426
+ const e = document.createRange();
427
+ e.selectNodeContents(i);
428
+ const t = e.getBoundingClientRect();
308
429
  return {
309
- left: e.left,
310
- top: e.top,
311
- width: e.width,
312
- height: e.height
430
+ left: t.left,
431
+ top: t.top,
432
+ width: t.width,
433
+ height: t.height
313
434
  };
314
435
  }
315
- function _(r) {
316
- const t = parseFloat(r.fontSize);
317
- let e = parseFloat(r.lineHeight);
318
- isNaN(e) && (e = t * 1.2);
319
- let i = parseFloat(r.letterSpacing);
320
- return isNaN(i) && (i = 0), {
321
- font: `${r.fontStyle} ${r.fontWeight} ${r.fontSize} ${r.fontFamily}`,
322
- color: r.color,
323
- textAlign: r.textAlign || "start",
436
+ function V(i) {
437
+ const e = parseFloat(i.fontSize);
438
+ let t = parseFloat(i.lineHeight);
439
+ isNaN(t) && (t = e * 1.2);
440
+ let r = parseFloat(i.letterSpacing);
441
+ return isNaN(r) && (r = 0), {
442
+ font: `${i.fontStyle} ${i.fontWeight} ${i.fontSize} ${i.fontFamily}`,
443
+ color: i.color,
444
+ textAlign: i.textAlign || "start",
324
445
  textBaseline: "alphabetic",
325
- direction: r.direction || "inherit",
326
- lineHeight: e,
327
- letterSpacing: i
446
+ direction: i.direction || "inherit",
447
+ lineHeight: t,
448
+ letterSpacing: r
328
449
  };
329
450
  }
330
- function M(r, t = R | b | D | E | C) {
331
- if (r.nodeType === Node.TEXT_NODE) {
332
- const u = r;
333
- if (!u.textContent || !u.textContent.trim())
451
+ function A(i, e = O | z | q | _ | D, t, r) {
452
+ if (i.nodeType === Node.TEXT_NODE) {
453
+ const f = i;
454
+ if (!f.textContent || !f.textContent.trim())
334
455
  return null;
335
- const m = u.textContent.replace(/\s+/g, " ").trim();
336
- if (m.length === 0)
456
+ const C = f.textContent.replace(/\s+/g, " ").trim();
457
+ if (C.length === 0)
337
458
  return null;
338
- const g = N(u);
339
- if (g.width === 0 || g.height === 0)
459
+ const y = J(f);
460
+ if (y.width === 0 || y.height === 0)
340
461
  return null;
341
- const y = u.parentElement, f = y ? window.getComputedStyle(y) : null;
342
- return f ? {
462
+ const L = f.parentElement, M = L ? window.getComputedStyle(L) : null;
463
+ return M ? {
343
464
  id: Math.random().toString(36).substring(2, 9),
344
465
  type: "TEXT",
345
- element: u,
466
+ element: f,
346
467
  rect: {
347
- x: g.left + window.scrollX,
348
- y: g.top + window.scrollY,
349
- width: g.width,
350
- height: g.height
468
+ x: y.left + window.scrollX,
469
+ y: y.top + window.scrollY,
470
+ width: y.width,
471
+ height: y.height
351
472
  },
352
473
  styles: {
353
474
  backgroundColor: "transparent",
354
- opacity: parseFloat(f.opacity),
475
+ opacity: parseFloat(M.opacity),
355
476
  zIndex: 0,
356
477
  borderRadius: "0px",
357
478
  borderColor: "transparent",
358
479
  borderWidth: "0px"
359
480
  },
360
- textContent: m,
361
- textStyles: _(f),
362
- dirtyMask: t,
481
+ textContent: C,
482
+ textStyles: V(M),
483
+ dirtyMask: e,
484
+ visibility: t,
485
+ isTraveler: !1,
363
486
  children: []
364
487
  } : null;
365
488
  }
366
- const e = r, i = e.getBoundingClientRect(), s = window.getComputedStyle(e);
367
- if (i.width === 0 || i.height === 0 || s.display === "none")
489
+ const s = i, n = s.dataset.mirageFilter;
490
+ let a = t, o = t;
491
+ if (n) {
492
+ const f = new Set(n.split(/\s+/));
493
+ for (const C of f)
494
+ if (!k.includes(C))
495
+ throw new Error(
496
+ `[MirageEngine] Invalid filter token: '${C}'. Expected one of: 'include-tree', 'exclude-tree', 'include-self', 'exclude-self', 'end'.`
497
+ );
498
+ if (f.has("end"))
499
+ return null;
500
+ if (f.has("include-tree") && f.has("exclude-tree"))
501
+ throw new Error(
502
+ "[MirageEngine] Conflicting filters: 'include-tree' and 'exclude-tree' cannot be used together on the same element."
503
+ );
504
+ if (f.has("include-self") && f.has("exclude-self"))
505
+ throw new Error(
506
+ "[MirageEngine] Conflicting filters: 'include-self' and 'exclude-self' cannot be used together on the same element."
507
+ );
508
+ f.has("include-tree") ? a = a | x : f.has("exclude-tree") && (a = a & ~x), o = a, f.has("include-self") ? o = o | x : f.has("exclude-self") && (o = o & ~x);
509
+ }
510
+ o = o | t & R;
511
+ const l = s.dataset.mirageTravel;
512
+ let c = !1;
513
+ l && new Set(l.split(/\s+/)).has("traveler") && (o = o & ~R, a = a & ~R, c = !0);
514
+ const g = s.getBoundingClientRect(), u = window.getComputedStyle(s);
515
+ if (g.width === 0 || g.height === 0 || u.display === "none")
368
516
  return null;
369
- let n = e.getAttribute("data-mid");
370
- n || (n = Math.random().toString(36).substring(2, 11), e.setAttribute("data-mid", n));
371
- const a = parseInt(s.zIndex), o = {
372
- backgroundColor: s.backgroundColor,
373
- opacity: parseFloat(s.opacity),
374
- zIndex: isNaN(a) ? 0 : a,
375
- borderRadius: s.borderRadius,
376
- borderColor: s.borderColor,
377
- borderWidth: s.borderWidth
517
+ let p = s.getAttribute("data-mid");
518
+ p || (p = Math.random().toString(36).substring(2, 11), s.setAttribute("data-mid", p));
519
+ const m = parseInt(u.zIndex), T = {
520
+ backgroundColor: u.backgroundColor,
521
+ opacity: parseFloat(u.opacity),
522
+ zIndex: isNaN(m) ? 0 : m,
523
+ borderRadius: u.borderRadius,
524
+ borderColor: u.borderColor,
525
+ borderWidth: u.borderWidth
378
526
  };
379
- let l, d;
380
- const p = [];
381
- return Array.from(e.childNodes).forEach((u) => {
382
- const m = M(u, t);
383
- m && p.push(m);
527
+ let v, B;
528
+ const b = [];
529
+ return Array.from(s.childNodes).forEach((f) => {
530
+ const C = f.nodeType === Node.TEXT_NODE ? o : a, y = A(
531
+ f,
532
+ e,
533
+ C
534
+ );
535
+ y && b.push(y);
384
536
  }), {
385
- id: n,
537
+ id: p,
386
538
  type: "BOX",
387
- element: e,
539
+ element: s,
388
540
  rect: {
389
- x: i.left + window.scrollX,
390
- y: i.top + window.scrollY,
391
- width: i.width,
392
- height: i.height
541
+ x: g.left + window.scrollX,
542
+ y: g.top + window.scrollY,
543
+ width: g.width,
544
+ height: g.height
393
545
  },
394
- styles: o,
395
- textContent: l,
396
- textStyles: d,
397
- dirtyMask: t,
398
- children: p
546
+ styles: T,
547
+ textContent: v,
548
+ textStyles: B,
549
+ dirtyMask: e,
550
+ visibility: o,
551
+ isTraveler: c,
552
+ children: b
399
553
  };
400
554
  }
401
- class k {
402
- constructor(t, e) {
403
- c(this, "target");
404
- c(this, "renderer");
405
- c(this, "observer");
406
- c(this, "isDomDirty", !1);
407
- c(this, "isRunning", !1);
408
- c(this, "pendingMask", v);
409
- c(this, "mutationTimer", null);
410
- c(this, "cssTimer", null);
411
- c(this, "onTransitionFinished", (t) => {
412
- this.target.contains(t.target) && this.mutationTimer === null && (this.cssTimer && clearTimeout(this.cssTimer), this.pendingMask |= R | b, this.cssTimer = window.setTimeout(() => {
555
+ class j {
556
+ constructor(e, t, r) {
557
+ h(this, "target");
558
+ h(this, "renderer");
559
+ h(this, "filter");
560
+ h(this, "observer");
561
+ h(this, "isDomDirty", !1);
562
+ h(this, "isRunning", !1);
563
+ h(this, "isTravelEnabled", !1);
564
+ h(this, "pendingMask", E);
565
+ h(this, "mutationTimer", null);
566
+ h(this, "cssTimer", null);
567
+ h(this, "resizeConfig");
568
+ h(this, "resizeTimer", null);
569
+ h(this, "isResizing", !1);
570
+ h(this, "onTransitionFinished", (e) => {
571
+ this.target.contains(e.target) && this.mutationTimer === null && (this.cssTimer && clearTimeout(this.cssTimer), this.pendingMask |= O | z, this.cssTimer = window.setTimeout(() => {
413
572
  this.isDomDirty = !0, this.cssTimer = null;
414
573
  }, 50));
415
574
  });
416
- c(this, "onWindowResize", () => {
417
- this.renderer.setSize(window.innerWidth, window.innerHeight), this.isDomDirty = !0;
575
+ h(this, "onWindowResize", () => {
576
+ if (!this.resizeConfig.enabled) {
577
+ this.isDomDirty = !0;
578
+ return;
579
+ }
580
+ this.isResizing || (this.isResizing = !0, this.resizeConfig.onStart && this.resizeConfig.onStart()), this.resizeTimer && clearTimeout(this.resizeTimer), this.resizeTimer = window.setTimeout(() => {
581
+ this.isDomDirty = !0, this.resizeConfig.onEnd && this.resizeConfig.onEnd(), this.isResizing = !1, this.resizeTimer = null;
582
+ }, this.resizeConfig.delay);
418
583
  });
419
- c(this, "renderLoop", () => {
584
+ h(this, "renderLoop", () => {
420
585
  this.isRunning && (this.isDomDirty && this.forceUpdateScene(), this.renderer.render(), requestAnimationFrame(this.renderLoop));
421
586
  });
422
- this.target = t, this.renderer = e, this.observer = new MutationObserver((i) => {
423
- let s = v;
424
- for (const n of i)
425
- n.type === "childList" ? s |= C : n.type === "attributes" && (n.attributeName === "style" || n.attributeName === "class") && (s |= R | b);
426
- if (s !== v) {
427
- if (this.pendingMask |= s, s & C) {
587
+ var n, a;
588
+ this.target = e, this.renderer = t, this.filter = r.filter;
589
+ const s = (n = r.resizeDebounce) != null ? n : !0;
590
+ s === !1 ? this.resizeConfig = { enabled: !1, delay: 0 } : s === !0 ? this.resizeConfig = { enabled: !0, delay: 150 } : this.resizeConfig = {
591
+ enabled: !0,
592
+ delay: (a = s.delay) != null ? a : 150,
593
+ onStart: s.onStart,
594
+ onEnd: s.onEnd
595
+ }, this.observer = new MutationObserver((o) => {
596
+ let l = E;
597
+ for (const c of o)
598
+ c.type === "childList" ? l |= D : c.type === "attributes" && (c.attributeName === "style" || c.attributeName === "class") && (l |= O | z);
599
+ if (l !== E) {
600
+ if (this.pendingMask |= l, l & D) {
428
601
  this.clearTimers(), console.log("Structural Change detected"), this.isDomDirty = !0;
429
602
  return;
430
603
  }
@@ -450,21 +623,28 @@ class k {
450
623
  }
451
624
  forceUpdateScene() {
452
625
  this.isDomDirty = !1;
453
- const t = M(this.target, this.pendingMask);
454
- t && this.renderer.syncScene(t), this.pendingMask = v;
626
+ const e = document.querySelector("[data-mirage-travel='traveler']") !== null;
627
+ e && !this.isTravelEnabled && (this.isTravelEnabled = !0, this.renderer.createRenderTarget());
628
+ const t = A(
629
+ this.target,
630
+ this.pendingMask,
631
+ e ? x | R : x,
632
+ this.filter
633
+ );
634
+ t && this.renderer.syncScene(t), this.pendingMask = E;
455
635
  }
456
636
  }
457
- class P {
458
- constructor(t, e) {
459
- c(this, "renderer");
460
- c(this, "syncer");
461
- c(this, "target");
637
+ class Q {
638
+ constructor(e, t) {
639
+ h(this, "renderer");
640
+ h(this, "syncer");
641
+ h(this, "target");
462
642
  var s, n, a;
463
- this.target = t;
464
- let i;
465
- 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)
643
+ this.target = e;
644
+ let r;
645
+ if (t.mode === "duplicate" ? r = (n = (s = t.container) != null ? s : this.target.parentElement) != null ? n : void 0 : r = (a = this.target.parentElement) != null ? a : void 0, !r)
466
646
  throw new Error("[Mirage] Cannot find a container (parent or option).");
467
- this.renderer = new X(this.target, e, i), this.renderer.mount(), this.syncer = new k(this.target, this.renderer);
647
+ this.renderer = new G(this.target, t, r), this.renderer.mount(), this.syncer = new j(this.target, this.renderer, t);
468
648
  }
469
649
  start() {
470
650
  this.syncer.start();
@@ -476,13 +656,12 @@ class P {
476
656
  this.syncer.stop(), this.renderer.dispose();
477
657
  }
478
658
  }
479
- class Y {
480
- // private _engine: Engine | Traveler;
481
- constructor(t, e) {
482
- c(this, "_engine");
483
- if (!t)
659
+ class K {
660
+ constructor(e, t) {
661
+ h(this, "_engine");
662
+ if (!e)
484
663
  throw new Error("[Mirage] Target element is required.");
485
- this._engine = new P(t, e);
664
+ this._engine = new Q(e, t);
486
665
  }
487
666
  start() {
488
667
  this._engine.start();
@@ -495,5 +674,5 @@ class Y {
495
674
  }
496
675
  }
497
676
  export {
498
- Y as Mirage
677
+ K as Mirage
499
678
  };
@@ -1,55 +1,85 @@
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 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=`
1
+ (function(v,y){typeof exports=="object"&&typeof module!="undefined"?y(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],y):(v=typeof globalThis!="undefined"?globalThis:v||self,y(v.MirageEngine={},v.THREE))})(this,function(v,y){"use strict";var ee=Object.defineProperty;var te=(v,y,b)=>y in v?ee(v,y,{enumerable:!0,configurable:!0,writable:!0,value:b}):v[y]=b;var h=(v,y,b)=>(te(v,typeof y!="symbol"?y+"":y,b),b);function b(i){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(i){for(const t in i)if(t!=="default"){const r=Object.getOwnPropertyDescriptor(i,t);Object.defineProperty(e,t,r.get?r:{enumerable:!0,get:()=>i[t]})}}return e.default=i,Object.freeze(e)}const d=b(y),M=0,F=1,z=2,k=4,D=8,N=16,C=1,E=2,Y=["include-tree","exclude-tree","include-self","exclude-self","end"];function U(i,e,t){const r=e.split(`
2
+ `),s=[];return r.forEach(n=>{const a=n.split(" ");let o=a[0];for(let l=1;l<a.length;l++){const c=a[l];i.measureText(o+" "+c).width<t?o+=" "+c:(s.push(o),o=c)}s.push(o)}),s}function A(i,e,t,r,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=t*l,n.height=r*l,a.scale(l,l),a.font=e.font,a.fillStyle=e.color,a.textBaseline="top",a.globalAlpha=1;const c=U(a,i,t),g=e.lineHeight;c.forEach((p,m)=>{const w=m*g+2;let T=0;e.textAlign==="center"?T=t/2:e.textAlign==="right"&&(T=t),a.textAlign=e.textAlign,a.fillText(p,T,w)});const u=new d.CanvasTexture(n);return u.colorSpace=d.SRGBColorSpace,u.minFilter=d.LinearFilter,u.magFilter=d.LinearFilter,u.needsUpdate=!0,u}function S(i){return typeof i=="number"?i:parseFloat(i)||0}function X(i,e){var o,l,c,g;if(!e){i.set(0,0,0,0);return}const t=e.split("/")[0].trim().split(/\s+/),r=S(t[0]),s=S((o=t[1])!=null?o:t[0]),n=S((l=t[2])!=null?l:t[0]),a=S((g=(c=t[3])!=null?c:t[1])!=null?g:t[0]);i.set(r,s,n,a)}const W=`
3
3
  varying vec2 vUv;
4
+ varying vec4 vScreenPos;
4
5
  void main() {
5
6
  vUv = uv;
6
7
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
8
+ vScreenPos = gl_Position;
7
9
  }
8
- `,I=`
10
+ `,H=`
11
+
9
12
  varying vec2 vUv;
10
13
 
11
14
  uniform vec2 uSize;
12
- uniform float uRadius;
15
+ uniform vec4 uBorderRadius;
13
16
  uniform float uBorderWidth;
14
- uniform vec3 uColor;
17
+ uniform vec3 uBgColor;
15
18
  uniform vec3 uBorderColor;
16
19
  uniform float uOpacity;
17
20
  uniform float uBgOpacity;
21
+ uniform float uBorderOpacity;
22
+
23
+ #INJECT_DECLARATIONS
18
24
 
19
- // SDF 박스 함수
20
25
  float sdRoundedBox(vec2 p, vec2 b, float r) {
21
26
  vec2 q = abs(p) - b + r;
27
+
22
28
  return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
23
29
  }
24
30
 
25
31
  void main() {
26
32
  vec2 p = (vUv - 0.5) * uSize;
27
33
  vec2 halfSize = uSize * 0.5;
28
-
29
- float d = sdRoundedBox(p, halfSize, uRadius);
30
-
31
- float smoothEdge = 1.0;
32
34
 
33
- float fillAlpha = 1.0 - smoothstep(-uBorderWidth - smoothEdge, -uBorderWidth, d);
35
+ #INJECT_UV_MODIFIER
34
36
 
35
- float borderAlpha = 0.0;
36
-
37
- if (uBorderWidth > 0.01) {
38
- borderAlpha = (1.0 - smoothstep(0.0, smoothEdge, d)) - fillAlpha;
39
- }
37
+ // color decision pipeline
38
+ vec4 baseColor = vec4(uBgColor, uBgOpacity);
40
39
 
41
- vec3 color = uColor;
42
- float totalAlpha = borderAlpha + fillAlpha;
43
-
44
- if (totalAlpha > 0.001) {
45
- color = mix(uColor, uBorderColor, borderAlpha / totalAlpha);
40
+ #INJECT_BASE_COLOR
41
+
42
+ // sdf shape pipeline
43
+ vec2 xRadii = mix(uBorderRadius.xw, uBorderRadius.yz, step(0.0, p.x));
44
+ float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
45
+ float d = sdRoundedBox(p, halfSize, r);
46
+
47
+ float aa = 1.0;
48
+ float bgMask = 1.0 - smoothstep(0.0, aa, d);
49
+
50
+ float halfBorder = uBorderWidth * 0.5;
51
+ float borderD = abs(d + halfBorder) - halfBorder;
52
+ float borderAlpha = (1.0 - smoothstep(0.0, aa, borderD)) * uBorderOpacity;
53
+ if (uBorderWidth <= 0.01) {
54
+ borderAlpha = 0.0;
46
55
  }
47
-
48
- float shapeAlpha = borderAlpha + (fillAlpha * uBgOpacity);
49
- float finalOpacity = shapeAlpha * uOpacity;
50
-
56
+
57
+ // final blending (border + background)
58
+ float aFront = borderAlpha;
59
+ float aBack = baseColor.a;
60
+ float aOut = aFront + aBack * (1.0 - aFront);
61
+
62
+ float safeAlpha = max(aOut, 0.0001);
63
+ vec3 cOut = (uBorderColor * aFront + baseColor.rgb * aBack * (1.0 - aFront)) / safeAlpha;
64
+ vec4 finalColor = vec4(cOut, aOut);
65
+
66
+ // final color control (Tint, Noise)
67
+ #INJECT_COLOR_MODIFIER
68
+
69
+ float finalOpacity = finalColor.a * bgMask * uOpacity;
51
70
  if (finalOpacity < 0.001) discard;
52
71
 
53
- gl_FragColor = vec4(color, finalOpacity);
72
+ gl_FragColor = vec4(finalColor.rgb, finalOpacity);
73
+
74
+ #include <colorspace_fragment>
54
75
  }
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"})});
76
+ `;function B(i){if(!i||i==="transparent")return{color:new d.Color(16777215),alpha:0};const e=i.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(e){const t=parseInt(e[1],10),r=parseInt(e[2],10),s=parseInt(e[3],10),n=e[4]!==void 0?parseFloat(e[4]):1;return{color:new d.Color(`rgb(${t}, ${r}, ${s})`),alpha:n}}return{color:new d.Color(i),alpha:1}}function $(i,e,t,r=null,s){var T;const n=r!==null,a=n?`
77
+ uniform sampler2D uTexture;
78
+ varying vec4 vScreenPos;
79
+ `:"",o=n?`
80
+ vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
81
+ vec2 resultUv = screenUv;
82
+ ${(s==null?void 0:s.uvModifier)||""}
83
+ `:"",l=n?`
84
+ baseColor = texture2D(uTexture, resultUv);
85
+ `:"",c=(s==null?void 0:s.colorModifier)||"",g=H.replace("#INJECT_DECLARATIONS",a).replace("#INJECT_UV_MODIFIER",o).replace("#INJECT_BASE_COLOR",l).replace("#INJECT_COLOR_MODIFIER",c),u=B(i.backgroundColor),p=B(i.borderColor),m={uSize:{value:new d.Vector2(e,t)},uBorderRadius:{value:new d.Vector4(0,0,0,0)},uBorderWidth:{value:S(i.borderWidth)},uBgColor:{value:u.color},uBorderColor:{value:p.color},uOpacity:{value:(T=i.opacity)!=null?T:1},uBgOpacity:{value:u.alpha},uBorderOpacity:{value:p.alpha},uTexture:{value:null}};return X(m.uBorderRadius.value,i.borderRadius),n&&(m.uTexture.value=r),new d.ShaderMaterial({uniforms:m,vertexShader:W,fragmentShader:g,transparent:!0,side:d.FrontSide})}function G(i,e,t,r,s){var o;const n=B(e.backgroundColor),a=B(e.borderColor);i.uniforms.uSize.value.set(t,r),X(i.uniforms.uBorderRadius.value,e.borderRadius),i.uniforms.uBorderWidth.value=S(e.borderWidth),i.uniforms.uBgColor.value.copy(n.color),i.uniforms.uBorderColor.value.copy(a.color),i.uniforms.uOpacity.value=(o=e.opacity)!=null?o:1,i.uniforms.uBgOpacity.value=n.alpha,i.uniforms.uBorderOpacity.value=a.alpha,i.uniforms.uTexture&&s!==void 0&&(i.uniforms.uTexture.value=s)}const L={create(i,e,t,r,s,n=2,a=null){if(i==="BOX")return $(e,r,s,a);if(i==="TEXT"){const o=A(t||"",e,r,s,n);return new d.MeshBasicMaterial({map:o,transparent:!0,side:d.FrontSide,color:16777215})}return new d.MeshBasicMaterial({visible:!1})},update(i,e,t,r,s,n,a=2,o){if(e==="BOX")G(i,t,s,n,o);else if(e==="TEXT"){const l=i;l.map&&l.map.dispose();const c=A(r||"",t,s,n,a);l.map=c,l.needsUpdate=!0}}};class j{constructor(e,t,r){h(this,"canvas");h(this,"scene");h(this,"camera");h(this,"renderer");h(this,"renderTarget",null);h(this,"renderOrder",0);h(this,"qualityFactor",2);h(this,"mode","overlay");h(this,"target");h(this,"mountContainer");h(this,"targetRect");h(this,"meshMap",new Map);var a,o;this.target=e,this.mountContainer=r,this.mode=(a=t.mode)!=null?a:"overlay",this.canvas=document.createElement("canvas"),this.scene=new d.Scene,this.targetRect=this.target.getBoundingClientRect();const s=this.targetRect.width,n=this.targetRect.height;this.camera=new d.OrthographicCamera(s/-2,s/2,n/2,n/-2,1,1e3),this.camera.position.z=100,this.camera.layers.set(0),this.renderer=new d.WebGLRenderer({canvas:this.canvas,alpha:!0,antialias:!0}),this.renderer.setPixelRatio(window.devicePixelRatio),this.renderer.setSize(s,n),this.applyTextQuality((o=t.quality)!=null?o:"medium")}createRenderTarget(){this.renderTarget=new d.WebGLRenderTarget(this.targetRect.width*this.qualityFactor,this.targetRect.height*this.qualityFactor,{minFilter:d.LinearFilter,magFilter:d.LinearFilter,format:d.RGBAFormat,stencilBuffer:!1,depthBuffer:!0,samples:4})}applyTextQuality(e){if(typeof e=="number"){this.qualityFactor=Math.max(.1,e);return}switch(e){case"low":this.qualityFactor=1;break;case"high":this.qualityFactor=4;break;case"medium":default:this.qualityFactor=2;break}}mount(){this.mountContainer.prepend(this.canvas),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(e,t){this.renderer.setSize(e,t),this.renderTarget&&this.renderTarget.setSize(e*this.qualityFactor,t*this.qualityFactor),this.camera.left=e/-2,this.camera.right=e/2,this.camera.top=t/2,this.camera.bottom=t/-2,this.camera.updateProjectionMatrix()}syncScene(e){const t=this.target.getBoundingClientRect(),r=Math.abs(t.width-this.targetRect.width)>.1||Math.abs(t.height-this.targetRect.height)>.1,s=this.mode==="overlay"&&(Math.abs(t.top-this.targetRect.top)>.1||Math.abs(t.left-this.targetRect.left)>.1);r?(this.targetRect=t,this.setSize(this.targetRect.width,this.targetRect.height),this.updateCanvasLayout()):s?(this.targetRect=t,this.updateCanvasLayout()):this.targetRect=t,this.renderOrder=0;const n=new Set;this.reconcileNode(e,n);for(const[a,o]of this.meshMap.entries())n.has(a)||(this.scene.remove(o),o.geometry.dispose(),o.material instanceof d.Material&&o.material.dispose(),this.meshMap.delete(a))}reconcileNode(e,t){var s;t.add(e.element);let r=this.meshMap.get(e.element);if(!r){const n=new d.PlaneGeometry(1,1);let a;a=L.create("BOX",e.styles,"",e.rect.width,e.rect.height,this.qualityFactor,e.isTraveler?(s=this.renderTarget)==null?void 0:s.texture:void 0),r=new d.Mesh(n,a),e.type==="TEXT"&&(r.name="BG_MESH"),this.scene.add(r),this.meshMap.set(e.element,r)}if(r.userData.domRect=e.rect,this.updateMeshProperties(r,e),this.updateMeshLayers(r,e),e.isTraveler&&r.layers.enable(28),e.type==="BOX")for(const n of e.children)this.reconcileNode(n,t);else e.type==="TEXT"&&this.reconcileTextChild(r,e)}reconcileTextChild(e,t){var o,l;let r=e.children.find(c=>c.name==="TEXT_CHILD");const s=JSON.stringify(t.textStyles),n=(o=r==null?void 0:r.userData)==null?void 0:o.styleHash;if(!r||t.dirtyMask&N||s!==n){r&&((l=r.material.map)==null||l.dispose(),r.geometry.dispose(),e.remove(r));const c=L.create("TEXT",t.textStyles,t.textContent||"",t.rect.width,t.rect.height,this.qualityFactor),g=new d.PlaneGeometry(1,1);r=new d.Mesh(g,c),r.name="TEXT_CHILD",r.userData={styleHash:s},this.updateMeshLayers(r,t),r.position.z=.005,e.add(r)}if(r){const c=e.userData.domRect,g=c.x+c.width/2,u=c.y+c.height/2,p=t.rect.x+t.rect.width/2,m=t.rect.y+t.rect.height/2,w=p-g,T=-(m-u);r.position.set(w,T,.005)}}updateMeshProperties(e,t){var m;const{rect:r,styles:s}=t,n=this.renderer.getPixelRatio(),a=this.renderer.domElement.width/n,o=this.renderer.domElement.height/n;e.scale.set(r.width,r.height,1);const l=.001;this.renderOrder++;const c=this.targetRect.left+window.scrollX,g=this.targetRect.top+window.scrollY,u=r.x-c,p=r.y-g;e.position.set(u-a/2+r.width/2,-p+o/2-r.height/2,s.zIndex+this.renderOrder*l),L.update(e.material,"BOX",t.styles,"",t.rect.width,t.rect.height,this.qualityFactor,t.isTraveler?(m=this.renderTarget)==null?void 0:m.texture:void 0)}updateMeshLayers(e,t){const r=(1-(t.visibility&C))*30;e.layers.set(r),t.visibility===(C|E)&&e.layers.enable(29)}captureRenderTarget(){const e=[];for(const l of this.meshMap.values())l.layers.mask&1<<28&&e.push(l);if(e.length===0)return;const t=new d.Color,r=this.renderer.getClearAlpha();this.renderer.getClearColor(t),this.renderer.setClearColor(0,0),this.renderer.setRenderTarget(this.renderTarget),this.renderer.clear(),this.renderer.autoClear=!1,this.renderer.setScissorTest(!0),this.camera.layers.set(29);const s=new d.Vector3,n=this.targetRect.width,a=this.targetRect.height,o=this.renderer.getPixelRatio();for(const l of e){s.setFromMatrixPosition(l.matrixWorld),s.project(this.camera);const c=(s.x+1)/2*n,g=(s.y+1)/2*a,u=l.scale.x,p=l.scale.y,m=c-u/2,w=g-p/2,T=m*this.qualityFactor/o,_=w*this.qualityFactor/o,O=u*this.qualityFactor/o,f=p*this.qualityFactor/o;this.renderer.setScissor(T,_,O,f),this.renderer.render(this.scene,this.camera)}this.renderer.setScissorTest(!1),this.renderer.autoClear=!0,this.renderer.setRenderTarget(null),this.camera.layers.set(28),this.renderer.setClearColor(t,r)}render(){this.renderTarget&&this.captureRenderTarget(),this.renderer.render(this.scene,this.camera)}showScissoredRenderTarget(){if(!this.renderTarget)return;const e=this.targetRect.width,t=this.targetRect.height,r=new d.PlaneGeometry(e,t),s=new d.MeshBasicMaterial({map:this.renderTarget.texture,side:d.DoubleSide,transparent:!0,opacity:.8}),n=new d.Mesh(r,s);n.position.set(0,0,90),n.layers.set(28),this.scene.add(n)}}function J(i){const e=document.createRange();e.selectNodeContents(i);const t=e.getBoundingClientRect();return{left:t.left,top:t.top,width:t.width,height:t.height}}function V(i){const e=parseFloat(i.fontSize);let t=parseFloat(i.lineHeight);isNaN(t)&&(t=e*1.2);let r=parseFloat(i.letterSpacing);return isNaN(r)&&(r=0),{font:`${i.fontStyle} ${i.fontWeight} ${i.fontSize} ${i.fontFamily}`,color:i.color,textAlign:i.textAlign||"start",textBaseline:"alphabetic",direction:i.direction||"inherit",lineHeight:t,letterSpacing:r}}function P(i,e=F|z|k|N|D,t,r){if(i.nodeType===Node.TEXT_NODE){const f=i;if(!f.textContent||!f.textContent.trim())return null;const R=f.textContent.replace(/\s+/g," ").trim();if(R.length===0)return null;const x=J(f);if(x.width===0||x.height===0)return null;const q=f.parentElement,I=q?window.getComputedStyle(q):null;return I?{id:Math.random().toString(36).substring(2,9),type:"TEXT",element:f,rect:{x:x.left+window.scrollX,y:x.top+window.scrollY,width:x.width,height:x.height},styles:{backgroundColor:"transparent",opacity:parseFloat(I.opacity),zIndex:0,borderRadius:"0px",borderColor:"transparent",borderWidth:"0px"},textContent:R,textStyles:V(I),dirtyMask:e,visibility:t,isTraveler:!1,children:[]}:null}const s=i,n=s.dataset.mirageFilter;let a=t,o=t;if(n){const f=new Set(n.split(/\s+/));for(const R of f)if(!Y.includes(R))throw new Error(`[MirageEngine] Invalid filter token: '${R}'. Expected one of: 'include-tree', 'exclude-tree', 'include-self', 'exclude-self', 'end'.`);if(f.has("end"))return null;if(f.has("include-tree")&&f.has("exclude-tree"))throw new Error("[MirageEngine] Conflicting filters: 'include-tree' and 'exclude-tree' cannot be used together on the same element.");if(f.has("include-self")&&f.has("exclude-self"))throw new Error("[MirageEngine] Conflicting filters: 'include-self' and 'exclude-self' cannot be used together on the same element.");f.has("include-tree")?a=a|C:f.has("exclude-tree")&&(a=a&~C),o=a,f.has("include-self")?o=o|C:f.has("exclude-self")&&(o=o&~C)}o=o|t&E;const l=s.dataset.mirageTravel;let c=!1;l&&new Set(l.split(/\s+/)).has("traveler")&&(o=o&~E,a=a&~E,c=!0);const g=s.getBoundingClientRect(),u=window.getComputedStyle(s);if(g.width===0||g.height===0||u.display==="none")return null;let p=s.getAttribute("data-mid");p||(p=Math.random().toString(36).substring(2,11),s.setAttribute("data-mid",p));const m=parseInt(u.zIndex),w={backgroundColor:u.backgroundColor,opacity:parseFloat(u.opacity),zIndex:isNaN(m)?0:m,borderRadius:u.borderRadius,borderColor:u.borderColor,borderWidth:u.borderWidth};let T,_;const O=[];return Array.from(s.childNodes).forEach(f=>{const R=f.nodeType===Node.TEXT_NODE?o:a,x=P(f,e,R);x&&O.push(x)}),{id:p,type:"BOX",element:s,rect:{x:g.left+window.scrollX,y:g.top+window.scrollY,width:g.width,height:g.height},styles:w,textContent:T,textStyles:_,dirtyMask:e,visibility:o,isTraveler:c,children:O}}class Q{constructor(e,t,r){h(this,"target");h(this,"renderer");h(this,"filter");h(this,"observer");h(this,"isDomDirty",!1);h(this,"isRunning",!1);h(this,"isTravelEnabled",!1);h(this,"pendingMask",M);h(this,"mutationTimer",null);h(this,"cssTimer",null);h(this,"resizeConfig");h(this,"resizeTimer",null);h(this,"isResizing",!1);h(this,"onTransitionFinished",e=>{this.target.contains(e.target)&&this.mutationTimer===null&&(this.cssTimer&&clearTimeout(this.cssTimer),this.pendingMask|=F|z,this.cssTimer=window.setTimeout(()=>{this.isDomDirty=!0,this.cssTimer=null},50))});h(this,"onWindowResize",()=>{if(!this.resizeConfig.enabled){this.isDomDirty=!0;return}this.isResizing||(this.isResizing=!0,this.resizeConfig.onStart&&this.resizeConfig.onStart()),this.resizeTimer&&clearTimeout(this.resizeTimer),this.resizeTimer=window.setTimeout(()=>{this.isDomDirty=!0,this.resizeConfig.onEnd&&this.resizeConfig.onEnd(),this.isResizing=!1,this.resizeTimer=null},this.resizeConfig.delay)});h(this,"renderLoop",()=>{this.isRunning&&(this.isDomDirty&&this.forceUpdateScene(),this.renderer.render(),requestAnimationFrame(this.renderLoop))});var n,a;this.target=e,this.renderer=t,this.filter=r.filter;const s=(n=r.resizeDebounce)!=null?n:!0;s===!1?this.resizeConfig={enabled:!1,delay:0}:s===!0?this.resizeConfig={enabled:!0,delay:150}:this.resizeConfig={enabled:!0,delay:(a=s.delay)!=null?a:150,onStart:s.onStart,onEnd:s.onEnd},this.observer=new MutationObserver(o=>{let l=M;for(const c of o)c.type==="childList"?l|=D:c.type==="attributes"&&(c.attributeName==="style"||c.attributeName==="class")&&(l|=F|z);if(l!==M){if(this.pendingMask|=l,l&D){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 e=document.querySelector("[data-mirage-travel='traveler']")!==null;e&&!this.isTravelEnabled&&(this.isTravelEnabled=!0,this.renderer.createRenderTarget());const t=P(this.target,this.pendingMask,e?C|E:C,this.filter);t&&this.renderer.syncScene(t),this.pendingMask=M}}class Z{constructor(e,t){h(this,"renderer");h(this,"syncer");h(this,"target");var s,n,a;this.target=e;let r;if(t.mode==="duplicate"?r=(n=(s=t.container)!=null?s:this.target.parentElement)!=null?n:void 0:r=(a=this.target.parentElement)!=null?a:void 0,!r)throw new Error("[Mirage] Cannot find a container (parent or option).");this.renderer=new j(this.target,t,r),this.renderer.mount(),this.syncer=new Q(this.target,this.renderer,t)}start(){this.syncer.start()}stop(){this.syncer.stop()}dispose(){this.syncer.stop(),this.renderer.dispose()}}class K{constructor(e,t){h(this,"_engine");if(!e)throw new Error("[Mirage] Target element is required.");this._engine=new Z(e,t)}start(){this._engine.start()}stop(){this._engine.stop()}destroy(){this._engine.dispose()}}v.Mirage=K,Object.defineProperty(v,Symbol.toStringTag,{value:"Module"})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mirage-engine",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
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,8 +21,8 @@
21
21
  "dist"
22
22
  ],
23
23
  "dependencies": {
24
- "@mirage-engine/painter": "0.2.3",
25
- "@mirage-engine/core": "0.0.2"
24
+ "@mirage-engine/painter": "0.3.0",
25
+ "@mirage-engine/core": "0.2.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "three": "^0.150.0"