argentui 0.3.0

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/dist/index.cjs ADDED
@@ -0,0 +1,838 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ EFFECTS: () => EFFECTS,
24
+ FINISHES: () => FINISHES,
25
+ Metal: () => Metal,
26
+ MetalBadge: () => MetalBadge,
27
+ MetalButton: () => MetalButton,
28
+ MetalCard: () => MetalCard,
29
+ MetalFill: () => MetalFill,
30
+ MetalLogo: () => MetalLogo,
31
+ MetalProgress: () => MetalProgress,
32
+ MetalText: () => MetalText,
33
+ MetalToggle: () => MetalToggle,
34
+ NATIVE_TONES: () => NATIVE_TONES,
35
+ TONE_PARAMS: () => TONE_PARAMS,
36
+ mountMetal: () => mountMetal,
37
+ setHaptics: () => setHaptics,
38
+ useInView: () => useInView,
39
+ useMounted: () => useMounted,
40
+ useReducedMotion: () => useReducedMotion
41
+ });
42
+ module.exports = __toCommonJS(index_exports);
43
+
44
+ // src/surfaces.tsx
45
+ var import_react2 = require("react");
46
+
47
+ // src/metal.tsx
48
+ var import_react = require("react");
49
+ var import_shaders_react = require("@paper-design/shaders-react");
50
+
51
+ // src/engine.ts
52
+ var VERT = `#version 300 es
53
+ void main() {
54
+ vec2 pos = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
55
+ gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);
56
+ }`;
57
+ var FRAG = `#version 300 es
58
+ precision highp float;
59
+ uniform vec2 u_res;
60
+ uniform float u_time;
61
+ uniform vec3 u_light;
62
+ uniform vec3 u_dark;
63
+ uniform float u_rep;
64
+ uniform float u_angle;
65
+ uniform float u_soft;
66
+ uniform float u_disp;
67
+ uniform float u_dist;
68
+ uniform float u_scale;
69
+ out vec4 outColor;
70
+
71
+ // 2D simplex noise \u2014 Ashima Arts / Stefan Gustavson (webgl-noise, MIT).
72
+ vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
73
+ vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
74
+ vec3 permute(vec3 x) { return mod289(((x * 34.0) + 1.0) * x); }
75
+ float snoise(vec2 v) {
76
+ const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
77
+ vec2 i = floor(v + dot(v, C.yy));
78
+ vec2 x0 = v - i + dot(i, C.xx);
79
+ vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
80
+ vec4 x12 = x0.xyxy + C.xxzz;
81
+ x12.xy -= i1;
82
+ i = mod289(i);
83
+ vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
84
+ vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
85
+ m = m * m;
86
+ m = m * m;
87
+ vec3 x = 2.0 * fract(p * C.www) - 1.0;
88
+ vec3 h = abs(x) - 0.5;
89
+ vec3 ox = floor(x + 0.5);
90
+ vec3 a0 = x - ox;
91
+ m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
92
+ vec3 g;
93
+ g.x = a0.x * x0.x + h.x * x0.y;
94
+ g.yz = a0.yz * x12.xz + h.yz * x12.yw;
95
+ return 130.0 * dot(m, g);
96
+ }
97
+
98
+ // One reflection band: a soft triangle wave over the warped coordinate.
99
+ float stripe(float coord) {
100
+ float f = fract(coord);
101
+ float tri = abs(f * 2.0 - 1.0);
102
+ return smoothstep(0.5 - u_soft, 0.5 + u_soft, tri);
103
+ }
104
+
105
+ void main() {
106
+ vec2 uv = (gl_FragCoord.xy / u_res - 0.5);
107
+ uv.x *= u_res.x / u_res.y;
108
+ uv /= max(u_scale, 0.001);
109
+
110
+ float t = u_time;
111
+
112
+ // curvature: bands bend as if wrapping a bulged surface
113
+ float d = length(uv);
114
+ float bulge = 1.0 - 0.45 * d * d;
115
+
116
+ // flowing warp \u2014 two noise octaves drifting at different rates
117
+ float n = snoise(uv * 0.9 + vec2(0.0, -t * 0.28));
118
+ n += 0.35 * snoise(uv * 1.8 + vec2(t * 0.1, -t * 0.45));
119
+
120
+ // rotate so bands run along u_angle
121
+ float c = cos(u_angle), s = sin(u_angle);
122
+ vec2 ruv = mat2(c, -s, s, c) * uv;
123
+
124
+ float coord = ruv.y * bulge * u_rep + n * u_dist * 2.2 - t * 0.22;
125
+
126
+ // per-channel dispersion \u2014 the chrome fringing
127
+ float r = stripe(coord + u_disp);
128
+ float g = stripe(coord);
129
+ float b = stripe(coord - u_disp);
130
+
131
+ vec3 col = vec3(
132
+ mix(u_dark.r, u_light.r, r),
133
+ mix(u_dark.g, u_light.g, g),
134
+ mix(u_dark.b, u_light.b, b)
135
+ );
136
+
137
+ // rim lift toward the edges
138
+ col *= 0.94 + 0.14 * smoothstep(0.25, 0.85, d);
139
+
140
+ outColor = vec4(col, 1.0);
141
+ }`;
142
+ function hexToRgb(hex) {
143
+ const h = hex.replace("#", "");
144
+ const v = h.length === 3 ? h.split("").map((ch) => ch + ch).join("") : h;
145
+ const n = parseInt(v, 16);
146
+ return [(n >> 16 & 255) / 255, (n >> 8 & 255) / 255, (n & 255) / 255];
147
+ }
148
+ var NATIVE_TONES = {
149
+ silver: { light: "#f8fafc", dark: "#23262b", repetition: 1.8, angle: 68, softness: 0.34, dispersion: 0.03, distortion: 0.38 },
150
+ gold: { light: "#ffe9a8", dark: "#6e5408", repetition: 1.8, angle: 68, softness: 0.34, dispersion: 0.028, distortion: 0.36 },
151
+ gunmetal: { light: "#b6bec9", dark: "#14161a", repetition: 1.6, angle: 80, softness: 0.36, dispersion: 0.025, distortion: 0.34 },
152
+ obsidian: { light: "#787c88", dark: "#000000", repetition: 1.4, angle: 92, softness: 0.42, dispersion: 0.02, distortion: 0.28 }
153
+ };
154
+ function compile(gl, type, src) {
155
+ const sh = gl.createShader(type);
156
+ if (!sh) return null;
157
+ gl.shaderSource(sh, src);
158
+ gl.compileShader(sh);
159
+ if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
160
+ console.error("argent shader:", gl.getShaderInfoLog(sh));
161
+ gl.deleteShader(sh);
162
+ return null;
163
+ }
164
+ return sh;
165
+ }
166
+ function mountMetal(canvas, params) {
167
+ const gl = canvas.getContext("webgl2", { antialias: true });
168
+ if (!gl) return null;
169
+ const vs = compile(gl, gl.VERTEX_SHADER, VERT);
170
+ const fs = compile(gl, gl.FRAGMENT_SHADER, FRAG);
171
+ if (!vs || !fs) return null;
172
+ const prog = gl.createProgram();
173
+ gl.attachShader(prog, vs);
174
+ gl.attachShader(prog, fs);
175
+ gl.linkProgram(prog);
176
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
177
+ console.error("argent shader link:", gl.getProgramInfoLog(prog));
178
+ return null;
179
+ }
180
+ gl.useProgram(prog);
181
+ const U = (name) => gl.getUniformLocation(prog, name);
182
+ const uRes = U("u_res"), uTime = U("u_time"), uLight = U("u_light"), uDark = U("u_dark");
183
+ const uRep = U("u_rep"), uAngle = U("u_angle"), uSoft = U("u_soft");
184
+ const uDisp = U("u_disp"), uDist = U("u_dist"), uScale = U("u_scale");
185
+ let p = { ...params };
186
+ let raf = 0;
187
+ let tAcc = Math.random() * 40;
188
+ let last = performance.now();
189
+ let dead = false;
190
+ function draw() {
191
+ raf = 0;
192
+ if (dead) return;
193
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
194
+ const w = Math.max(1, Math.round(canvas.clientWidth * dpr));
195
+ const h = Math.max(1, Math.round(canvas.clientHeight * dpr));
196
+ if (canvas.width !== w || canvas.height !== h) {
197
+ canvas.width = w;
198
+ canvas.height = h;
199
+ gl.viewport(0, 0, w, h);
200
+ }
201
+ const now = performance.now();
202
+ tAcc += (now - last) / 1e3 * p.speed;
203
+ last = now;
204
+ gl.uniform2f(uRes, w, h);
205
+ gl.uniform1f(uTime, tAcc);
206
+ gl.uniform3fv(uLight, hexToRgb(p.light));
207
+ gl.uniform3fv(uDark, hexToRgb(p.dark));
208
+ gl.uniform1f(uRep, p.repetition);
209
+ gl.uniform1f(uAngle, p.angle * Math.PI / 180);
210
+ gl.uniform1f(uSoft, Math.max(0.02, p.softness));
211
+ gl.uniform1f(uDisp, p.dispersion);
212
+ gl.uniform1f(uDist, p.distortion);
213
+ gl.uniform1f(uScale, p.scale);
214
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
215
+ if (p.speed > 0) raf = requestAnimationFrame(draw);
216
+ }
217
+ draw();
218
+ return {
219
+ update(np) {
220
+ p = { ...p, ...np };
221
+ last = performance.now();
222
+ if (!raf) raf = requestAnimationFrame(draw);
223
+ },
224
+ destroy() {
225
+ dead = true;
226
+ if (raf) cancelAnimationFrame(raf);
227
+ gl.getExtension("WEBGL_lose_context")?.loseContext();
228
+ }
229
+ };
230
+ }
231
+
232
+ // src/metal.tsx
233
+ var import_jsx_runtime = require("react/jsx-runtime");
234
+ var TONE_PARAMS = {
235
+ silver: {
236
+ colorBack: "#a7abb1",
237
+ colorTint: "#ffffff",
238
+ repetition: 3,
239
+ softness: 0.18,
240
+ shiftRed: 0.32,
241
+ shiftBlue: 0.32,
242
+ distortion: 0.14,
243
+ contour: 0.55,
244
+ angle: 68
245
+ },
246
+ gold: {
247
+ colorBack: "#94700e",
248
+ colorTint: "#ffedb0",
249
+ repetition: 3,
250
+ softness: 0.2,
251
+ shiftRed: 0.3,
252
+ shiftBlue: 0.12,
253
+ distortion: 0.13,
254
+ contour: 0.5,
255
+ angle: 68
256
+ },
257
+ gunmetal: {
258
+ colorBack: "#33373d",
259
+ colorTint: "#b2bac4",
260
+ repetition: 2.6,
261
+ softness: 0.26,
262
+ shiftRed: 0.22,
263
+ shiftBlue: 0.32,
264
+ distortion: 0.1,
265
+ contour: 0.45,
266
+ angle: 80
267
+ },
268
+ obsidian: {
269
+ colorBack: "#000000",
270
+ colorTint: "#9498a6",
271
+ repetition: 2,
272
+ softness: 0.4,
273
+ shiftRed: 0.14,
274
+ shiftBlue: 0.24,
275
+ distortion: 0.07,
276
+ contour: 0.32,
277
+ angle: 92
278
+ }
279
+ };
280
+ var imageMountQueue = Promise.resolve();
281
+ function useStaggeredMount() {
282
+ const [go, setGo] = (0, import_react.useState)(false);
283
+ (0, import_react.useEffect)(() => {
284
+ let alive = true;
285
+ imageMountQueue = imageMountQueue.then(async () => {
286
+ if (!alive) return;
287
+ setGo(true);
288
+ await new Promise((r) => setTimeout(r, 300));
289
+ });
290
+ return () => {
291
+ alive = false;
292
+ };
293
+ }, []);
294
+ return go;
295
+ }
296
+ function useMounted() {
297
+ const [m, setM] = (0, import_react.useState)(false);
298
+ (0, import_react.useEffect)(() => setM(true), []);
299
+ return m;
300
+ }
301
+ function useReducedMotion() {
302
+ const [reduced, setReduced] = (0, import_react.useState)(false);
303
+ (0, import_react.useEffect)(() => {
304
+ const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
305
+ setReduced(mq.matches);
306
+ const onChange = (e) => setReduced(e.matches);
307
+ mq.addEventListener("change", onChange);
308
+ return () => mq.removeEventListener("change", onChange);
309
+ }, []);
310
+ return reduced;
311
+ }
312
+ function useInView(ref, margin = "250px") {
313
+ const [inView, setInView] = (0, import_react.useState)(false);
314
+ (0, import_react.useEffect)(() => {
315
+ const el = ref.current;
316
+ if (!el) return;
317
+ const io = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting), { rootMargin: margin });
318
+ io.observe(el);
319
+ return () => io.disconnect();
320
+ }, [ref, margin]);
321
+ return inView;
322
+ }
323
+ var FINISHES = {
324
+ /** Cards, nav, panels — broad flowing reflection bands (the tone defaults). */
325
+ surface: { scale: 1.1 },
326
+ /** Buttons — slightly spread, calmer warp so labels stay readable. */
327
+ button: { scale: 1.4, distortion: 0.1 },
328
+ /** Thin horizontal strips (progress) — near-vertical stripes crossing the bar. */
329
+ bar: { angle: 14, repetition: 5, softness: 0.3, distortion: 0.06, shift: 0.5, scale: 0.8, speed: 0.75 },
330
+ /** Small round things (toggle thumbs) — one soft highlight, like a polished sphere. */
331
+ orb: { angle: 112, angleJitter: 20, repetition: 1.7, softness: 0.5, distortion: 0.05, shift: 0.4, scale: 1.5 },
332
+ /** Hairline edges (badges, thin borders) — dense bands so any slice sparkles. */
333
+ rim: { angle: 40, repetition: 4.5, softness: 0.3, distortion: 0.08, shift: 0.5, scale: 0.85, speed: 0.85 }
334
+ };
335
+ var EFFECTS = {
336
+ /** The default — steady flowing reflection bands. */
337
+ flow: {},
338
+ /** Slow, heavy, half-melted — soft wide bands churning lazily. */
339
+ molten: { softness: 0.5, distortion: 0.3, speed: 0.45, shift: 0.6 },
340
+ /** Agitated surface — tighter bands, hard noise, quick motion. */
341
+ ripple: { repetition: 4, softness: 0.24, distortion: 0.5, speed: 1.4 },
342
+ /** Mirror-polished — crisp hard bands, strong chromatic fringe, calm. */
343
+ chrome: { softness: 0.05, distortion: 0.05, shift: 1.6, speed: 0.8 },
344
+ /** Horizontal swells rolling through the surface. */
345
+ wave: { angle: 0, repetition: 5, softness: 0.36, distortion: 0.18, speed: 0.7 }
346
+ };
347
+ var FILL_STYLE = { position: "absolute", inset: 0, width: "100%", height: "100%" };
348
+ function NativeCanvas({ params }) {
349
+ const canvasRef = (0, import_react.useRef)(null);
350
+ const mountRef = (0, import_react.useRef)(null);
351
+ const initial = (0, import_react.useRef)(params);
352
+ initial.current = params;
353
+ (0, import_react.useEffect)(() => {
354
+ const canvas = canvasRef.current;
355
+ if (!canvas) return;
356
+ mountRef.current = mountMetal(canvas, initial.current);
357
+ return () => {
358
+ mountRef.current?.destroy();
359
+ mountRef.current = null;
360
+ };
361
+ }, []);
362
+ (0, import_react.useEffect)(() => {
363
+ mountRef.current?.update(params);
364
+ }, [params]);
365
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("canvas", { ref: canvasRef, style: { ...FILL_STYLE, display: "block" } });
366
+ }
367
+ function MetalFill({ tone, speed = 1, scale, engine = "paper", finish = "surface", effect = "flow", angle }) {
368
+ const ref = (0, import_react.useRef)(null);
369
+ const mounted = useMounted();
370
+ const inView = useInView(ref);
371
+ const reduced = useReducedMotion();
372
+ const f = FINISHES[finish];
373
+ const e = EFFECTS[effect];
374
+ const [jitter] = (0, import_react.useState)(() => f.angleJitter ? (Math.random() * 2 - 1) * f.angleJitter : 0);
375
+ const base = TONE_PARAMS[tone];
376
+ const effAngle = (angle ?? e.angle ?? f.angle ?? base.angle ?? 70) + jitter;
377
+ const effScale = scale ?? f.scale ?? 1.1;
378
+ const effSpeed = (reduced ? 0 : speed) * (f.speed ?? 1) * (e.speed ?? 1);
379
+ const shift = (f.shift ?? 1) * (e.shift ?? 1);
380
+ const repetition = e.repetition ?? f.repetition ?? base.repetition;
381
+ const softness = e.softness ?? f.softness ?? base.softness;
382
+ const distortion = e.distortion ?? f.distortion ?? base.distortion;
383
+ const native = NATIVE_TONES[tone];
384
+ const nativeParams = {
385
+ ...native,
386
+ angle: effAngle,
387
+ repetition: repetition ?? native.repetition,
388
+ softness: softness ?? native.softness,
389
+ // the native warp amount runs ~3× paper's distortion scale
390
+ distortion: distortion !== void 0 ? distortion * 3 : native.distortion,
391
+ dispersion: native.dispersion * shift,
392
+ speed: effSpeed,
393
+ scale: effScale
394
+ };
395
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ref, "aria-hidden": "true", style: { position: "absolute", inset: 0 }, children: mounted && inView && (engine === "native" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NativeCanvas, { params: nativeParams }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
396
+ import_shaders_react.LiquidMetal,
397
+ {
398
+ shape: "none",
399
+ fit: "cover",
400
+ scale: effScale,
401
+ speed: effSpeed,
402
+ ...base,
403
+ angle: effAngle,
404
+ repetition,
405
+ softness,
406
+ distortion,
407
+ shiftRed: (base.shiftRed ?? 0.3) * shift,
408
+ shiftBlue: (base.shiftBlue ?? 0.3) * shift,
409
+ style: FILL_STYLE
410
+ }
411
+ )) });
412
+ }
413
+
414
+ // src/haptics.ts
415
+ var enabled = true;
416
+ function setHaptics(on) {
417
+ enabled = on;
418
+ }
419
+ function vibrate(pattern) {
420
+ if (!enabled || typeof navigator === "undefined" || !("vibrate" in navigator)) return;
421
+ try {
422
+ navigator.vibrate(pattern);
423
+ } catch {
424
+ }
425
+ }
426
+ var PRESS_PATTERN = 8;
427
+
428
+ // src/surfaces.tsx
429
+ var import_jsx_runtime2 = require("react/jsx-runtime");
430
+ function cx(...parts) {
431
+ return parts.filter(Boolean).join(" ");
432
+ }
433
+ function assignRef(ref, node) {
434
+ if (typeof ref === "function") ref(node);
435
+ else if (ref) ref.current = node;
436
+ }
437
+ function buildVars(radius, borderWidth, halo, style) {
438
+ const vars = {
439
+ borderRadius: radius,
440
+ "--argent-bw": `${borderWidth}px`,
441
+ "--argent-radius": `${radius}px`,
442
+ ...style
443
+ };
444
+ if (halo) vars["--argent-halo"] = `${halo === true ? 8 : halo}px`;
445
+ return vars;
446
+ }
447
+ var Metal = (0, import_react2.forwardRef)(function Metal2({
448
+ as,
449
+ tone = "silver",
450
+ variant = "border",
451
+ frame = "single",
452
+ tint = false,
453
+ borderWidth = 1.5,
454
+ revealOnHover = false,
455
+ radius = 14,
456
+ speed,
457
+ metalScale,
458
+ engine,
459
+ finish,
460
+ effect,
461
+ angle,
462
+ sheen = false,
463
+ halo = false,
464
+ className,
465
+ style,
466
+ children,
467
+ ...rest
468
+ }, ref) {
469
+ const Tag = as ?? "div";
470
+ const border = variant === "border";
471
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
472
+ Tag,
473
+ {
474
+ ref: (node) => assignRef(ref, node),
475
+ className: cx(
476
+ "argent",
477
+ border ? "argent--border" : "argent--fill",
478
+ border && frame === "double" && "argent--double",
479
+ border && tint && "argent--tint",
480
+ revealOnHover && "argent--reveal",
481
+ sheen && "argent--sheen",
482
+ !!halo && "argent--halo",
483
+ className
484
+ ),
485
+ "data-tone": tone,
486
+ style: buildVars(radius, borderWidth, halo, style),
487
+ ...rest,
488
+ children: [
489
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "argent-fill", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MetalFill, { tone, speed, scale: metalScale, engine, finish, effect, angle }) }),
490
+ border && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "argent-core", "aria-hidden": "true" }),
491
+ sheen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "argent-sheen", "aria-hidden": "true" }),
492
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "argent-content", children })
493
+ ]
494
+ }
495
+ );
496
+ });
497
+ var MetalCard = (0, import_react2.forwardRef)(function MetalCard2({ className, radius = 18, ...rest }, ref) {
498
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Metal, { ref, radius, className: cx("argent-card", className), ...rest });
499
+ });
500
+ var SIZE_RADIUS = { sm: 11, md: 13, lg: 15 };
501
+ var MetalButton = (0, import_react2.forwardRef)(function MetalButton2({ tone = "silver", size = "md", variant = "border", radius, borderWidth = 1.5, revealOnHover = true, haptics = true, speed, engine, finish = "button", effect, angle, halo = false, type = "button", className, children, style, onPointerDown, ...rest }, ref) {
502
+ const border = variant === "border";
503
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
504
+ "button",
505
+ {
506
+ ref,
507
+ type,
508
+ onPointerDown: (e) => {
509
+ if (haptics) vibrate(PRESS_PATTERN);
510
+ onPointerDown?.(e);
511
+ },
512
+ className: cx("argent", border ? "argent--border" : "argent--fill", border && revealOnHover && "argent--reveal", "argent--sheen", !!halo && "argent--halo", "argent-btn", `argent-btn--${size}`, className),
513
+ "data-tone": tone,
514
+ style: buildVars(radius ?? SIZE_RADIUS[size], borderWidth, halo, style),
515
+ ...rest,
516
+ children: [
517
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "argent-fill", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MetalFill, { tone, speed, engine, finish, effect, angle }) }),
518
+ border && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "argent-core", "aria-hidden": "true" }),
519
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "argent-sheen", "aria-hidden": "true" }),
520
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "argent-content argent-btn-label", children })
521
+ ]
522
+ }
523
+ );
524
+ });
525
+
526
+ // src/controls.tsx
527
+ var import_react3 = require("react");
528
+ var import_jsx_runtime3 = require("react/jsx-runtime");
529
+ var MetalToggle = (0, import_react3.forwardRef)(function MetalToggle2({ tone = "silver", checked, defaultChecked = false, onCheckedChange, haptics = true, className, onClick, ...rest }, ref) {
530
+ const [internal, setInternal] = (0, import_react3.useState)(defaultChecked);
531
+ const isOn = checked ?? internal;
532
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
533
+ "button",
534
+ {
535
+ ref,
536
+ type: "button",
537
+ role: "switch",
538
+ "aria-checked": isOn,
539
+ "data-tone": tone,
540
+ "data-checked": isOn || void 0,
541
+ className: ["argent-toggle", className].filter(Boolean).join(" "),
542
+ onClick: (e) => {
543
+ if (haptics) vibrate(PRESS_PATTERN);
544
+ if (checked === void 0) setInternal(!isOn);
545
+ onCheckedChange?.(!isOn);
546
+ onClick?.(e);
547
+ },
548
+ ...rest,
549
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "argent-toggle-thumb", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MetalFill, { tone, finish: "orb" }) })
550
+ }
551
+ );
552
+ });
553
+ var MetalProgress = (0, import_react3.forwardRef)(function MetalProgress2({ tone = "silver", value, height = 10, className, style, ...rest }, ref) {
554
+ const indeterminate = value === void 0;
555
+ const clamped = indeterminate ? 0 : Math.max(0, Math.min(100, value));
556
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
557
+ "div",
558
+ {
559
+ ref,
560
+ role: "progressbar",
561
+ "aria-valuemin": 0,
562
+ "aria-valuemax": 100,
563
+ "aria-valuenow": indeterminate ? void 0 : clamped,
564
+ "data-tone": tone,
565
+ className: ["argent-progress", indeterminate && "argent-progress--indeterminate", className].filter(Boolean).join(" "),
566
+ style: { height, ...style },
567
+ ...rest,
568
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
569
+ "span",
570
+ {
571
+ className: "argent-progress-fill",
572
+ style: indeterminate ? void 0 : { width: `${clamped}%` },
573
+ "aria-hidden": "true",
574
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MetalFill, { tone, finish: "bar" })
575
+ }
576
+ )
577
+ }
578
+ );
579
+ });
580
+ var MetalBadge = (0, import_react3.forwardRef)(function MetalBadge2({ className, radius = 999, borderWidth = 1, finish = "rim", ...rest }, ref) {
581
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
582
+ Metal,
583
+ {
584
+ ref,
585
+ as: "span",
586
+ variant: "border",
587
+ radius,
588
+ borderWidth,
589
+ finish,
590
+ className: ["argent-badge", className].filter(Boolean).join(" "),
591
+ ...rest
592
+ }
593
+ );
594
+ });
595
+
596
+ // src/text.tsx
597
+ var import_react4 = require("react");
598
+ var import_shaders_react2 = require("@paper-design/shaders-react");
599
+ var import_jsx_runtime4 = require("react/jsx-runtime");
600
+ var DEFAULT_STACK = "-apple-system, 'Helvetica Neue', Helvetica, Arial, sans-serif";
601
+ function svgOpen(g) {
602
+ const style = g.fontCss ? `<style>${g.fontCss}</style>` : "";
603
+ return `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ${g.w} ${g.h}' width='${g.w}' height='${g.h}'>${style}`;
604
+ }
605
+ function svgText(g, attrs) {
606
+ const safe = g.text.replace(/&/g, "&amp;").replace(/</g, "&lt;");
607
+ return `<text x='50%' y='${Math.round(g.fontSize * 1)}' font-size='${g.fontSize}' font-family="${g.fontFamily.replace(/"/g, "'")}" font-weight='${g.fontWeight}' text-anchor='middle' ${attrs}>${safe}</text>`;
608
+ }
609
+ function encode(svg) {
610
+ return `data:image/svg+xml,${encodeURIComponent(svg)}`;
611
+ }
612
+ function fillSilhouette(g) {
613
+ return encode(`${svgOpen(g)}${svgText(g, "fill='#000'")}</svg>`);
614
+ }
615
+ function outlineSilhouette(g, ow) {
616
+ return encode(`${svgOpen(g)}${svgText(g, `fill='none' stroke='#000' stroke-width='${ow}' stroke-linejoin='round'`)}</svg>`);
617
+ }
618
+ function interior(g, fill, gradient) {
619
+ if (!gradient) return encode(`${svgOpen(g)}${svgText(g, `fill='${fill}'`)}</svg>`);
620
+ const defs = `<defs><linearGradient id='g' x1='0' y1='0' x2='0' y2='1'><stop offset='0' stop-color='${gradient[0]}'/><stop offset='1' stop-color='${gradient[1]}'/></linearGradient></defs>`;
621
+ return encode(`${svgOpen(g)}${defs}${svgText(g, "fill='url(#g)'")}</svg>`);
622
+ }
623
+ var LAYER = { position: "absolute", inset: 0, width: "100%", height: "100%" };
624
+ function ShaderText({
625
+ text,
626
+ tone,
627
+ variant,
628
+ fill,
629
+ fillGradient,
630
+ outlineWidth,
631
+ fontSize,
632
+ fontWeight,
633
+ fontFamily,
634
+ fontCss,
635
+ speed,
636
+ shimmer,
637
+ className,
638
+ style,
639
+ ...rest
640
+ }) {
641
+ const ref = (0, import_react4.useRef)(null);
642
+ const mounted = useMounted();
643
+ const inView = useInView(ref);
644
+ const reduced = useReducedMotion();
645
+ const turn = useStaggeredMount();
646
+ const [geom, setGeom] = (0, import_react4.useState)(null);
647
+ const outlined = variant === "outline";
648
+ const ow = outlineWidth ?? Math.max(2, Math.round(fontSize * 0.05));
649
+ (0, import_react4.useEffect)(() => {
650
+ let alive = true;
651
+ const fontSpec = `${fontWeight} ${fontSize}px ${fontFamily}`;
652
+ const measure = () => {
653
+ if (!alive) return;
654
+ const ctx = document.createElement("canvas").getContext("2d");
655
+ if (!ctx) return;
656
+ ctx.font = fontSpec;
657
+ const m = ctx.measureText(text);
658
+ setGeom({
659
+ text,
660
+ w: Math.ceil(m.width + fontSize * 0.24 + ow * 2),
661
+ h: Math.ceil(fontSize * 1.3 + ow),
662
+ fontSize,
663
+ fontWeight,
664
+ fontFamily,
665
+ fontCss
666
+ });
667
+ };
668
+ if (document.fonts?.load) document.fonts.load(fontSpec, text).then(measure, measure);
669
+ else measure();
670
+ return () => {
671
+ alive = false;
672
+ };
673
+ }, [text, fontSize, fontWeight, fontFamily, fontCss, ow]);
674
+ const ready = mounted && inView && turn && geom;
675
+ const { colorBack: _drop, ...params } = TONE_PARAMS[tone];
676
+ const shaderParams = outlined ? { ...params, contour: 0.2, distortion: Math.min(params.distortion ?? 0.1, 0.08) } : params;
677
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
678
+ "span",
679
+ {
680
+ ref,
681
+ role: "img",
682
+ "aria-label": text,
683
+ className,
684
+ style: {
685
+ position: "relative",
686
+ display: "inline-block",
687
+ verticalAlign: "middle",
688
+ width: geom?.w,
689
+ height: geom?.h,
690
+ ...style
691
+ },
692
+ ...rest,
693
+ children: ready ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
694
+ outlined && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: interior(geom, fill, fillGradient), alt: "", "aria-hidden": "true", style: LAYER }),
695
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
696
+ import_shaders_react2.LiquidMetal,
697
+ {
698
+ image: outlined ? outlineSilhouette(geom, ow) : fillSilhouette(geom),
699
+ suspendWhenProcessingImage: false,
700
+ colorBack: "#00000000",
701
+ fit: "contain",
702
+ scale: 0.97,
703
+ speed: reduced ? 0 : speed,
704
+ ...shaderParams,
705
+ style: LAYER
706
+ },
707
+ `${geom.text}|${geom.fontFamily}|${geom.w}|${outlined ? "o" : "f"}`
708
+ )
709
+ ] }) : (
710
+ // CSS chrome stands in until the shader is ready (and during SSR)
711
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
712
+ "span",
713
+ {
714
+ "aria-hidden": "true",
715
+ className: ["argent-text", shimmer && !outlined && "argent-text--shimmer"].filter(Boolean).join(" "),
716
+ "data-tone": tone,
717
+ style: {
718
+ fontSize,
719
+ fontWeight,
720
+ fontFamily,
721
+ lineHeight: 1.3,
722
+ whiteSpace: "nowrap",
723
+ ...outlined && {
724
+ background: "none",
725
+ WebkitTextFillColor: fillGradient?.[0] ?? fill,
726
+ color: fillGradient?.[0] ?? fill,
727
+ WebkitTextStroke: "1px rgba(220, 224, 230, 0.55)"
728
+ }
729
+ },
730
+ children: text
731
+ }
732
+ )
733
+ )
734
+ }
735
+ );
736
+ }
737
+ var MetalText = (0, import_react4.forwardRef)(function MetalText2({
738
+ as,
739
+ tone = "silver",
740
+ shimmer = true,
741
+ shader = false,
742
+ variant = "fill",
743
+ fill = "#101114",
744
+ fillGradient,
745
+ outlineWidth,
746
+ fontSize = 64,
747
+ fontWeight = 800,
748
+ fontFamily = DEFAULT_STACK,
749
+ fontCss,
750
+ speed = 1,
751
+ className,
752
+ children,
753
+ ...rest
754
+ }, ref) {
755
+ if (shader) {
756
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
757
+ ShaderText,
758
+ {
759
+ text: String(children),
760
+ tone,
761
+ variant,
762
+ fill,
763
+ fillGradient,
764
+ outlineWidth,
765
+ fontSize,
766
+ fontWeight,
767
+ fontFamily,
768
+ fontCss,
769
+ speed,
770
+ shimmer,
771
+ className,
772
+ ...rest
773
+ }
774
+ );
775
+ }
776
+ const Tag = as ?? "span";
777
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
778
+ Tag,
779
+ {
780
+ ref,
781
+ className: ["argent-text", shimmer && "argent-text--shimmer", className].filter(Boolean).join(" "),
782
+ "data-tone": tone,
783
+ ...rest,
784
+ children
785
+ }
786
+ );
787
+ });
788
+
789
+ // src/logo.tsx
790
+ var import_react5 = require("react");
791
+ var import_shaders_react3 = require("@paper-design/shaders-react");
792
+ var import_jsx_runtime5 = require("react/jsx-runtime");
793
+ function MetalLogo({ src, tone = "silver", size = 160, width, height, speed = 1, style, ...rest }) {
794
+ const ref = (0, import_react5.useRef)(null);
795
+ const mounted = useMounted();
796
+ const inView = useInView(ref);
797
+ const reduced = useReducedMotion();
798
+ const turn = useStaggeredMount();
799
+ const w = width ?? size;
800
+ const h = height ?? size;
801
+ const { colorBack: _drop, ...params } = TONE_PARAMS[tone];
802
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref, style: { position: "relative", width: w, height: h, ...style }, ...rest, children: mounted && inView && turn && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
803
+ import_shaders_react3.LiquidMetal,
804
+ {
805
+ image: src,
806
+ suspendWhenProcessingImage: false,
807
+ colorBack: "#00000000",
808
+ fit: "contain",
809
+ scale: 0.92,
810
+ speed: reduced ? 0 : speed,
811
+ ...params,
812
+ style: { position: "absolute", inset: 0, width: "100%", height: "100%" }
813
+ },
814
+ src
815
+ ) });
816
+ }
817
+ // Annotate the CommonJS export names for ESM import in node:
818
+ 0 && (module.exports = {
819
+ EFFECTS,
820
+ FINISHES,
821
+ Metal,
822
+ MetalBadge,
823
+ MetalButton,
824
+ MetalCard,
825
+ MetalFill,
826
+ MetalLogo,
827
+ MetalProgress,
828
+ MetalText,
829
+ MetalToggle,
830
+ NATIVE_TONES,
831
+ TONE_PARAMS,
832
+ mountMetal,
833
+ setHaptics,
834
+ useInView,
835
+ useMounted,
836
+ useReducedMotion
837
+ });
838
+ //# sourceMappingURL=index.cjs.map