@mirage-engine/core 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # @mirage-engine/core
2
+
3
+ ## 0.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - a05073c: core package separation
8
+ - Updated dependencies [a05073c]
9
+ - @mirage-engine/painter@0.2.3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 siwoo lee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,20 @@
1
+ <p align="center">
2
+ <a href="https://www.npmjs.com/package/mirage-engine">
3
+ <img src="https://raw.githubusercontent.com/dltldn333/MirageEngine/main/.github/assets/mirage-engine.png" width="300px">
4
+ </a>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@mirage-engine/core"><img src="https://img.shields.io/npm/v/@mirage-engine/core.svg?color=black"></a>
9
+ <a href="https://www.npmjs.org/package/@mirage-engine/core"><img src="https://img.shields.io/npm/dm/@mirage-engine/core.svg?color=black"></a>
10
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg?color=black"></a>
11
+ <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-Ready-blue.svg?color=black"></a>
12
+ </p>
13
+
14
+ # MirageEngine
15
+
16
+ > **A core of mirage-engine**
17
+
18
+ This package does not work alone. Please install [mirage-engine](https://www.npmjs.com/package/mirage-engine) instead.
19
+
20
+ **License | MIT © dltldn333**
@@ -0,0 +1,487 @@
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(`
8
+ `), s = [];
9
+ return i.forEach((n) => {
10
+ const a = n.split(" ");
11
+ let o = a[0];
12
+ 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);
15
+ }
16
+ s.push(o);
17
+ }), s;
18
+ }
19
+ function S(r, t, e, i, s = 2) {
20
+ const n = document.createElement("canvas"), a = n.getContext("2d");
21
+ if (!a)
22
+ throw new Error("[Mirage] Failed to create canvas context");
23
+ 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, f) => {
27
+ const y = f * p + 2;
28
+ let g = 0;
29
+ t.textAlign === "center" ? g = e / 2 : t.textAlign === "right" && (g = e), a.textAlign = t.textAlign, a.fillText(m, g, y);
30
+ });
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;
33
+ }
34
+ function x(r) {
35
+ return typeof r == "number" ? r : parseFloat(r) || 0;
36
+ }
37
+ const O = `
38
+ varying vec2 vUv;
39
+ void main() {
40
+ vUv = uv;
41
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
42
+ }
43
+ `, A = `
44
+ varying vec2 vUv;
45
+
46
+ uniform vec2 uSize;
47
+ uniform float uRadius;
48
+ uniform float uBorderWidth;
49
+ uniform vec3 uColor;
50
+ uniform vec3 uBorderColor;
51
+ uniform float uOpacity;
52
+ uniform float uBgOpacity;
53
+
54
+ // SDF 박스 함수
55
+ float sdRoundedBox(vec2 p, vec2 b, float r) {
56
+ vec2 q = abs(p) - b + r;
57
+ return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
58
+ }
59
+
60
+ void main() {
61
+ vec2 p = (vUv - 0.5) * uSize;
62
+ vec2 halfSize = uSize * 0.5;
63
+
64
+ float d = sdRoundedBox(p, halfSize, uRadius);
65
+
66
+ float smoothEdge = 1.0;
67
+
68
+ float fillAlpha = 1.0 - smoothstep(-uBorderWidth - smoothEdge, -uBorderWidth, d);
69
+
70
+ float borderAlpha = 0.0;
71
+
72
+ if (uBorderWidth > 0.01) {
73
+ borderAlpha = (1.0 - smoothstep(0.0, smoothEdge, d)) - fillAlpha;
74
+ }
75
+
76
+ vec3 color = uColor;
77
+ float totalAlpha = borderAlpha + fillAlpha;
78
+
79
+ if (totalAlpha > 0.001) {
80
+ color = mix(uColor, uBorderColor, borderAlpha / totalAlpha);
81
+ }
82
+
83
+ float shapeAlpha = borderAlpha + (fillAlpha * uBgOpacity);
84
+ float finalOpacity = shapeAlpha * uOpacity;
85
+
86
+ if (finalOpacity < 0.001) discard;
87
+
88
+ gl_FragColor = vec4(color, finalOpacity);
89
+ }
90
+ `;
91
+ function w(r) {
92
+ if (!r || r === "transparent")
93
+ return { color: new h.Color(16777215), alpha: 0 };
94
+ const t = r.match(
95
+ /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/
96
+ );
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 };
100
+ }
101
+ return { color: new h.Color(r), alpha: 1 };
102
+ }
103
+ function I(r, t, e) {
104
+ var o;
105
+ const i = w(r.backgroundColor), s = w(r.borderColor), n = {
106
+ uSize: { value: new h.Vector2(t, e) },
107
+ uRadius: { value: x(r.borderRadius) },
108
+ uBorderWidth: { value: x(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 }
113
+ };
114
+ return new h.ShaderMaterial({
115
+ uniforms: n,
116
+ vertexShader: O,
117
+ fragmentShader: A,
118
+ transparent: !0,
119
+ side: h.FrontSide
120
+ // for better performance
121
+ });
122
+ }
123
+ function L(r, t, e, i) {
124
+ var a;
125
+ const s = w(t.backgroundColor), n = w(t.borderColor);
126
+ r.uniforms.uSize.value.set(e, i), r.uniforms.uRadius.value = x(t.borderRadius), r.uniforms.uBorderWidth.value = x(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;
127
+ }
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,
137
+ s,
138
+ n
139
+ );
140
+ return new h.MeshBasicMaterial({
141
+ map: a,
142
+ transparent: !0,
143
+ side: h.FrontSide,
144
+ color: 16777215
145
+ });
146
+ }
147
+ return new h.MeshBasicMaterial({ visible: !1 });
148
+ },
149
+ update(r, t, e, i, s, n, a = 2) {
150
+ if (t === "BOX")
151
+ L(
152
+ r,
153
+ e,
154
+ s,
155
+ n
156
+ );
157
+ else if (t === "TEXT") {
158
+ const o = r;
159
+ o.map && o.map.dispose();
160
+ const l = S(
161
+ i || "",
162
+ e,
163
+ s,
164
+ n,
165
+ a
166
+ );
167
+ o.map = l, o.needsUpdate = !0;
168
+ }
169
+ }
170
+ };
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();
187
+ const s = this.targetRect.width, n = this.targetRect.height;
188
+ this.camera = new h.OrthographicCamera(
189
+ s / -2,
190
+ s / 2,
191
+ n / 2,
192
+ n / -2,
193
+ 1,
194
+ 1e3
195
+ ), this.camera.position.z = 100, this.renderer = new h.WebGLRenderer({
196
+ canvas: this.canvas,
197
+ alpha: !0,
198
+ antialias: !0
199
+ }), this.renderer.setPixelRatio(window.devicePixelRatio), this.renderer.setSize(s, n), this.applyTextQuality((l = e.textQuality) != null ? l : "medium");
200
+ }
201
+ applyTextQuality(t) {
202
+ if (typeof t == "number") {
203
+ this.textQualityFactor = Math.max(0.1, t);
204
+ return;
205
+ }
206
+ switch (t) {
207
+ case "low":
208
+ this.textQualityFactor = 1;
209
+ break;
210
+ case "high":
211
+ this.textQualityFactor = 4;
212
+ break;
213
+ case "medium":
214
+ default:
215
+ this.textQualityFactor = 2;
216
+ break;
217
+ }
218
+ }
219
+ mount() {
220
+ this.mountContainer.appendChild(this.canvas), this.canvas.style.zIndex = this.customZIndex, this.canvas.style.pointerEvents = this.mode === "overlay" ? "none" : "auto", this.updateCanvasLayout();
221
+ }
222
+ updateCanvasLayout() {
223
+ 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");
224
+ }
225
+ dispose() {
226
+ this.renderer.dispose(), this.canvas.remove();
227
+ }
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();
230
+ }
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;
234
+ const n = /* @__PURE__ */ new Set();
235
+ this.reconcileNode(t, n);
236
+ 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));
238
+ }
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(
244
+ "BOX",
245
+ t.styles,
246
+ "",
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);
251
+ }
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);
255
+ else
256
+ t.type === "TEXT" && this.reconcileTextChild(i, t);
257
+ }
258
+ reconcileTextChild(t, e) {
259
+ var o, l;
260
+ let i = t.children.find(
261
+ (d) => d.name === "TEXT_CHILD"
262
+ );
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(
267
+ "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);
275
+ }
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, f = e.rect.y + e.rect.height / 2, y = m - p, g = -(f - u);
278
+ i.position.set(y, g, 5e-3);
279
+ }
280
+ }
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);
284
+ const l = 1e-3;
285
+ 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,
290
+ s.zIndex + this.renderOrder * l
291
+ ), T.update(
292
+ t.material,
293
+ "BOX",
294
+ e.styles,
295
+ "",
296
+ e.rect.width,
297
+ e.rect.height
298
+ );
299
+ }
300
+ render() {
301
+ this.renderer.render(this.scene, this.camera);
302
+ }
303
+ }
304
+ function N(r) {
305
+ const t = document.createRange();
306
+ t.selectNodeContents(r);
307
+ const e = t.getBoundingClientRect();
308
+ return {
309
+ left: e.left,
310
+ top: e.top,
311
+ width: e.width,
312
+ height: e.height
313
+ };
314
+ }
315
+ function k(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",
324
+ textBaseline: "alphabetic",
325
+ direction: r.direction || "inherit",
326
+ lineHeight: e,
327
+ letterSpacing: i
328
+ };
329
+ }
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())
334
+ return null;
335
+ const m = u.textContent.replace(/\s+/g, " ").trim();
336
+ if (m.length === 0)
337
+ return null;
338
+ const f = N(u);
339
+ if (f.width === 0 || f.height === 0)
340
+ return null;
341
+ const y = u.parentElement, g = y ? window.getComputedStyle(y) : null;
342
+ return g ? {
343
+ id: Math.random().toString(36).substring(2, 9),
344
+ type: "TEXT",
345
+ element: u,
346
+ rect: {
347
+ x: f.left + window.scrollX,
348
+ y: f.top + window.scrollY,
349
+ width: f.width,
350
+ height: f.height
351
+ },
352
+ styles: {
353
+ backgroundColor: "transparent",
354
+ opacity: parseFloat(g.opacity),
355
+ zIndex: 0,
356
+ borderRadius: "0px",
357
+ borderColor: "transparent",
358
+ borderWidth: "0px"
359
+ },
360
+ textContent: m,
361
+ textStyles: k(g),
362
+ dirtyMask: t,
363
+ children: []
364
+ } : null;
365
+ }
366
+ const e = r, i = e.getBoundingClientRect(), s = window.getComputedStyle(e);
367
+ if (i.width === 0 || i.height === 0 || s.display === "none")
368
+ 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
378
+ };
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);
384
+ }), {
385
+ id: n,
386
+ type: "BOX",
387
+ element: e,
388
+ rect: {
389
+ x: i.left + window.scrollX,
390
+ y: i.top + window.scrollY,
391
+ width: i.width,
392
+ height: i.height
393
+ },
394
+ styles: o,
395
+ textContent: l,
396
+ textStyles: d,
397
+ dirtyMask: t,
398
+ children: p
399
+ };
400
+ }
401
+ class P {
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(() => {
413
+ this.isDomDirty = !0, this.cssTimer = null;
414
+ }, 50));
415
+ });
416
+ c(this, "onWindowResize", () => {
417
+ this.renderer.setSize(window.innerWidth, window.innerHeight), this.isDomDirty = !0;
418
+ });
419
+ c(this, "renderLoop", () => {
420
+ this.isRunning && (this.isDomDirty && this.forceUpdateScene(), this.renderer.render(), requestAnimationFrame(this.renderLoop));
421
+ });
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) {
428
+ this.clearTimers(), console.log("Structural Change detected"), this.isDomDirty = !0;
429
+ return;
430
+ }
431
+ this.mutationTimer && clearTimeout(this.mutationTimer), this.mutationTimer = window.setTimeout(() => {
432
+ this.mutationTimer = null, this.isDomDirty = !0;
433
+ }, 200);
434
+ }
435
+ });
436
+ }
437
+ start() {
438
+ this.isRunning || (this.isRunning = !0, this.observer.observe(this.target, {
439
+ childList: !0,
440
+ subtree: !0,
441
+ attributes: !0,
442
+ characterData: !0
443
+ }), this.target.addEventListener("transitionend", this.onTransitionFinished), this.target.addEventListener("animationend", this.onTransitionFinished), window.addEventListener("resize", this.onWindowResize), this.forceUpdateScene(), this.renderLoop());
444
+ }
445
+ stop() {
446
+ 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);
447
+ }
448
+ clearTimers() {
449
+ this.mutationTimer && (clearTimeout(this.mutationTimer), this.mutationTimer = null), this.cssTimer && (clearTimeout(this.cssTimer), this.cssTimer = null);
450
+ }
451
+ forceUpdateScene() {
452
+ this.isDomDirty = !1;
453
+ const t = M(this.target, this.pendingMask);
454
+ t && this.renderer.syncScene(t), this.pendingMask = v;
455
+ }
456
+ }
457
+ class Y {
458
+ constructor(t, e) {
459
+ c(this, "renderer");
460
+ c(this, "syncer");
461
+ c(this, "target");
462
+ 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)
466
+ 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 P(this.target, this.renderer);
468
+ }
469
+ start() {
470
+ this.syncer.start();
471
+ }
472
+ stop() {
473
+ this.syncer.stop();
474
+ }
475
+ dispose() {
476
+ this.syncer.stop(), this.renderer.dispose();
477
+ }
478
+ }
479
+ export {
480
+ E as DIRTY_CONTENT,
481
+ v as DIRTY_NONE,
482
+ R as DIRTY_RECT,
483
+ C as DIRTY_STRUCTURE,
484
+ b as DIRTY_STYLE,
485
+ D as DIRTY_ZINDEX,
486
+ Y as Engine
487
+ };
@@ -0,0 +1,55 @@
1
+ (function(d,f){typeof exports=="object"&&typeof module!="undefined"?f(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],f):(d=typeof globalThis!="undefined"?globalThis:d||self,f(d.MirageEngine={},d.THREE))})(this,function(d,f){"use strict";var H=Object.defineProperty;var W=(d,f,T)=>f in d?H(d,f,{enumerable:!0,configurable:!0,writable:!0,value:T}):d[f]=T;var c=(d,f,T)=>(W(d,typeof f!="symbol"?f+"":f,T),T);function T(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=T(f),x=0,R=1,b=2,B=4,C=8,M=16;function I(r,t,e){const i=t.split(`
2
+ `),n=[];return i.forEach(s=>{const a=s.split(" ");let o=a[0];for(let l=1;l<a.length;l++){const u=a[l];r.measureText(o+" "+u).width<e?o+=" "+u:(n.push(o),o=u)}n.push(o)}),n}function O(r,t,e,i,n=2){const s=document.createElement("canvas"),a=s.getContext("2d");if(!a)throw new Error("[Mirage] Failed to create canvas context");const l=(window.devicePixelRatio||1)*n;s.width=e*l,s.height=i*l,a.scale(l,l),a.font=t.font,a.fillStyle=t.color,a.textBaseline="top",a.globalAlpha=1;const u=I(a,r,e),m=t.lineHeight;u.forEach((g,y)=>{const w=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,w)});const p=new h.CanvasTexture(s);return p.colorSpace=h.SRGBColorSpace,p.minFilter=h.LinearFilter,p.magFilter=h.LinearFilter,p.needsUpdate=!0,p}function S(r){return typeof r=="number"?r:parseFloat(r)||0}const F=`
3
+ varying vec2 vUv;
4
+ void main() {
5
+ vUv = uv;
6
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
7
+ }
8
+ `,N=`
9
+ varying vec2 vUv;
10
+
11
+ uniform vec2 uSize;
12
+ uniform float uRadius;
13
+ uniform float uBorderWidth;
14
+ uniform vec3 uColor;
15
+ uniform vec3 uBorderColor;
16
+ uniform float uOpacity;
17
+ uniform float uBgOpacity;
18
+
19
+ // SDF 박스 함수
20
+ float sdRoundedBox(vec2 p, vec2 b, float r) {
21
+ vec2 q = abs(p) - b + r;
22
+ return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
23
+ }
24
+
25
+ void main() {
26
+ vec2 p = (vUv - 0.5) * uSize;
27
+ vec2 halfSize = uSize * 0.5;
28
+
29
+ float d = sdRoundedBox(p, halfSize, uRadius);
30
+
31
+ float smoothEdge = 1.0;
32
+
33
+ float fillAlpha = 1.0 - smoothstep(-uBorderWidth - smoothEdge, -uBorderWidth, d);
34
+
35
+ float borderAlpha = 0.0;
36
+
37
+ if (uBorderWidth > 0.01) {
38
+ borderAlpha = (1.0 - smoothstep(0.0, smoothEdge, d)) - fillAlpha;
39
+ }
40
+
41
+ vec3 color = uColor;
42
+ float totalAlpha = borderAlpha + fillAlpha;
43
+
44
+ if (totalAlpha > 0.001) {
45
+ color = mix(uColor, uBorderColor, borderAlpha / totalAlpha);
46
+ }
47
+
48
+ float shapeAlpha = borderAlpha + (fillAlpha * uBgOpacity);
49
+ float finalOpacity = shapeAlpha * uOpacity;
50
+
51
+ if (finalOpacity < 0.001) discard;
52
+
53
+ gl_FragColor = vec4(color, finalOpacity);
54
+ }
55
+ `;function E(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),n=parseInt(t[3],10),s=t[4]!==void 0?parseFloat(t[4]):1;return{color:new h.Color(`rgb(${e}, ${i}, ${n})`),alpha:s}}return{color:new h.Color(r),alpha:1}}function L(r,t,e){var o;const i=E(r.backgroundColor),n=E(r.borderColor),s={uSize:{value:new h.Vector2(t,e)},uRadius:{value:S(r.borderRadius)},uBorderWidth:{value:S(r.borderWidth)},uColor:{value:i.color},uBorderColor:{value:n.color},uOpacity:{value:(o=r.opacity)!=null?o:1},uBgOpacity:{value:i.alpha}};return new h.ShaderMaterial({uniforms:s,vertexShader:F,fragmentShader:N,transparent:!0,side:h.FrontSide})}function A(r,t,e,i){var a;const n=E(t.backgroundColor),s=E(t.borderColor);r.uniforms.uSize.value.set(e,i),r.uniforms.uRadius.value=S(t.borderRadius),r.uniforms.uBorderWidth.value=S(t.borderWidth),r.uniforms.uColor.value.copy(n.color),r.uniforms.uBorderColor.value.copy(s.color),r.uniforms.uOpacity.value=(a=t.opacity)!=null?a:1,r.uniforms.uBgOpacity.value=n.alpha}const D={create(r,t,e,i,n,s=2){if(r==="BOX")return L(t,i,n);if(r==="TEXT"){const a=O(e||"",t,i,n,s);return new h.MeshBasicMaterial({map:a,transparent:!0,side:h.FrontSide,color:16777215})}return new h.MeshBasicMaterial({visible:!1})},update(r,t,e,i,n,s,a=2){if(t==="BOX")A(r,e,n,s);else if(t==="TEXT"){const o=r;o.map&&o.map.dispose();const l=O(i||"",e,n,s,a);o.map=l,o.needsUpdate=!0}}};class X{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 n=this.targetRect.width,s=this.targetRect.height;this.camera=new h.OrthographicCamera(n/-2,n/2,s/2,s/-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(n,s),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,n=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()):n?(this.targetRect=e,this.updateCanvasLayout()):this.targetRect=e,this.renderOrder=0;const s=new Set;this.reconcileNode(t,s);for(const[a,o]of this.meshMap.entries())s.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 n=new h.PlaneGeometry(1,1),s=D.create("BOX",t.styles,"",t.rect.width,t.rect.height);i=new h.Mesh(n,s),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 n of t.children)this.reconcileNode(n,e);else t.type==="TEXT"&&this.reconcileTextChild(i,t)}reconcileTextChild(t,e){var o,l;let i=t.children.find(u=>u.name==="TEXT_CHILD");const n=JSON.stringify(e.textStyles),s=(o=i==null?void 0:i.userData)==null?void 0:o.styleHash;if(!i||e.dirtyMask&M||n!==s){i&&((l=i.material.map)==null||l.dispose(),i.geometry.dispose(),t.remove(i));const u=D.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,u),i.name="TEXT_CHILD",i.userData={styleHash:n},i.position.z=.005,t.add(i)}if(i){const u=t.userData.domRect,m=u.x+u.width/2,p=u.y+u.height/2,g=e.rect.x+e.rect.width/2,y=e.rect.y+e.rect.height/2,w=g-m,v=-(y-p);i.position.set(w,v,.005)}}updateMeshProperties(t,e){const{rect:i,styles:n}=e,s=this.renderer.getPixelRatio(),a=this.renderer.domElement.width/s,o=this.renderer.domElement.height/s;t.scale.set(i.width,i.height,1);const l=.001;this.renderOrder++;const u=this.targetRect.left+window.scrollX,m=this.targetRect.top+window.scrollY,p=i.x-u,g=i.y-m;t.position.set(p-a/2+i.width/2,-g+o/2-i.height/2,n.zIndex+this.renderOrder*l),D.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 Y(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 z(r,t=R|b|B|M|C){if(r.nodeType===Node.TEXT_NODE){const p=r;if(!p.textContent||!p.textContent.trim())return null;const g=p.textContent.replace(/\s+/g," ").trim();if(g.length===0)return null;const y=_(p);if(y.width===0||y.height===0)return null;const w=p.parentElement,v=w?window.getComputedStyle(w):null;return v?{id:Math.random().toString(36).substring(2,9),type:"TEXT",element:p,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:Y(v),dirtyMask:t,children:[]}:null}const e=r,i=e.getBoundingClientRect(),n=window.getComputedStyle(e);if(i.width===0||i.height===0||n.display==="none")return null;let s=e.getAttribute("data-mid");s||(s=Math.random().toString(36).substring(2,11),e.setAttribute("data-mid",s));const a=parseInt(n.zIndex),o={backgroundColor:n.backgroundColor,opacity:parseFloat(n.opacity),zIndex:isNaN(a)?0:a,borderRadius:n.borderRadius,borderColor:n.borderColor,borderWidth:n.borderWidth};let l,u;const m=[];return Array.from(e.childNodes).forEach(p=>{const g=z(p,t);g&&m.push(g)}),{id:s,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:u,dirtyMask:t,children:m}}class k{constructor(t,e){c(this,"target");c(this,"renderer");c(this,"observer");c(this,"isDomDirty",!1);c(this,"isRunning",!1);c(this,"pendingMask",x);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|=R|b,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 n=x;for(const s of i)s.type==="childList"?n|=C:s.type==="attributes"&&(s.attributeName==="style"||s.attributeName==="class")&&(n|=R|b);if(n!==x){if(this.pendingMask|=n,n&C){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=z(this.target,this.pendingMask);t&&this.renderer.syncScene(t),this.pendingMask=x}}class P{constructor(t,e){c(this,"renderer");c(this,"syncer");c(this,"target");var n,s,a;this.target=t;let i;if(e.mode==="duplicate"?i=(s=(n=e.container)!=null?n:this.target.parentElement)!=null?s: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 X(this.target,e,i),this.renderer.mount(),this.syncer=new k(this.target,this.renderer)}start(){this.syncer.start()}stop(){this.syncer.stop()}dispose(){this.syncer.stop(),this.renderer.dispose()}}d.DIRTY_CONTENT=M,d.DIRTY_NONE=x,d.DIRTY_RECT=R,d.DIRTY_STRUCTURE=C,d.DIRTY_STYLE=b,d.DIRTY_ZINDEX=B,d.Engine=P,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})});
@@ -0,0 +1,11 @@
1
+ import { CoreConfig } from '../types';
2
+
3
+ export declare class Engine {
4
+ private renderer;
5
+ private syncer;
6
+ private target;
7
+ constructor(target: HTMLElement, config: CoreConfig);
8
+ start(): void;
9
+ stop(): void;
10
+ dispose(): void;
11
+ }
@@ -0,0 +1,20 @@
1
+ import { Renderer } from '../renderer/Renderer';
2
+
3
+ export declare class Syncer {
4
+ private target;
5
+ private renderer;
6
+ private observer;
7
+ private isDomDirty;
8
+ private isRunning;
9
+ private pendingMask;
10
+ private mutationTimer;
11
+ private cssTimer;
12
+ constructor(target: HTMLElement, renderer: Renderer);
13
+ start(): void;
14
+ stop(): void;
15
+ private clearTimers;
16
+ private onTransitionFinished;
17
+ private onWindowResize;
18
+ private forceUpdateScene;
19
+ private renderLoop;
20
+ }
@@ -0,0 +1,3 @@
1
+ import { SceneNode } from '../types';
2
+
3
+ export declare function extractSceneGraph(sourceNode: HTMLElement | Node, initialMask?: number): SceneNode | null;
@@ -0,0 +1,2 @@
1
+ export { Engine } from './core/Engine';
2
+ export * from './types';