@mirage-engine/painter 1.0.2 → 1.0.4

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @mirage-engine/painter
2
2
 
3
+ ## 1.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 371f29d: native
8
+
9
+ ## 1.0.3
10
+
11
+ ### Patch Changes
12
+
13
+ - 5ff6ac0: bug fix
14
+
3
15
  ## 1.0.2
4
16
 
5
17
  ### Patch Changes
@@ -1,8 +1,8 @@
1
- var R = Object.defineProperty;
2
- var M = (r, e, n) => e in r ? R(r, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : r[e] = n;
3
- var w = (r, e, n) => (M(r, typeof e != "symbol" ? e + "" : e, n), n);
1
+ var k = Object.defineProperty;
2
+ var P = (r, e, t) => e in r ? k(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
3
+ var S = (r, e, t) => (P(r, typeof e != "symbol" ? e + "" : e, t), t);
4
4
  import * as d from "three";
5
- class I {
5
+ class A {
6
6
  /**
7
7
  * Calculates or retrieves the exact alphabetic baseline (in pixels) for the given font.
8
8
  * @param font CSS font shorthand string (e.g., "400 16px Inter")
@@ -11,125 +11,128 @@ class I {
11
11
  static getBaseline(e) {
12
12
  if (this.cache.has(e))
13
13
  return this.cache.get(e);
14
- const n = this.calculateBaselineFromDOM(e);
15
- return this.cache.set(e, n), n;
14
+ const t = this.calculateBaselineFromDOM(e);
15
+ return this.cache.set(e, t), t;
16
16
  }
17
17
  static calculateBaselineFromDOM(e) {
18
18
  if (typeof document > "u")
19
19
  return 0;
20
- const n = document.createElement("div"), a = document.createElement("span"), o = document.createElement("img");
21
- n.style.visibility = "hidden", n.style.position = "absolute", n.style.top = "0px", n.style.left = "0px", n.style.font = e, n.style.margin = "0", n.style.padding = "0", n.style.border = "none", a.style.margin = "0", a.style.padding = "0", a.style.border = "none", a.style.lineHeight = "normal", o.width = 1, o.height = 1, o.style.verticalAlign = "baseline", a.appendChild(document.createTextNode("Hidden Text")), n.appendChild(a), n.appendChild(o), document.body.appendChild(n);
22
- const i = o.offsetTop - a.offsetTop;
23
- return document.body.removeChild(n), i;
20
+ const t = document.createElement("div"), n = document.createElement("span"), o = document.createElement("img");
21
+ t.style.visibility = "hidden", t.style.position = "absolute", t.style.top = "0px", t.style.left = "0px", t.style.font = e, t.style.margin = "0", t.style.padding = "0", t.style.border = "none", n.style.margin = "0", n.style.padding = "0", n.style.border = "none", n.style.lineHeight = "normal", o.width = 1, o.height = 1, o.style.verticalAlign = "baseline", n.appendChild(document.createTextNode("Hidden Text")), t.appendChild(n), t.appendChild(o), document.body.appendChild(t);
22
+ const a = o.offsetTop - n.offsetTop;
23
+ return document.body.removeChild(t), a;
24
24
  }
25
25
  }
26
- w(I, "cache", /* @__PURE__ */ new Map());
27
- class k extends d.MeshBasicMaterial {
28
- constructor(n, a, o, i, t = 2) {
26
+ S(A, "cache", /* @__PURE__ */ new Map());
27
+ class G extends d.MeshBasicMaterial {
28
+ constructor(t, n, o, a, i = 2) {
29
29
  super({
30
30
  transparent: !0,
31
31
  side: d.FrontSide,
32
32
  color: 16777215
33
33
  });
34
- w(this, "canvas");
35
- w(this, "ctx");
36
- w(this, "qualityFactor");
34
+ S(this, "canvas");
35
+ S(this, "ctx");
36
+ S(this, "qualityFactor");
37
37
  if (this.canvas = document.createElement("canvas"), this.ctx = this.canvas.getContext("2d"), !this.ctx)
38
38
  throw new Error("[Mirage] Failed to create canvas context");
39
- this.qualityFactor = t, this.map = new d.CanvasTexture(this.canvas), this.map.colorSpace = d.LinearSRGBColorSpace, this.map.minFilter = d.LinearFilter, this.map.magFilter = d.LinearFilter, this.updateText(n, a, o, i);
39
+ this.qualityFactor = i, this.map = new d.CanvasTexture(this.canvas), this.map.colorSpace = d.LinearSRGBColorSpace, this.map.minFilter = d.LinearFilter, this.map.magFilter = d.LinearFilter, this.updateText(t, n, o, a);
40
40
  }
41
- wrapText(n, a) {
42
- const o = n.split(`
43
- `), i = [];
44
- return o.forEach((t) => {
45
- const c = t.match(/[^\s\-]+\-?|\-|\s+/g) || [];
41
+ wrapText(t, n) {
42
+ const o = t.split(`
43
+ `), a = [];
44
+ return o.forEach((i) => {
45
+ const c = i.match(/[^\s\-]+\-?|\-|\s+/g) || [];
46
46
  if (c.length === 0) {
47
- i.push("");
47
+ a.push("");
48
48
  return;
49
49
  }
50
50
  let s = c[0];
51
51
  for (let l = 1; l < c.length; l++) {
52
52
  const u = c[l];
53
- this.ctx.measureText(s + u).width <= a + 2 ? s += u : (s && i.push(s), s = u.trimStart());
53
+ this.ctx.measureText(s + u).width <= n + 2 ? s += u : (s && a.push(s), s = u.trimStart());
54
54
  }
55
- s && i.push(s);
56
- }), i;
55
+ s && a.push(s);
56
+ }), a;
57
57
  }
58
- updateText(n, a, o, i, t) {
59
- t !== void 0 && (this.qualityFactor = t);
60
- const s = (window.devicePixelRatio || 1) * this.qualityFactor, l = o * s, u = i * s;
61
- this.canvas.width !== l || this.canvas.height !== u ? (this.canvas.width = l, this.canvas.height = u) : this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height), this.ctx.setTransform(s, 0, 0, s, 0, 0), this.ctx.font = a.font, this.ctx.fillStyle = a.color, this.ctx.textBaseline = "alphabetic", this.ctx.globalAlpha = 1;
62
- const p = this.wrapText(n, o), b = a.lineHeight, v = I.getBaseline(a.font);
63
- p.forEach((f, m) => {
64
- const S = m * b + v;
65
- let C = 0;
66
- a.textAlign === "center" ? C = o / 2 : a.textAlign === "right" && (C = o), this.ctx.textAlign = a.textAlign, this.ctx.fillText(f, C, S);
58
+ updateText(t, n, o, a, i) {
59
+ i !== void 0 && (this.qualityFactor = i);
60
+ const s = (window.devicePixelRatio || 1) * this.qualityFactor, l = o * s, u = a * s;
61
+ this.canvas.width !== l || this.canvas.height !== u ? (this.canvas.width = l, this.canvas.height = u) : this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height), this.ctx.setTransform(s, 0, 0, s, 0, 0), this.ctx.font = n.font, this.ctx.fillStyle = n.color, this.ctx.textBaseline = "alphabetic", this.ctx.globalAlpha = 1;
62
+ const p = this.wrapText(t, o), m = n.lineHeight, v = A.getBaseline(n.font);
63
+ p.forEach((f, y) => {
64
+ const C = y * m + v;
65
+ let b = 0;
66
+ n.textAlign === "center" ? b = o / 2 : n.textAlign === "right" && (b = o), this.ctx.textAlign = n.textAlign, this.ctx.fillText(f, b, C);
67
67
  }), this.map && (this.map.needsUpdate = !0);
68
68
  }
69
69
  dispose() {
70
70
  this.map && this.map.dispose(), super.dispose();
71
71
  }
72
72
  }
73
- function x(r) {
74
- return typeof r == "number" ? r : parseFloat(r) || 0;
73
+ function x(r, e = 0) {
74
+ if (typeof r == "number")
75
+ return r;
76
+ const t = parseFloat(r) || 0;
77
+ return typeof r == "string" && r.includes("%") ? t / 100 * e : t;
75
78
  }
76
- function y(r) {
79
+ function w(r) {
77
80
  if (!r || r === "transparent")
78
81
  return { color: new d.Color(16777215), alpha: 0 };
79
82
  const e = r.match(
80
83
  /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/
81
84
  );
82
85
  if (e) {
83
- const n = parseInt(e[1], 10), a = parseInt(e[2], 10), o = parseInt(e[3], 10), i = e[4] !== void 0 ? parseFloat(e[4]) : 1;
84
- return { color: new d.Color(`rgb(${n}, ${a}, ${o})`), alpha: i };
86
+ const t = parseInt(e[1], 10), n = parseInt(e[2], 10), o = parseInt(e[3], 10), a = e[4] !== void 0 ? parseFloat(e[4]) : 1;
87
+ return { color: new d.Color(`rgb(${t}, ${n}, ${o})`), alpha: a };
85
88
  }
86
89
  return { color: new d.Color(r), alpha: 1 };
87
90
  }
88
- function P(r) {
91
+ function E(r) {
89
92
  const e = [];
90
- let n = "", a = 0;
93
+ let t = "", n = 0;
91
94
  for (let o = 0; o < r.length; o++) {
92
- const i = r[o];
93
- if (i === "(")
94
- a++;
95
- else if (i === ")")
96
- a--;
97
- else if (i === "," && a === 0) {
98
- e.push(n.trim()), n = "";
95
+ const a = r[o];
96
+ if (a === "(")
97
+ n++;
98
+ else if (a === ")")
99
+ n--;
100
+ else if (a === "," && n === 0) {
101
+ e.push(t.trim()), t = "";
99
102
  continue;
100
103
  }
101
- n += i;
104
+ t += a;
102
105
  }
103
- return n && e.push(n.trim()), e;
106
+ return t && e.push(t.trim()), e;
104
107
  }
105
- function G(r) {
108
+ function L(r) {
106
109
  if (!r || typeof r != "string" || !r.includes("linear-gradient"))
107
110
  return null;
108
111
  const e = r.match(/linear-gradient\((.*)\)/);
109
112
  if (!e)
110
113
  return null;
111
- const n = e[1], a = P(n);
112
- let o = Math.PI, i = 0;
113
- const t = a[0].trim();
114
- if (t.startsWith("to "))
115
- t === "to top" ? o = 0 : t === "to right" ? o = Math.PI / 2 : t === "to bottom" ? o = Math.PI : t === "to left" ? o = Math.PI * 1.5 : t === "to top right" || t === "to right top" ? o = Math.PI / 4 : t === "to bottom right" || t === "to right bottom" ? o = Math.PI * 0.75 : t === "to bottom left" || t === "to left bottom" ? o = Math.PI * 1.25 : (t === "to top left" || t === "to left top") && (o = Math.PI * 1.75), i = 1;
116
- else if (t.endsWith("deg") || t.endsWith("rad") || t.endsWith("turn")) {
117
- const s = parseFloat(t);
118
- t.endsWith("deg") ? o = s * (Math.PI / 180) : t.endsWith("rad") ? o = s : t.endsWith("turn") && (o = s * Math.PI * 2), i = 1;
114
+ const t = e[1], n = E(t);
115
+ let o = Math.PI, a = 0;
116
+ const i = n[0].trim();
117
+ if (i.startsWith("to "))
118
+ i === "to top" ? o = 0 : i === "to right" ? o = Math.PI / 2 : i === "to bottom" ? o = Math.PI : i === "to left" ? o = Math.PI * 1.5 : i === "to top right" || i === "to right top" ? o = Math.PI / 4 : i === "to bottom right" || i === "to right bottom" ? o = Math.PI * 0.75 : i === "to bottom left" || i === "to left bottom" ? o = Math.PI * 1.25 : (i === "to top left" || i === "to left top") && (o = Math.PI * 1.75), a = 1;
119
+ else if (i.endsWith("deg") || i.endsWith("rad") || i.endsWith("turn")) {
120
+ const s = parseFloat(i);
121
+ i.endsWith("deg") ? o = s * (Math.PI / 180) : i.endsWith("rad") ? o = s : i.endsWith("turn") && (o = s * Math.PI * 2), a = 1;
119
122
  }
120
123
  const c = [];
121
- for (let s = i; s < a.length && !(c.length >= 8); s++) {
122
- const l = a[s].trim(), u = l.lastIndexOf(" ");
123
- let p = l, b = null;
124
+ for (let s = a; s < n.length && !(c.length >= 8); s++) {
125
+ const l = n[s].trim(), u = l.lastIndexOf(" ");
126
+ let p = l, m = null;
124
127
  if (u !== -1 && !l.endsWith(")")) {
125
128
  const f = l.substring(u + 1);
126
- (f.endsWith("%") || f.endsWith("px") || !isNaN(parseFloat(f))) && (p = l.substring(0, u).trim(), b = f);
129
+ (f.endsWith("%") || f.endsWith("px") || !isNaN(parseFloat(f))) && (p = l.substring(0, u).trim(), m = f);
127
130
  }
128
- const v = y(p);
131
+ const v = w(p);
129
132
  c.push({
130
133
  color: v.color,
131
134
  alpha: v.alpha,
132
- rawStop: b,
135
+ rawStop: m,
133
136
  stop: 0
134
137
  });
135
138
  }
@@ -151,13 +154,13 @@ function G(r) {
151
154
  }
152
155
  return { angle: o, stops: c };
153
156
  }
154
- const E = `varying vec2 vUv;
157
+ const F = `varying vec2 vUv;
155
158
  varying vec4 vScreenPos;
156
159
  void main() {
157
160
  vUv = uv;
158
161
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
159
162
  vScreenPos = gl_Position;
160
- }`, L = `varying vec2 vUv;
163
+ }`, U = `varying vec2 vUv;
161
164
  uniform vec2 uSize;
162
165
  uniform vec4 uBorderRadius;
163
166
  uniform float uBorderWidth;
@@ -240,6 +243,18 @@ void main() {
240
243
  vec2 p = (vUv - 0.5) * uSize;
241
244
  vec2 halfSize = uSize * 0.5;
242
245
 
246
+ // CSS Proportional Border-Radius Clamping
247
+ float tl = uBorderRadius.x;
248
+ float tr = uBorderRadius.y;
249
+ float br = uBorderRadius.z;
250
+ float bl = uBorderRadius.w;
251
+ float fTop = uSize.x / max(tl + tr, 0.0001);
252
+ float fBottom = uSize.x / max(bl + br, 0.0001);
253
+ float fLeft = uSize.y / max(tl + bl, 0.0001);
254
+ float fRight = uSize.y / max(tr + br, 0.0001);
255
+ float f = min(1.0, min(min(fTop, fBottom), min(fLeft, fRight)));
256
+ vec4 clampedRadius = uBorderRadius * f;
257
+
243
258
  #INJECT_UV_MODIFIER
244
259
 
245
260
  // color decision pipeline
@@ -253,9 +268,8 @@ void main() {
253
268
  #INJECT_BASE_COLOR
254
269
 
255
270
  // Hybrid SDF
256
- vec2 xRadii = mix(uBorderRadius.xw, uBorderRadius.yz, step(0.0, p.x));
271
+ vec2 xRadii = mix(clampedRadius.xw, clampedRadius.yz, step(0.0, p.x));
257
272
  float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
258
- r = min(r, min(halfSize.x, halfSize.y));
259
273
 
260
274
  float d = sdRoundedBox(p, halfSize, r);
261
275
 
@@ -286,51 +300,53 @@ void main() {
286
300
 
287
301
  // #include <colorspace_fragment>
288
302
  }
289
- `, F = `uniform sampler2D uTexture;
303
+ `, W = `uniform sampler2D uTexture;
290
304
  varying vec4 vScreenPos;
291
305
  uniform vec2 uTextureRepeat;
292
306
  uniform vec2 uTextureOffset;`, _ = `vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
293
307
  vec2 resultUv = screenUv;
294
308
 
295
- `, W = `vec4 texColor = texture2D(uTexture, resultUv);
296
- baseColor = blendSrcOver(baseColor, texColor);`, O = {
297
- vertexShader: E,
298
- fragmentShader: L
299
- }, B = {
300
- declChunk: F,
309
+ `, z = `vec4 texColor = texture2D(uTexture, resultUv);
310
+ baseColor = blendSrcOver(baseColor, texColor);`, R = {
311
+ vertexShader: F,
312
+ fragmentShader: U
313
+ }, I = {
314
+ declChunk: W,
301
315
  uvChunk: _,
302
- baseColorChunk: W
316
+ baseColorChunk: z
303
317
  };
304
- function U(r, e, n, a = null, o) {
305
- const i = a !== null || !!r.imageSrc;
306
- let t = "";
318
+ function V(r, e, t, n = null, o) {
319
+ const a = n !== null || !!r.imageSrc;
320
+ let i = "";
307
321
  const c = {};
308
322
  if (o != null && o.uniforms)
309
323
  for (const [h, g] of Object.entries(o.uniforms))
310
- typeof g == "number" ? (t += `uniform float ${h};
311
- `, c[h] = { value: g }) : Array.isArray(g) ? g.length === 2 ? (t += `uniform vec2 ${h};
312
- `, c[h] = { value: new d.Vector2(...g) }) : g.length === 3 ? (t += `uniform vec3 ${h};
313
- `, c[h] = { value: new d.Vector3(...g) }) : g.length === 4 && (t += `uniform vec4 ${h};
314
- `, c[h] = { value: new d.Vector4(...g) }) : (t += `uniform float ${h};
324
+ typeof g == "number" ? (i += `uniform float ${h};
325
+ `, c[h] = { value: g }) : Array.isArray(g) ? g.length === 2 ? (i += `uniform vec2 ${h};
326
+ `, c[h] = { value: new d.Vector2(...g) }) : g.length === 3 ? (i += `uniform vec3 ${h};
327
+ `, c[h] = { value: new d.Vector3(...g) }) : g.length === 4 && (i += `uniform vec4 ${h};
328
+ `, c[h] = { value: new d.Vector4(...g) }) : (i += `uniform float ${h};
315
329
  `, c[h] = { value: g });
316
- const s = (i ? B.declChunk : "") + `
317
- ` + t, l = r.isTraveler ? B.uvChunk : `vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;
318
- `, u = i ? l + ((o == null ? void 0 : o.uvModifier) || "") : "", p = i ? B.baseColorChunk : "", b = (o == null ? void 0 : o.colorModifier) || "", v = O.fragmentShader.replace("#INJECT_DECLARATIONS", s).replace("#INJECT_UV_MODIFIER", u).replace("#INJECT_BASE_COLOR", p).replace("#INJECT_COLOR_MODIFIER", b), f = y(r.backgroundColor), m = y(r.borderColor), S = {
319
- uSize: { value: new d.Vector2(e, n) },
330
+ const s = o !== void 0, l = (a || s ? I.declChunk : "") + `
331
+ ` + i, u = `vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
332
+ `, p = r.isTraveler ? `vec2 resultUv = screenUv;
333
+ ` : `vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;
334
+ `, m = a || s ? u + p + ((o == null ? void 0 : o.uvModifier) || "") : "", v = a || s ? I.baseColorChunk : "", f = (o == null ? void 0 : o.colorModifier) || "", y = R.fragmentShader.replace("#INJECT_DECLARATIONS", l).replace("#INJECT_UV_MODIFIER", m).replace("#INJECT_BASE_COLOR", v).replace("#INJECT_COLOR_MODIFIER", f), C = w(r.backgroundColor), b = w(r.borderColor), B = {
335
+ uSize: { value: new d.Vector2(e, t) },
320
336
  uBgColor: {
321
337
  value: new d.Vector4(
322
- f.color.r,
323
- f.color.g,
324
- f.color.b,
325
- f.alpha
338
+ C.color.r,
339
+ C.color.g,
340
+ C.color.b,
341
+ C.alpha
326
342
  )
327
343
  },
328
344
  uBorderColor: {
329
345
  value: new d.Vector4(
330
- m.color.r,
331
- m.color.g,
332
- m.color.b,
333
- m.alpha
346
+ b.color.r,
347
+ b.color.g,
348
+ b.color.b,
349
+ b.alpha
334
350
  )
335
351
  },
336
352
  uBorderRadius: { value: new d.Vector4(0, 0, 0, 0) },
@@ -346,24 +362,24 @@ function U(r, e, n, a = null, o) {
346
362
  },
347
363
  uGradientStops: { value: new Float32Array(8) }
348
364
  };
349
- A(S.uBorderRadius.value, r.borderRadius), i && (S.uTexture.value = a);
350
- const C = new d.ShaderMaterial({
351
- uniforms: { ...S, ...c },
352
- vertexShader: O.vertexShader,
353
- fragmentShader: v,
365
+ M(B.uBorderRadius.value, r.borderRadius, Math.min(e, t)), a && (B.uTexture.value = n);
366
+ const O = new d.ShaderMaterial({
367
+ uniforms: { ...B, ...c },
368
+ vertexShader: R.vertexShader,
369
+ fragmentShader: y,
354
370
  transparent: !0,
355
371
  side: d.FrontSide
356
372
  // for better performance
357
373
  });
358
- return r.backgroundImage && T(C, { backgroundImage: r.backgroundImage }), C;
374
+ return r.backgroundImage && T(O, { backgroundImage: r.backgroundImage }), O;
359
375
  }
360
- function V(r, e, n, a, o) {
361
- const i = x(e.borderWidth);
376
+ function D(r, e, t, n, o) {
377
+ const a = x(e.borderWidth);
362
378
  T(r, {
363
- width: n,
364
- height: a,
379
+ width: t,
380
+ height: n,
365
381
  borderRadius: e.borderRadius,
366
- borderWidth: i,
382
+ borderWidth: a,
367
383
  backgroundColor: e.backgroundColor,
368
384
  borderColor: e.borderColor,
369
385
  opacity: e.opacity,
@@ -372,8 +388,12 @@ function V(r, e, n, a, o) {
372
388
  });
373
389
  }
374
390
  function T(r, e) {
375
- var a;
376
- if (e.width !== void 0 && e.height !== void 0 && r.uniforms.uSize.value.set(e.width, e.height), e.borderRadius !== void 0 && A(r.uniforms.uBorderRadius.value, e.borderRadius), e.borderWidth !== void 0 && (r.uniforms.uBorderWidth.value = e.borderWidth), e.backgroundColor !== void 0)
391
+ var n;
392
+ if (e.width !== void 0 && e.height !== void 0 && r.uniforms.uSize.value.set(e.width, e.height), e.borderRadius !== void 0) {
393
+ const o = e.width !== void 0 && e.height !== void 0 ? Math.min(e.width, e.height) : Math.min(r.uniforms.uSize.value.x, r.uniforms.uSize.value.y);
394
+ M(r.uniforms.uBorderRadius.value, e.borderRadius, o);
395
+ }
396
+ if (e.borderWidth !== void 0 && (r.uniforms.uBorderWidth.value = e.borderWidth), e.backgroundColor !== void 0)
377
397
  if (Array.isArray(e.backgroundColor)) {
378
398
  const o = r.uniforms.uBgColor.value.w;
379
399
  r.uniforms.uBgColor.value.set(
@@ -383,7 +403,7 @@ function T(r, e) {
383
403
  o
384
404
  );
385
405
  } else if (typeof e.backgroundColor == "string") {
386
- const o = y(e.backgroundColor);
406
+ const o = w(e.backgroundColor);
387
407
  r.uniforms.uBgColor.value.set(
388
408
  o.color.r,
389
409
  o.color.g,
@@ -409,7 +429,7 @@ function T(r, e) {
409
429
  o
410
430
  );
411
431
  } else if (typeof e.borderColor == "string") {
412
- const o = y(e.borderColor);
432
+ const o = w(e.borderColor);
413
433
  r.uniforms.uBorderColor.value.set(
414
434
  o.color.r,
415
435
  o.color.g,
@@ -426,11 +446,11 @@ function T(r, e) {
426
446
  );
427
447
  }
428
448
  e.opacity !== void 0 && (r.uniforms.uOpacity.value = e.opacity), e.bgOpacity !== void 0 && (r.uniforms.uBgColor.value.w = e.bgOpacity), e.borderOpacity !== void 0 && (r.uniforms.uBorderColor.value.w = e.borderOpacity), r.uniforms.uTexture && e.texture !== void 0 && (r.uniforms.uTexture.value = e.texture);
429
- const n = e.texture !== void 0 ? e.texture : (a = r.uniforms.uTexture) == null ? void 0 : a.value;
430
- if (n && (n.image instanceof ImageBitmap || n.image instanceof HTMLImageElement || n.image instanceof HTMLCanvasElement)) {
431
- const o = n.image.width, i = n.image.height, t = e.width ?? r.uniforms.uSize.value.x, c = e.height ?? r.uniforms.uSize.value.y;
432
- if (o && i && t && c) {
433
- const s = o / i, l = t / c;
449
+ const t = e.texture !== void 0 ? e.texture : (n = r.uniforms.uTexture) == null ? void 0 : n.value;
450
+ if (t && (t.image instanceof ImageBitmap || t.image instanceof HTMLImageElement || t.image instanceof HTMLCanvasElement)) {
451
+ const o = t.image.naturalWidth || t.image.videoWidth || t.image.width, a = t.image.naturalHeight || t.image.videoHeight || t.image.height, i = e.width ?? r.uniforms.uSize.value.x, c = e.height ?? r.uniforms.uSize.value.y;
452
+ if (o && a && i && c) {
453
+ const s = o / a, l = i / c;
434
454
  if (s > l) {
435
455
  const u = l / s;
436
456
  r.uniforms.uTextureRepeat.value.set(u, 1), r.uniforms.uTextureOffset.value.set((1 - u) / 2, 0);
@@ -442,27 +462,27 @@ function T(r, e) {
442
462
  } else
443
463
  r.uniforms.uTextureRepeat && (r.uniforms.uTextureRepeat.value.set(1, 1), r.uniforms.uTextureOffset.value.set(0, 0));
444
464
  if (e.backgroundImage !== void 0) {
445
- const o = G(e.backgroundImage);
465
+ const o = L(e.backgroundImage);
446
466
  if (o) {
447
467
  r.uniforms.uGradientCount.value = o.stops.length, r.uniforms.uGradientAngle.value = o.angle;
448
- for (let i = 0; i < 8; i++)
449
- if (i < o.stops.length) {
450
- const t = o.stops[i];
451
- r.uniforms.uGradientColors.value[i].set(
452
- t.color.r,
453
- t.color.g,
454
- t.color.b,
455
- t.alpha
456
- ), r.uniforms.uGradientStops.value[i] = t.stop;
468
+ for (let a = 0; a < 8; a++)
469
+ if (a < o.stops.length) {
470
+ const i = o.stops[a];
471
+ r.uniforms.uGradientColors.value[a].set(
472
+ i.color.r,
473
+ i.color.g,
474
+ i.color.b,
475
+ i.alpha
476
+ ), r.uniforms.uGradientStops.value[a] = i.stop;
457
477
  } else
458
- r.uniforms.uGradientColors.value[i].set(0, 0, 0, 0), r.uniforms.uGradientStops.value[i] = 1;
478
+ r.uniforms.uGradientColors.value[a].set(0, 0, 0, 0), r.uniforms.uGradientStops.value[a] = 1;
459
479
  } else
460
480
  r.uniforms.uGradientCount.value = 0;
461
481
  }
462
482
  for (const o of Object.keys(e))
463
483
  o !== "width" && o !== "height" && o !== "borderRadius" && o !== "borderWidth" && o !== "backgroundColor" && o !== "borderColor" && o !== "opacity" && o !== "bgOpacity" && o !== "borderOpacity" && o !== "texture" && o !== "backgroundImage" && r.uniforms[o] !== void 0 && (r.uniforms[o].value !== void 0 && r.uniforms[o].value !== null && typeof r.uniforms[o].value.set == "function" ? Array.isArray(e[o]) ? r.uniforms[o].value.set(...e[o]) : e[o] !== void 0 && (e[o].copy ? r.uniforms[o].value.copy(e[o]) : r.uniforms[o].value = e[o]) : r.uniforms[o].value = e[o]);
464
484
  }
465
- function A(r, e) {
485
+ function M(r, e, t = 0) {
466
486
  if (e == null) {
467
487
  r.set(0, 0, 0, 0);
468
488
  return;
@@ -475,38 +495,38 @@ function A(r, e) {
475
495
  r.set(e[0], e[1], e[2], e[3]);
476
496
  return;
477
497
  }
478
- const n = e.split("/")[0].trim().split(/\s+/), a = x(n[0]), o = x(n[1] ?? n[0]), i = x(n[2] ?? n[0]), t = x(n[3] ?? n[1] ?? n[0]);
479
- r.set(a, o, i, t);
498
+ const n = e.split("/")[0].trim().split(/\s+/), o = x(n[0], t), a = x(n[1] ?? n[0], t), i = x(n[2] ?? n[0], t), c = x(n[3] ?? n[1] ?? n[0], t);
499
+ r.set(o, a, i, c);
480
500
  }
481
501
  const N = {
482
- create(r, e, n, a, o, i = 2, t = null, c) {
483
- return r === "BOX" ? U(
502
+ create(r, e, t, n, o, a = 2, i = null, c) {
503
+ return r === "BOX" ? V(
484
504
  e,
485
- a,
505
+ n,
486
506
  o,
487
- t,
507
+ i,
488
508
  c
489
- ) : r === "TEXT" ? new k(
490
- n || "",
509
+ ) : r === "TEXT" ? new G(
510
+ t || "",
491
511
  e,
492
- a,
512
+ n,
493
513
  o,
494
- i
514
+ a
495
515
  ) : new d.MeshBasicMaterial({ visible: !1 });
496
516
  },
497
- update(r, e, n, a, o, i, t = 2, c) {
498
- e === "BOX" ? V(
517
+ update(r, e, t, n, o, a, i = 2, c) {
518
+ e === "BOX" ? D(
499
519
  r,
500
- n,
520
+ t,
501
521
  o,
502
- i,
522
+ a,
503
523
  c
504
524
  ) : e === "TEXT" && r.updateText(
505
- a || "",
506
- n,
525
+ n || "",
526
+ t,
507
527
  o,
508
- i,
509
- t
528
+ a,
529
+ i
510
530
  );
511
531
  },
512
532
  forceUpdateUniforms(r, e) {
@@ -515,12 +535,12 @@ const N = {
515
535
  };
516
536
  export {
517
537
  N as Painter,
518
- k as TextGenerator,
519
- U as createBoxMaterial,
520
- y as parseColor,
521
- G as parseLinearGradient,
538
+ G as TextGenerator,
539
+ V as createBoxMaterial,
540
+ w as parseColor,
541
+ L as parseLinearGradient,
522
542
  x as parsePixelValue,
523
543
  T as setBoxUniforms,
524
- P as splitByComma,
525
- V as updateBoxMaterial
544
+ E as splitByComma,
545
+ D as updateBoxMaterial
526
546
  };
@@ -1,11 +1,11 @@
1
- (function(f,h){typeof exports=="object"&&typeof module<"u"?h(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],h):(f=typeof globalThis<"u"?globalThis:f||self,h(f.MiragePainter={},f.THREE))})(this,function(f,h){"use strict";var N=Object.defineProperty;var q=(f,h,C)=>h in f?N(f,h,{enumerable:!0,configurable:!0,writable:!0,value:C}):f[h]=C;var O=(f,h,C)=>(q(f,typeof h!="symbol"?h+"":h,C),C);function C(r){const o=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(r){for(const t in r)if(t!=="default"){const i=Object.getOwnPropertyDescriptor(r,t);Object.defineProperty(o,t,i.get?i:{enumerable:!0,get:()=>r[t]})}}return o.default=r,Object.freeze(o)}const u=C(h);class M{static getBaseline(o){if(this.cache.has(o))return this.cache.get(o);const t=this.calculateBaselineFromDOM(o);return this.cache.set(o,t),t}static calculateBaselineFromDOM(o){if(typeof document>"u")return 0;const t=document.createElement("div"),i=document.createElement("span"),e=document.createElement("img");t.style.visibility="hidden",t.style.position="absolute",t.style.top="0px",t.style.left="0px",t.style.font=o,t.style.margin="0",t.style.padding="0",t.style.border="none",i.style.margin="0",i.style.padding="0",i.style.border="none",i.style.lineHeight="normal",e.width=1,e.height=1,e.style.verticalAlign="baseline",i.appendChild(document.createTextNode("Hidden Text")),t.appendChild(i),t.appendChild(e),document.body.appendChild(t);const a=e.offsetTop-i.offsetTop;return document.body.removeChild(t),a}}O(M,"cache",new Map);class R extends u.MeshBasicMaterial{constructor(t,i,e,a,n=2){super({transparent:!0,side:u.FrontSide,color:16777215});O(this,"canvas");O(this,"ctx");O(this,"qualityFactor");if(this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),!this.ctx)throw new Error("[Mirage] Failed to create canvas context");this.qualityFactor=n,this.map=new u.CanvasTexture(this.canvas),this.map.colorSpace=u.LinearSRGBColorSpace,this.map.minFilter=u.LinearFilter,this.map.magFilter=u.LinearFilter,this.updateText(t,i,e,a)}wrapText(t,i){const e=t.split(`
2
- `),a=[];return e.forEach(n=>{const c=n.match(/[^\s\-]+\-?|\-|\s+/g)||[];if(c.length===0){a.push("");return}let s=c[0];for(let l=1;l<c.length;l++){const d=c[l];this.ctx.measureText(s+d).width<=i+2?s+=d:(s&&a.push(s),s=d.trimStart())}s&&a.push(s)}),a}updateText(t,i,e,a,n){n!==void 0&&(this.qualityFactor=n);const s=(window.devicePixelRatio||1)*this.qualityFactor,l=e*s,d=a*s;this.canvas.width!==l||this.canvas.height!==d?(this.canvas.width=l,this.canvas.height=d):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.setTransform(s,0,0,s,0,0),this.ctx.font=i.font,this.ctx.fillStyle=i.color,this.ctx.textBaseline="alphabetic",this.ctx.globalAlpha=1;const g=this.wrapText(t,e),y=i.lineHeight,m=M.getBaseline(i.font);g.forEach((p,T)=>{const B=T*y+m;let w=0;i.textAlign==="center"?w=e/2:i.textAlign==="right"&&(w=e),this.ctx.textAlign=i.textAlign,this.ctx.fillText(p,w,B)}),this.map&&(this.map.needsUpdate=!0)}dispose(){this.map&&this.map.dispose(),super.dispose()}}function x(r){return typeof r=="number"?r:parseFloat(r)||0}function S(r){if(!r||r==="transparent")return{color:new u.Color(16777215),alpha:0};const o=r.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(o){const t=parseInt(o[1],10),i=parseInt(o[2],10),e=parseInt(o[3],10),a=o[4]!==void 0?parseFloat(o[4]):1;return{color:new u.Color(`rgb(${t}, ${i}, ${e})`),alpha:a}}return{color:new u.Color(r),alpha:1}}function k(r){const o=[];let t="",i=0;for(let e=0;e<r.length;e++){const a=r[e];if(a==="(")i++;else if(a===")")i--;else if(a===","&&i===0){o.push(t.trim()),t="";continue}t+=a}return t&&o.push(t.trim()),o}function P(r){if(!r||typeof r!="string"||!r.includes("linear-gradient"))return null;const o=r.match(/linear-gradient\((.*)\)/);if(!o)return null;const t=o[1],i=k(t);let e=Math.PI,a=0;const n=i[0].trim();if(n.startsWith("to "))n==="to top"?e=0:n==="to right"?e=Math.PI/2:n==="to bottom"?e=Math.PI:n==="to left"?e=Math.PI*1.5:n==="to top right"||n==="to right top"?e=Math.PI/4:n==="to bottom right"||n==="to right bottom"?e=Math.PI*.75:n==="to bottom left"||n==="to left bottom"?e=Math.PI*1.25:(n==="to top left"||n==="to left top")&&(e=Math.PI*1.75),a=1;else if(n.endsWith("deg")||n.endsWith("rad")||n.endsWith("turn")){const s=parseFloat(n);n.endsWith("deg")?e=s*(Math.PI/180):n.endsWith("rad")?e=s:n.endsWith("turn")&&(e=s*Math.PI*2),a=1}const c=[];for(let s=a;s<i.length&&!(c.length>=8);s++){const l=i[s].trim(),d=l.lastIndexOf(" ");let g=l,y=null;if(d!==-1&&!l.endsWith(")")){const p=l.substring(d+1);(p.endsWith("%")||p.endsWith("px")||!isNaN(parseFloat(p)))&&(g=l.substring(0,d).trim(),y=p)}const m=S(g);c.push({color:m.color,alpha:m.alpha,rawStop:y,stop:0})}if(c.length>0){for(let l=0;l<c.length;l++)c[l].rawStop!==null&&(c[l].stop=parseFloat(c[l].rawStop)/100);c[0].rawStop===null&&(c[0].stop=0),c.length>1&&c[c.length-1].rawStop===null&&(c[c.length-1].stop=1);let s=0;for(let l=1;l<c.length;l++)if(c[l].rawStop!==null||l===c.length-1){const d=l-s;if(d>1){const g=c[s].stop,m=(c[l].stop-g)/d;for(let p=1;p<d;p++)c[s+p].stop=g+m*p}s=l}}return{angle:e,stops:c}}const F=`varying vec2 vUv;
1
+ (function(f,p){typeof exports=="object"&&typeof module<"u"?p(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],p):(f=typeof globalThis<"u"?globalThis:f||self,p(f.MiragePainter={},f.THREE))})(this,function(f,p){"use strict";var q=Object.defineProperty;var j=(f,p,x)=>p in f?q(f,p,{enumerable:!0,configurable:!0,writable:!0,value:x}):f[p]=x;var T=(f,p,x)=>(j(f,typeof p!="symbol"?p+"":p,x),x);function x(r){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(r){for(const t in r)if(t!=="default"){const n=Object.getOwnPropertyDescriptor(r,t);Object.defineProperty(e,t,n.get?n:{enumerable:!0,get:()=>r[t]})}}return e.default=r,Object.freeze(e)}const u=x(p);class M{static getBaseline(e){if(this.cache.has(e))return this.cache.get(e);const t=this.calculateBaselineFromDOM(e);return this.cache.set(e,t),t}static calculateBaselineFromDOM(e){if(typeof document>"u")return 0;const t=document.createElement("div"),n=document.createElement("span"),o=document.createElement("img");t.style.visibility="hidden",t.style.position="absolute",t.style.top="0px",t.style.left="0px",t.style.font=e,t.style.margin="0",t.style.padding="0",t.style.border="none",n.style.margin="0",n.style.padding="0",n.style.border="none",n.style.lineHeight="normal",o.width=1,o.height=1,o.style.verticalAlign="baseline",n.appendChild(document.createTextNode("Hidden Text")),t.appendChild(n),t.appendChild(o),document.body.appendChild(t);const a=o.offsetTop-n.offsetTop;return document.body.removeChild(t),a}}T(M,"cache",new Map);class A extends u.MeshBasicMaterial{constructor(t,n,o,a,i=2){super({transparent:!0,side:u.FrontSide,color:16777215});T(this,"canvas");T(this,"ctx");T(this,"qualityFactor");if(this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),!this.ctx)throw new Error("[Mirage] Failed to create canvas context");this.qualityFactor=i,this.map=new u.CanvasTexture(this.canvas),this.map.colorSpace=u.LinearSRGBColorSpace,this.map.minFilter=u.LinearFilter,this.map.magFilter=u.LinearFilter,this.updateText(t,n,o,a)}wrapText(t,n){const o=t.split(`
2
+ `),a=[];return o.forEach(i=>{const c=i.match(/[^\s\-]+\-?|\-|\s+/g)||[];if(c.length===0){a.push("");return}let s=c[0];for(let l=1;l<c.length;l++){const d=c[l];this.ctx.measureText(s+d).width<=n+2?s+=d:(s&&a.push(s),s=d.trimStart())}s&&a.push(s)}),a}updateText(t,n,o,a,i){i!==void 0&&(this.qualityFactor=i);const s=(window.devicePixelRatio||1)*this.qualityFactor,l=o*s,d=a*s;this.canvas.width!==l||this.canvas.height!==d?(this.canvas.width=l,this.canvas.height=d):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.setTransform(s,0,0,s,0,0),this.ctx.font=n.font,this.ctx.fillStyle=n.color,this.ctx.textBaseline="alphabetic",this.ctx.globalAlpha=1;const g=this.wrapText(t,o),y=n.lineHeight,m=M.getBaseline(n.font);g.forEach((h,R)=>{const w=R*y+m;let C=0;n.textAlign==="center"?C=o/2:n.textAlign==="right"&&(C=o),this.ctx.textAlign=n.textAlign,this.ctx.fillText(h,C,w)}),this.map&&(this.map.needsUpdate=!0)}dispose(){this.map&&this.map.dispose(),super.dispose()}}function S(r,e=0){if(typeof r=="number")return r;const t=parseFloat(r)||0;return typeof r=="string"&&r.includes("%")?t/100*e:t}function B(r){if(!r||r==="transparent")return{color:new u.Color(16777215),alpha:0};const e=r.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(e){const t=parseInt(e[1],10),n=parseInt(e[2],10),o=parseInt(e[3],10),a=e[4]!==void 0?parseFloat(e[4]):1;return{color:new u.Color(`rgb(${t}, ${n}, ${o})`),alpha:a}}return{color:new u.Color(r),alpha:1}}function P(r){const e=[];let t="",n=0;for(let o=0;o<r.length;o++){const a=r[o];if(a==="(")n++;else if(a===")")n--;else if(a===","&&n===0){e.push(t.trim()),t="";continue}t+=a}return t&&e.push(t.trim()),e}function k(r){if(!r||typeof r!="string"||!r.includes("linear-gradient"))return null;const e=r.match(/linear-gradient\((.*)\)/);if(!e)return null;const t=e[1],n=P(t);let o=Math.PI,a=0;const i=n[0].trim();if(i.startsWith("to "))i==="to top"?o=0:i==="to right"?o=Math.PI/2:i==="to bottom"?o=Math.PI:i==="to left"?o=Math.PI*1.5:i==="to top right"||i==="to right top"?o=Math.PI/4:i==="to bottom right"||i==="to right bottom"?o=Math.PI*.75:i==="to bottom left"||i==="to left bottom"?o=Math.PI*1.25:(i==="to top left"||i==="to left top")&&(o=Math.PI*1.75),a=1;else if(i.endsWith("deg")||i.endsWith("rad")||i.endsWith("turn")){const s=parseFloat(i);i.endsWith("deg")?o=s*(Math.PI/180):i.endsWith("rad")?o=s:i.endsWith("turn")&&(o=s*Math.PI*2),a=1}const c=[];for(let s=a;s<n.length&&!(c.length>=8);s++){const l=n[s].trim(),d=l.lastIndexOf(" ");let g=l,y=null;if(d!==-1&&!l.endsWith(")")){const h=l.substring(d+1);(h.endsWith("%")||h.endsWith("px")||!isNaN(parseFloat(h)))&&(g=l.substring(0,d).trim(),y=h)}const m=B(g);c.push({color:m.color,alpha:m.alpha,rawStop:y,stop:0})}if(c.length>0){for(let l=0;l<c.length;l++)c[l].rawStop!==null&&(c[l].stop=parseFloat(c[l].rawStop)/100);c[0].rawStop===null&&(c[0].stop=0),c.length>1&&c[c.length-1].rawStop===null&&(c[c.length-1].stop=1);let s=0;for(let l=1;l<c.length;l++)if(c[l].rawStop!==null||l===c.length-1){const d=l-s;if(d>1){const g=c[s].stop,m=(c[l].stop-g)/d;for(let h=1;h<d;h++)c[s+h].stop=g+m*h}s=l}}return{angle:o,stops:c}}const W=`varying vec2 vUv;
3
3
  varying vec4 vScreenPos;
4
4
  void main() {
5
5
  vUv = uv;
6
6
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
7
7
  vScreenPos = gl_Position;
8
- }`,W=`varying vec2 vUv;
8
+ }`,z=`varying vec2 vUv;
9
9
  uniform vec2 uSize;
10
10
  uniform vec4 uBorderRadius;
11
11
  uniform float uBorderWidth;
@@ -88,6 +88,18 @@ void main() {
88
88
  vec2 p = (vUv - 0.5) * uSize;
89
89
  vec2 halfSize = uSize * 0.5;
90
90
 
91
+ // CSS Proportional Border-Radius Clamping
92
+ float tl = uBorderRadius.x;
93
+ float tr = uBorderRadius.y;
94
+ float br = uBorderRadius.z;
95
+ float bl = uBorderRadius.w;
96
+ float fTop = uSize.x / max(tl + tr, 0.0001);
97
+ float fBottom = uSize.x / max(bl + br, 0.0001);
98
+ float fLeft = uSize.y / max(tl + bl, 0.0001);
99
+ float fRight = uSize.y / max(tr + br, 0.0001);
100
+ float f = min(1.0, min(min(fTop, fBottom), min(fLeft, fRight)));
101
+ vec4 clampedRadius = uBorderRadius * f;
102
+
91
103
  #INJECT_UV_MODIFIER
92
104
 
93
105
  // color decision pipeline
@@ -101,9 +113,8 @@ void main() {
101
113
  #INJECT_BASE_COLOR
102
114
 
103
115
  // Hybrid SDF
104
- vec2 xRadii = mix(uBorderRadius.xw, uBorderRadius.yz, step(0.0, p.x));
116
+ vec2 xRadii = mix(clampedRadius.xw, clampedRadius.yz, step(0.0, p.x));
105
117
  float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
106
- r = min(r, min(halfSize.x, halfSize.y));
107
118
 
108
119
  float d = sdRoundedBox(p, halfSize, r);
109
120
 
@@ -134,18 +145,20 @@ void main() {
134
145
 
135
146
  // #include <colorspace_fragment>
136
147
  }
137
- `,U=`uniform sampler2D uTexture;
148
+ `,V=`uniform sampler2D uTexture;
138
149
  varying vec4 vScreenPos;
139
150
  uniform vec2 uTextureRepeat;
140
- uniform vec2 uTextureOffset;`,V=`vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
151
+ uniform vec2 uTextureOffset;`,D=`vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
141
152
  vec2 resultUv = screenUv;
142
153
 
143
- `,z=`vec4 texColor = texture2D(uTexture, resultUv);
144
- baseColor = blendSrcOver(baseColor, texColor);`,G={vertexShader:F,fragmentShader:W},A={declChunk:U,uvChunk:V,baseColorChunk:z};function E(r,o,t,i=null,e){const a=i!==null||!!r.imageSrc;let n="";const c={};if(e!=null&&e.uniforms)for(const[b,v]of Object.entries(e.uniforms))typeof v=="number"?(n+=`uniform float ${b};
145
- `,c[b]={value:v}):Array.isArray(v)?v.length===2?(n+=`uniform vec2 ${b};
146
- `,c[b]={value:new u.Vector2(...v)}):v.length===3?(n+=`uniform vec3 ${b};
147
- `,c[b]={value:new u.Vector3(...v)}):v.length===4&&(n+=`uniform vec4 ${b};
148
- `,c[b]={value:new u.Vector4(...v)}):(n+=`uniform float ${b};
149
- `,c[b]={value:v});const s=(a?A.declChunk:"")+`
150
- `+n,l=r.isTraveler?A.uvChunk:`vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;
151
- `,d=a?l+((e==null?void 0:e.uvModifier)||""):"",g=a?A.baseColorChunk:"",y=(e==null?void 0:e.colorModifier)||"",m=G.fragmentShader.replace("#INJECT_DECLARATIONS",s).replace("#INJECT_UV_MODIFIER",d).replace("#INJECT_BASE_COLOR",g).replace("#INJECT_COLOR_MODIFIER",y),p=S(r.backgroundColor),T=S(r.borderColor),B={uSize:{value:new u.Vector2(o,t)},uBgColor:{value:new u.Vector4(p.color.r,p.color.g,p.color.b,p.alpha)},uBorderColor:{value:new u.Vector4(T.color.r,T.color.g,T.color.b,T.alpha)},uBorderRadius:{value:new u.Vector4(0,0,0,0)},uBorderWidth:{value:x(r.borderWidth)},uOpacity:{value:r.opacity??1},uTexture:{value:null},uTextureRepeat:{value:new u.Vector2(1,1)},uTextureOffset:{value:new u.Vector2(0,0)},uGradientCount:{value:0},uGradientAngle:{value:0},uGradientColors:{value:Array.from({length:8},()=>new u.Vector4(0,0,0,0))},uGradientStops:{value:new Float32Array(8)}};_(B.uBorderRadius.value,r.borderRadius),a&&(B.uTexture.value=i);const w=new u.ShaderMaterial({uniforms:{...B,...c},vertexShader:G.vertexShader,fragmentShader:m,transparent:!0,side:u.FrontSide});return r.backgroundImage&&I(w,{backgroundImage:r.backgroundImage}),w}function L(r,o,t,i,e){const a=x(o.borderWidth);I(r,{width:t,height:i,borderRadius:o.borderRadius,borderWidth:a,backgroundColor:o.backgroundColor,borderColor:o.borderColor,opacity:o.opacity,texture:e,backgroundImage:o.backgroundImage})}function I(r,o){var i;if(o.width!==void 0&&o.height!==void 0&&r.uniforms.uSize.value.set(o.width,o.height),o.borderRadius!==void 0&&_(r.uniforms.uBorderRadius.value,o.borderRadius),o.borderWidth!==void 0&&(r.uniforms.uBorderWidth.value=o.borderWidth),o.backgroundColor!==void 0)if(Array.isArray(o.backgroundColor)){const e=r.uniforms.uBgColor.value.w;r.uniforms.uBgColor.value.set(o.backgroundColor[0],o.backgroundColor[1],o.backgroundColor[2],e)}else if(typeof o.backgroundColor=="string"){const e=S(o.backgroundColor);r.uniforms.uBgColor.value.set(e.color.r,e.color.g,e.color.b,e.alpha)}else{const e=r.uniforms.uBgColor.value.w;r.uniforms.uBgColor.value.set(o.backgroundColor.r,o.backgroundColor.g,o.backgroundColor.b,e)}if(o.borderColor!==void 0)if(Array.isArray(o.borderColor)){const e=r.uniforms.uBorderColor.value.w;r.uniforms.uBorderColor.value.set(o.borderColor[0],o.borderColor[1],o.borderColor[2],e)}else if(typeof o.borderColor=="string"){const e=S(o.borderColor);r.uniforms.uBorderColor.value.set(e.color.r,e.color.g,e.color.b,e.alpha)}else{const e=r.uniforms.uBorderColor.value.w;r.uniforms.uBorderColor.value.set(o.borderColor.r,o.borderColor.g,o.borderColor.b,e)}o.opacity!==void 0&&(r.uniforms.uOpacity.value=o.opacity),o.bgOpacity!==void 0&&(r.uniforms.uBgColor.value.w=o.bgOpacity),o.borderOpacity!==void 0&&(r.uniforms.uBorderColor.value.w=o.borderOpacity),r.uniforms.uTexture&&o.texture!==void 0&&(r.uniforms.uTexture.value=o.texture);const t=o.texture!==void 0?o.texture:(i=r.uniforms.uTexture)==null?void 0:i.value;if(t&&(t.image instanceof ImageBitmap||t.image instanceof HTMLImageElement||t.image instanceof HTMLCanvasElement)){const e=t.image.width,a=t.image.height,n=o.width??r.uniforms.uSize.value.x,c=o.height??r.uniforms.uSize.value.y;if(e&&a&&n&&c){const s=e/a,l=n/c;if(s>l){const d=l/s;r.uniforms.uTextureRepeat.value.set(d,1),r.uniforms.uTextureOffset.value.set((1-d)/2,0)}else{const d=s/l;r.uniforms.uTextureRepeat.value.set(1,d),r.uniforms.uTextureOffset.value.set(0,(1-d)/2)}}}else r.uniforms.uTextureRepeat&&(r.uniforms.uTextureRepeat.value.set(1,1),r.uniforms.uTextureOffset.value.set(0,0));if(o.backgroundImage!==void 0){const e=P(o.backgroundImage);if(e){r.uniforms.uGradientCount.value=e.stops.length,r.uniforms.uGradientAngle.value=e.angle;for(let a=0;a<8;a++)if(a<e.stops.length){const n=e.stops[a];r.uniforms.uGradientColors.value[a].set(n.color.r,n.color.g,n.color.b,n.alpha),r.uniforms.uGradientStops.value[a]=n.stop}else r.uniforms.uGradientColors.value[a].set(0,0,0,0),r.uniforms.uGradientStops.value[a]=1}else r.uniforms.uGradientCount.value=0}for(const e of Object.keys(o))e!=="width"&&e!=="height"&&e!=="borderRadius"&&e!=="borderWidth"&&e!=="backgroundColor"&&e!=="borderColor"&&e!=="opacity"&&e!=="bgOpacity"&&e!=="borderOpacity"&&e!=="texture"&&e!=="backgroundImage"&&r.uniforms[e]!==void 0&&(r.uniforms[e].value!==void 0&&r.uniforms[e].value!==null&&typeof r.uniforms[e].value.set=="function"?Array.isArray(o[e])?r.uniforms[e].value.set(...o[e]):o[e]!==void 0&&(o[e].copy?r.uniforms[e].value.copy(o[e]):r.uniforms[e].value=o[e]):r.uniforms[e].value=o[e])}function _(r,o){if(o==null){r.set(0,0,0,0);return}if(typeof o=="number"){r.set(o,o,o,o);return}if(Array.isArray(o)){r.set(o[0],o[1],o[2],o[3]);return}const t=o.split("/")[0].trim().split(/\s+/),i=x(t[0]),e=x(t[1]??t[0]),a=x(t[2]??t[0]),n=x(t[3]??t[1]??t[0]);r.set(i,e,a,n)}const D={create(r,o,t,i,e,a=2,n=null,c){return r==="BOX"?E(o,i,e,n,c):r==="TEXT"?new R(t||"",o,i,e,a):new u.MeshBasicMaterial({visible:!1})},update(r,o,t,i,e,a,n=2,c){o==="BOX"?L(r,t,e,a,c):o==="TEXT"&&r.updateText(i||"",t,e,a,n)},forceUpdateUniforms(r,o){I(r,o)}};f.Painter=D,f.TextGenerator=R,f.createBoxMaterial=E,f.parseColor=S,f.parseLinearGradient=P,f.parsePixelValue=x,f.setBoxUniforms=I,f.splitByComma=k,f.updateBoxMaterial=L,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})});
154
+ `,H=`vec4 texColor = texture2D(uTexture, resultUv);
155
+ baseColor = blendSrcOver(baseColor, texColor);`,G={vertexShader:W,fragmentShader:z},E={declChunk:V,uvChunk:D,baseColorChunk:H};function L(r,e,t,n=null,o){const a=n!==null||!!r.imageSrc;let i="";const c={};if(o!=null&&o.uniforms)for(const[v,b]of Object.entries(o.uniforms))typeof b=="number"?(i+=`uniform float ${v};
156
+ `,c[v]={value:b}):Array.isArray(b)?b.length===2?(i+=`uniform vec2 ${v};
157
+ `,c[v]={value:new u.Vector2(...b)}):b.length===3?(i+=`uniform vec3 ${v};
158
+ `,c[v]={value:new u.Vector3(...b)}):b.length===4&&(i+=`uniform vec4 ${v};
159
+ `,c[v]={value:new u.Vector4(...b)}):(i+=`uniform float ${v};
160
+ `,c[v]={value:b});const s=o!==void 0,l=(a||s?E.declChunk:"")+`
161
+ `+i,d=`vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
162
+ `,g=r.isTraveler?`vec2 resultUv = screenUv;
163
+ `:`vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;
164
+ `,y=a||s?d+g+((o==null?void 0:o.uvModifier)||""):"",m=a||s?E.baseColorChunk:"",h=(o==null?void 0:o.colorModifier)||"",R=G.fragmentShader.replace("#INJECT_DECLARATIONS",l).replace("#INJECT_UV_MODIFIER",y).replace("#INJECT_BASE_COLOR",m).replace("#INJECT_COLOR_MODIFIER",h),w=B(r.backgroundColor),C=B(r.borderColor),I={uSize:{value:new u.Vector2(e,t)},uBgColor:{value:new u.Vector4(w.color.r,w.color.g,w.color.b,w.alpha)},uBorderColor:{value:new u.Vector4(C.color.r,C.color.g,C.color.b,C.alpha)},uBorderRadius:{value:new u.Vector4(0,0,0,0)},uBorderWidth:{value:S(r.borderWidth)},uOpacity:{value:r.opacity??1},uTexture:{value:null},uTextureRepeat:{value:new u.Vector2(1,1)},uTextureOffset:{value:new u.Vector2(0,0)},uGradientCount:{value:0},uGradientAngle:{value:0},uGradientColors:{value:Array.from({length:8},()=>new u.Vector4(0,0,0,0))},uGradientStops:{value:new Float32Array(8)}};U(I.uBorderRadius.value,r.borderRadius,Math.min(e,t)),a&&(I.uTexture.value=n);const F=new u.ShaderMaterial({uniforms:{...I,...c},vertexShader:G.vertexShader,fragmentShader:R,transparent:!0,side:u.FrontSide});return r.backgroundImage&&O(F,{backgroundImage:r.backgroundImage}),F}function _(r,e,t,n,o){const a=S(e.borderWidth);O(r,{width:t,height:n,borderRadius:e.borderRadius,borderWidth:a,backgroundColor:e.backgroundColor,borderColor:e.borderColor,opacity:e.opacity,texture:o,backgroundImage:e.backgroundImage})}function O(r,e){var n;if(e.width!==void 0&&e.height!==void 0&&r.uniforms.uSize.value.set(e.width,e.height),e.borderRadius!==void 0){const o=e.width!==void 0&&e.height!==void 0?Math.min(e.width,e.height):Math.min(r.uniforms.uSize.value.x,r.uniforms.uSize.value.y);U(r.uniforms.uBorderRadius.value,e.borderRadius,o)}if(e.borderWidth!==void 0&&(r.uniforms.uBorderWidth.value=e.borderWidth),e.backgroundColor!==void 0)if(Array.isArray(e.backgroundColor)){const o=r.uniforms.uBgColor.value.w;r.uniforms.uBgColor.value.set(e.backgroundColor[0],e.backgroundColor[1],e.backgroundColor[2],o)}else if(typeof e.backgroundColor=="string"){const o=B(e.backgroundColor);r.uniforms.uBgColor.value.set(o.color.r,o.color.g,o.color.b,o.alpha)}else{const o=r.uniforms.uBgColor.value.w;r.uniforms.uBgColor.value.set(e.backgroundColor.r,e.backgroundColor.g,e.backgroundColor.b,o)}if(e.borderColor!==void 0)if(Array.isArray(e.borderColor)){const o=r.uniforms.uBorderColor.value.w;r.uniforms.uBorderColor.value.set(e.borderColor[0],e.borderColor[1],e.borderColor[2],o)}else if(typeof e.borderColor=="string"){const o=B(e.borderColor);r.uniforms.uBorderColor.value.set(o.color.r,o.color.g,o.color.b,o.alpha)}else{const o=r.uniforms.uBorderColor.value.w;r.uniforms.uBorderColor.value.set(e.borderColor.r,e.borderColor.g,e.borderColor.b,o)}e.opacity!==void 0&&(r.uniforms.uOpacity.value=e.opacity),e.bgOpacity!==void 0&&(r.uniforms.uBgColor.value.w=e.bgOpacity),e.borderOpacity!==void 0&&(r.uniforms.uBorderColor.value.w=e.borderOpacity),r.uniforms.uTexture&&e.texture!==void 0&&(r.uniforms.uTexture.value=e.texture);const t=e.texture!==void 0?e.texture:(n=r.uniforms.uTexture)==null?void 0:n.value;if(t&&(t.image instanceof ImageBitmap||t.image instanceof HTMLImageElement||t.image instanceof HTMLCanvasElement)){const o=t.image.naturalWidth||t.image.videoWidth||t.image.width,a=t.image.naturalHeight||t.image.videoHeight||t.image.height,i=e.width??r.uniforms.uSize.value.x,c=e.height??r.uniforms.uSize.value.y;if(o&&a&&i&&c){const s=o/a,l=i/c;if(s>l){const d=l/s;r.uniforms.uTextureRepeat.value.set(d,1),r.uniforms.uTextureOffset.value.set((1-d)/2,0)}else{const d=s/l;r.uniforms.uTextureRepeat.value.set(1,d),r.uniforms.uTextureOffset.value.set(0,(1-d)/2)}}}else r.uniforms.uTextureRepeat&&(r.uniforms.uTextureRepeat.value.set(1,1),r.uniforms.uTextureOffset.value.set(0,0));if(e.backgroundImage!==void 0){const o=k(e.backgroundImage);if(o){r.uniforms.uGradientCount.value=o.stops.length,r.uniforms.uGradientAngle.value=o.angle;for(let a=0;a<8;a++)if(a<o.stops.length){const i=o.stops[a];r.uniforms.uGradientColors.value[a].set(i.color.r,i.color.g,i.color.b,i.alpha),r.uniforms.uGradientStops.value[a]=i.stop}else r.uniforms.uGradientColors.value[a].set(0,0,0,0),r.uniforms.uGradientStops.value[a]=1}else r.uniforms.uGradientCount.value=0}for(const o of Object.keys(e))o!=="width"&&o!=="height"&&o!=="borderRadius"&&o!=="borderWidth"&&o!=="backgroundColor"&&o!=="borderColor"&&o!=="opacity"&&o!=="bgOpacity"&&o!=="borderOpacity"&&o!=="texture"&&o!=="backgroundImage"&&r.uniforms[o]!==void 0&&(r.uniforms[o].value!==void 0&&r.uniforms[o].value!==null&&typeof r.uniforms[o].value.set=="function"?Array.isArray(e[o])?r.uniforms[o].value.set(...e[o]):e[o]!==void 0&&(e[o].copy?r.uniforms[o].value.copy(e[o]):r.uniforms[o].value=e[o]):r.uniforms[o].value=e[o])}function U(r,e,t=0){if(e==null){r.set(0,0,0,0);return}if(typeof e=="number"){r.set(e,e,e,e);return}if(Array.isArray(e)){r.set(e[0],e[1],e[2],e[3]);return}const n=e.split("/")[0].trim().split(/\s+/),o=S(n[0],t),a=S(n[1]??n[0],t),i=S(n[2]??n[0],t),c=S(n[3]??n[1]??n[0],t);r.set(o,a,i,c)}const N={create(r,e,t,n,o,a=2,i=null,c){return r==="BOX"?L(e,n,o,i,c):r==="TEXT"?new A(t||"",e,n,o,a):new u.MeshBasicMaterial({visible:!1})},update(r,e,t,n,o,a,i=2,c){e==="BOX"?_(r,t,o,a,c):e==="TEXT"&&r.updateText(n||"",t,o,a,i)},forceUpdateUniforms(r,e){O(r,e)}};f.Painter=N,f.TextGenerator=A,f.createBoxMaterial=L,f.parseColor=B,f.parseLinearGradient=k,f.parsePixelValue=S,f.setBoxUniforms=O,f.splitByComma=P,f.updateBoxMaterial=_,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})});
@@ -1,5 +1,5 @@
1
1
  import * as THREE from "three";
2
- export declare function parsePixelValue(value: string | number): number;
2
+ export declare function parsePixelValue(value: string | number, baseSize?: number): number;
3
3
  export declare function parseColor(colorStr: string): {
4
4
  color: THREE.Color;
5
5
  alpha: number;
@@ -7,6 +7,7 @@ export interface TextStyles {
7
7
  direction: CanvasDirection;
8
8
  lineHeight: number;
9
9
  letterSpacing: number;
10
+ transform?: string;
10
11
  }
11
12
  export interface BoxStyles {
12
13
  backgroundColor: string;
@@ -18,6 +19,7 @@ export interface BoxStyles {
18
19
  borderRadius: string;
19
20
  borderColor: string;
20
21
  borderWidth: string;
22
+ transform?: string;
21
23
  }
22
24
  export interface ShaderHooks {
23
25
  uvModifier?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mirage-engine/painter",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "main": "src/index.ts",
5
5
  "types": "src/index.ts",
6
6
  "peerDependencies": {
@@ -42,10 +42,12 @@ export function createBoxMaterial(
42
42
  }
43
43
  }
44
44
 
45
- const declChunk = (hasTexture ? BoxChunk.declChunk : "") + "\n" + customUniformsCode;
46
- const baseUvCode = styles.isTraveler ? BoxChunk.uvChunk : "vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;\n";
47
- const uvChunk = hasTexture ? baseUvCode + (hooks?.uvModifier || "") : "";
48
- const baseColorChunk = hasTexture ? BoxChunk.baseColorChunk : "";
45
+ const hasHooks = hooks !== undefined;
46
+ const declChunk = (hasTexture || hasHooks ? BoxChunk.declChunk : "") + "\n" + customUniformsCode;
47
+ const screenUvDecl = "vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;\n";
48
+ const baseUvCode = styles.isTraveler ? "vec2 resultUv = screenUv;\n" : "vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;\n";
49
+ const uvChunk = (hasTexture || hasHooks) ? screenUvDecl + baseUvCode + (hooks?.uvModifier || "") : "";
50
+ const baseColorChunk = (hasTexture || hasHooks) ? BoxChunk.baseColorChunk : "";
49
51
  const colorModChunk = hooks?.colorModifier || "";
50
52
 
51
53
  const fragmentShader = BoxShader.fragmentShader
@@ -90,7 +92,7 @@ export function createBoxMaterial(
90
92
  uGradientStops: { value: new Float32Array(8) },
91
93
  };
92
94
  // border radius value initialize
93
- setBorderRadius(uniforms.uBorderRadius.value, styles.borderRadius);
95
+ setBorderRadius(uniforms.uBorderRadius.value, styles.borderRadius, Math.min(width, height));
94
96
 
95
97
  if (hasTexture) {
96
98
  uniforms.uTexture.value = texture;
@@ -156,7 +158,10 @@ export function setBoxUniforms(
156
158
  material.uniforms.uSize.value.set(values.width, values.height);
157
159
  }
158
160
  if (values.borderRadius !== undefined) {
159
- setBorderRadius(material.uniforms.uBorderRadius.value, values.borderRadius);
161
+ const baseSize = values.width !== undefined && values.height !== undefined
162
+ ? Math.min(values.width, values.height)
163
+ : Math.min(material.uniforms.uSize.value.x, material.uniforms.uSize.value.y);
164
+ setBorderRadius(material.uniforms.uBorderRadius.value, values.borderRadius, baseSize);
160
165
  }
161
166
  if (values.borderWidth !== undefined) {
162
167
  material.uniforms.uBorderWidth.value = values.borderWidth;
@@ -233,8 +238,8 @@ export function setBoxUniforms(
233
238
 
234
239
  const currentTex = values.texture !== undefined ? values.texture : material.uniforms.uTexture?.value;
235
240
  if (currentTex && (currentTex.image instanceof ImageBitmap || currentTex.image instanceof HTMLImageElement || currentTex.image instanceof HTMLCanvasElement)) {
236
- const imgWidth = currentTex.image.width;
237
- const imgHeight = currentTex.image.height;
241
+ const imgWidth = (currentTex.image as HTMLImageElement).naturalWidth || currentTex.image.videoWidth || currentTex.image.width;
242
+ const imgHeight = (currentTex.image as HTMLImageElement).naturalHeight || currentTex.image.videoHeight || currentTex.image.height;
238
243
  const domWidth = values.width ?? material.uniforms.uSize.value.x;
239
244
  const domHeight = values.height ?? material.uniforms.uSize.value.y;
240
245
 
@@ -323,7 +328,8 @@ export function setBoxUniforms(
323
328
 
324
329
  function setBorderRadius(
325
330
  target: THREE.Vector4,
326
- radius?: string | number | [number, number, number, number],
331
+ radius: string | number | [number, number, number, number] | undefined | null,
332
+ baseSize: number = 0,
327
333
  ) {
328
334
  if (radius === undefined || radius === null) {
329
335
  target.set(0, 0, 0, 0);
@@ -341,10 +347,10 @@ function setBorderRadius(
341
347
  }
342
348
 
343
349
  const arr = radius.split("/")[0].trim().split(/\s+/);
344
- const tl = parsePixelValue(arr[0]);
345
- const tr = parsePixelValue(arr[1] ?? arr[0]);
346
- const br = parsePixelValue(arr[2] ?? arr[0]);
347
- const bl = parsePixelValue(arr[3] ?? arr[1] ?? arr[0]);
350
+ const tl = parsePixelValue(arr[0], baseSize);
351
+ const tr = parsePixelValue(arr[1] ?? arr[0], baseSize);
352
+ const br = parsePixelValue(arr[2] ?? arr[0], baseSize);
353
+ const bl = parsePixelValue(arr[3] ?? arr[1] ?? arr[0], baseSize);
348
354
 
349
355
  target.set(tl, tr, br, bl);
350
356
  }
@@ -81,6 +81,18 @@ void main() {
81
81
  vec2 p = (vUv - 0.5) * uSize;
82
82
  vec2 halfSize = uSize * 0.5;
83
83
 
84
+ // CSS Proportional Border-Radius Clamping
85
+ float tl = uBorderRadius.x;
86
+ float tr = uBorderRadius.y;
87
+ float br = uBorderRadius.z;
88
+ float bl = uBorderRadius.w;
89
+ float fTop = uSize.x / max(tl + tr, 0.0001);
90
+ float fBottom = uSize.x / max(bl + br, 0.0001);
91
+ float fLeft = uSize.y / max(tl + bl, 0.0001);
92
+ float fRight = uSize.y / max(tr + br, 0.0001);
93
+ float f = min(1.0, min(min(fTop, fBottom), min(fLeft, fRight)));
94
+ vec4 clampedRadius = uBorderRadius * f;
95
+
84
96
  #INJECT_UV_MODIFIER
85
97
 
86
98
  // color decision pipeline
@@ -94,9 +106,8 @@ void main() {
94
106
  #INJECT_BASE_COLOR
95
107
 
96
108
  // Hybrid SDF
97
- vec2 xRadii = mix(uBorderRadius.xw, uBorderRadius.yz, step(0.0, p.x));
109
+ vec2 xRadii = mix(clampedRadius.xw, clampedRadius.yz, step(0.0, p.x));
98
110
  float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
99
- r = min(r, min(halfSize.x, halfSize.y));
100
111
 
101
112
  float d = sdRoundedBox(p, halfSize, r);
102
113
 
@@ -1,8 +1,12 @@
1
1
  import * as THREE from "three";
2
2
 
3
- export function parsePixelValue(value: string | number): number {
3
+ export function parsePixelValue(value: string | number, baseSize: number = 0): number {
4
4
  if (typeof value === "number") return value;
5
- return parseFloat(value) || 0;
5
+ const num = parseFloat(value) || 0;
6
+ if (typeof value === "string" && value.includes("%")) {
7
+ return (num / 100) * baseSize;
8
+ }
9
+ return num;
6
10
  }
7
11
 
8
12
  export function parseColor(colorStr: string) {
@@ -8,6 +8,7 @@ export interface TextStyles {
8
8
  direction: CanvasDirection;
9
9
  lineHeight: number; // px
10
10
  letterSpacing: number; // px
11
+ transform?: string;
11
12
  }
12
13
 
13
14
  export interface BoxStyles {
@@ -20,6 +21,7 @@ export interface BoxStyles {
20
21
  borderRadius: string;
21
22
  borderColor: string;
22
23
  borderWidth: string;
24
+ transform?: string;
23
25
  }
24
26
 
25
27
  export interface ShaderHooks {