@nadicodeai/ui 6.0.0 → 6.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/AGENTS.md +1 -1
- package/dist/components/forma-avatar.d.ts +23 -0
- package/dist/components/forma-avatar.d.ts.map +1 -0
- package/dist/components/forma-avatar.js +95 -0
- package/dist/hooks/use-confirm-action.d.ts +0 -6
- package/dist/hooks/use-confirm-action.d.ts.map +1 -1
- package/dist/hooks/use-confirm-action.js +5 -12
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/internal/forma/frame.d.ts +10 -0
- package/dist/internal/forma/frame.d.ts.map +1 -0
- package/dist/internal/forma/frame.js +211 -0
- package/dist/internal/forma/geometry.d.ts +41 -0
- package/dist/internal/forma/geometry.d.ts.map +1 -0
- package/dist/internal/forma/geometry.js +235 -0
- package/dist/internal/forma/hallmark.d.ts +5 -0
- package/dist/internal/forma/hallmark.d.ts.map +1 -0
- package/dist/internal/forma/hallmark.js +4 -0
- package/dist/internal/forma/model.d.ts +53 -0
- package/dist/internal/forma/model.d.ts.map +1 -0
- package/dist/internal/forma/model.js +70 -0
- package/dist/internal/forma/motion.d.ts +42 -0
- package/dist/internal/forma/motion.d.ts.map +1 -0
- package/dist/internal/forma/motion.js +194 -0
- package/dist/internal/forma/rig.d.ts +10 -0
- package/dist/internal/forma/rig.d.ts.map +1 -0
- package/dist/internal/forma/rig.js +211 -0
- package/dist/internal/forma/still.d.ts +4 -0
- package/dist/internal/forma/still.d.ts.map +1 -0
- package/dist/internal/forma/still.js +8 -0
- package/dist/lib/use-copy-to-clipboard.d.ts +0 -41
- package/dist/lib/use-copy-to-clipboard.d.ts.map +1 -1
- package/dist/lib/use-copy-to-clipboard.js +6 -15
- package/docs/contract.md +2 -2
- package/docs/forma-avatar.md +92 -0
- package/package.json +2 -2
package/AGENTS.md
CHANGED
|
@@ -7,7 +7,7 @@ exports.
|
|
|
7
7
|
- Component changes and admission: `docs/contract.md`.
|
|
8
8
|
- Consumer setup: `docs/consuming-cross-repo.md`.
|
|
9
9
|
- Tokens, CSS, motion, and assets: `../design-system/DESIGN.md`.
|
|
10
|
-
- Agent visuals: `docs/agent-visual.md`.
|
|
10
|
+
- Agent visuals: `docs/agent-visual.md`; the Forma avatars: `docs/forma-avatar.md`.
|
|
11
11
|
- System activity indicators: `docs/activity.md`.
|
|
12
12
|
- First run, sign-in, and repair surfaces: `docs/threshold.md`; the
|
|
13
13
|
primitives are `@nadicodeai/ui/threshold`, with
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type CSSProperties } from "react";
|
|
2
|
+
import { type FormaChoice, type FormaColor, type FormaForm, type FormaOptions, type FormaState, type FormaView } from "../internal/forma/model.js";
|
|
3
|
+
type AccessibleAvatar = {
|
|
4
|
+
decorative?: true;
|
|
5
|
+
label?: never;
|
|
6
|
+
} | {
|
|
7
|
+
decorative: false;
|
|
8
|
+
label: string;
|
|
9
|
+
};
|
|
10
|
+
type FormaAvatarProps = FormaOptions & AccessibleAvatar & {
|
|
11
|
+
className?: string;
|
|
12
|
+
style?: CSSProperties;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The Forma avatar: Nadia's approved shell and face, or one of the seven
|
|
16
|
+
* forms in the shell colour the customer chose, with the approved lifecycle
|
|
17
|
+
* gestures. One SVG per avatar; the server renders the state's resting frame
|
|
18
|
+
* and the client rig animates it in place.
|
|
19
|
+
*/
|
|
20
|
+
declare function FormaAvatar({ form, color, state, size, paused, view, motionKey, decorative, label, className, style, }: FormaAvatarProps): import("react").JSX.Element;
|
|
21
|
+
export { FormaAvatar, type FormaAvatarProps, type FormaChoice, type FormaColor, type FormaForm, type FormaState, type FormaView, };
|
|
22
|
+
export { defaultForm, formDetails, formaChoices, formaColorVariable, formaColors, formaForms, formaStates, } from "../internal/forma/model.js";
|
|
23
|
+
//# sourceMappingURL=forma-avatar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forma-avatar.d.ts","sourceRoot":"","sources":["../../src/components/forma-avatar.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAsC,MAAM,OAAO,CAAC;AAW/E,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,SAAS,EAMf,MAAM,wBAAwB,CAAC;AAIhC,KAAK,gBAAgB,GAAG;IAAE,UAAU,CAAC,EAAE,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,UAAU,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpG,KAAK,gBAAgB,GAAG,YAAY,GAClC,gBAAgB,GAAG;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC;AAiBJ;;;;;GAKG;AACH,iBAAS,WAAW,CAAC,EACnB,IAAI,EACJ,KAAK,EACL,KAAc,EACd,IAAS,EACT,MAAc,EACd,IAAc,EACd,SAAS,EACT,UAAiB,EACjB,KAAK,EACL,SAAS,EACT,KAAK,GACN,EAAE,gBAAgB,+BA4blB;AAED,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,SAAS,GACf,CAAC;AACF,OAAO,EACL,WAAW,EACX,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,WAAW,GACZ,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
4
|
+
import { ORBIT_DOTS, ORBIT_TILTS, RIBBON_BEADS, RIBBON_TILTS, SHELL_LAYERS, } from "../internal/forma/frame.js";
|
|
5
|
+
import { bodies, eyeProfiles } from "../internal/forma/geometry.js";
|
|
6
|
+
import { ITALY_PATH } from "../internal/forma/hallmark.js";
|
|
7
|
+
import { celebrationColors, formDetails, formaColorVariable, resolveFormaOptions, shellColor, } from "../internal/forma/model.js";
|
|
8
|
+
import { stillFrame } from "../internal/forma/still.js";
|
|
9
|
+
const WHITE = "var(--nc-identity-white)";
|
|
10
|
+
const INK = "var(--nc-identity-ink)";
|
|
11
|
+
function part(frame, name) {
|
|
12
|
+
const { display, ...attributes } = frame[name] ?? {};
|
|
13
|
+
return {
|
|
14
|
+
"data-part": name,
|
|
15
|
+
...attributes,
|
|
16
|
+
...(display === undefined ? {} : { style: { display } }),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const orbitDotRadius = (i) => (i % 10 === 0 ? 0.036 : 0.022 * (1 - (i % 10) / 13)).toFixed(4);
|
|
20
|
+
/**
|
|
21
|
+
* The Forma avatar: Nadia's approved shell and face, or one of the seven
|
|
22
|
+
* forms in the shell colour the customer chose, with the approved lifecycle
|
|
23
|
+
* gestures. One SVG per avatar; the server renders the state's resting frame
|
|
24
|
+
* and the client rig animates it in place.
|
|
25
|
+
*/
|
|
26
|
+
function FormaAvatar({ form, color, state = "idle", size = 32, paused = false, view = "front", motionKey, decorative = true, label, className, style, }) {
|
|
27
|
+
const options = resolveFormaOptions({ form, color, state, size, paused, view, motionKey });
|
|
28
|
+
const id = useId().replace(/:/g, "");
|
|
29
|
+
const host = useRef(null);
|
|
30
|
+
const handle = useRef(null);
|
|
31
|
+
const latest = useRef(options);
|
|
32
|
+
const [mode, setMode] = useState("still");
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
latest.current = options;
|
|
35
|
+
handle.current?.update(options);
|
|
36
|
+
}, [options]);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const element = host.current;
|
|
39
|
+
if (!element)
|
|
40
|
+
return;
|
|
41
|
+
let generation = 0;
|
|
42
|
+
const preference = typeof matchMedia === "function" ? matchMedia("(prefers-reduced-motion: reduce)") : null;
|
|
43
|
+
const reconcile = () => {
|
|
44
|
+
const token = ++generation;
|
|
45
|
+
handle.current?.dispose();
|
|
46
|
+
handle.current = null;
|
|
47
|
+
setMode("still");
|
|
48
|
+
if (paused || preference?.matches)
|
|
49
|
+
return;
|
|
50
|
+
import("../internal/forma/rig.js").then(({ mountForma }) => {
|
|
51
|
+
if (generation !== token)
|
|
52
|
+
return;
|
|
53
|
+
handle.current = mountForma(element, latest.current, { onModeChange: setMode });
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
reconcile();
|
|
57
|
+
preference?.addEventListener("change", reconcile);
|
|
58
|
+
return () => {
|
|
59
|
+
generation++;
|
|
60
|
+
preference?.removeEventListener("change", reconcile);
|
|
61
|
+
handle.current?.dispose();
|
|
62
|
+
handle.current = null;
|
|
63
|
+
};
|
|
64
|
+
}, [paused]);
|
|
65
|
+
const detail = formDetails[options.form];
|
|
66
|
+
const nadia = options.form === "nadia";
|
|
67
|
+
const shade = shellColor(options);
|
|
68
|
+
const pigment = `var(${formaColorVariable[shade]})`;
|
|
69
|
+
const visorFill = detail.visor === "ivory" ? "var(--nc-forma-avorio)" : "var(--nc-forma-visor)";
|
|
70
|
+
const bezelStroke = detail.visor === "ivory" ? "var(--nc-seam)" : "var(--nc-forma-bezel)";
|
|
71
|
+
const eye = nadia ? "var(--nc-forma-eye)" : detail.eyes === "ink" ? INK : WHITE;
|
|
72
|
+
const frame = stillFrame(options);
|
|
73
|
+
const signature = eyeProfiles[options.form].signature === true;
|
|
74
|
+
const { width: w, height: h } = bodies[options.form];
|
|
75
|
+
const shadow = nadia ? INK : pigment;
|
|
76
|
+
const shading = { mixBlendMode: "multiply" };
|
|
77
|
+
const body = `scale(${w} ${h})`;
|
|
78
|
+
const url = (name) => `url(#${name}-${id})`;
|
|
79
|
+
const renderEye = (name) => (_jsxs("g", { ...part(frame, `eye${name}`), children: [_jsx("ellipse", { cx: "0", cy: "0", fill: url("halo"), ...part(frame, `halo${name}`) }), _jsx("path", { fill: eye, ...part(frame, `light${name}`) }), _jsx("path", { fill: url("glint"), ...part(frame, `glint${name}`) }), signature ? (_jsx("path", { d: "M0.045 0.103 Q0.092 0.102 0.112 0.147", fill: "none", stroke: eye, strokeLinecap: "round", strokeWidth: "0.021", ...part(frame, `flick${name}`) })) : null, _jsx("path", { d: "M-0.084 -0.012 Q0 0.082 0.084 -0.012", fill: "none", stroke: eye, strokeLinecap: "round", strokeWidth: "0.036", ...part(frame, `happy${name}`) }), _jsx("ellipse", { cx: "0", cy: "0", fill: url("pauseHalo"), rx: "0.17", ry: "0.32", ...part(frame, `pauseHalo${name}`) }), _jsx("rect", { fill: "var(--nc-identity-giallo)", height: "0.388", rx: "0.044", width: "0.088", x: "-0.044", y: "-0.194", ...part(frame, `pause${name}`) })] }));
|
|
80
|
+
const renderOrbitDots = (layer) => ORBIT_TILTS.map((_, index) => Array.from({ length: ORBIT_DOTS }, (__, i) => (_jsx("circle", { fill: pigment, r: orbitDotRadius(i), ...part(frame, `orbit${index}${layer}${i}`) }, `${index}-${i}`))));
|
|
81
|
+
return (_jsx("span", { "aria-hidden": decorative || undefined, "aria-label": decorative ? undefined : label, className: className, "data-color": options.color ?? "default", "data-forma-avatar": options.form, "data-state": options.state, role: decorative ? undefined : "img", style: {
|
|
82
|
+
display: "inline-block",
|
|
83
|
+
position: "relative",
|
|
84
|
+
flexShrink: 0,
|
|
85
|
+
verticalAlign: "middle",
|
|
86
|
+
...style,
|
|
87
|
+
width: options.size,
|
|
88
|
+
height: options.size,
|
|
89
|
+
}, children: _jsxs("svg", { "aria-hidden": "true", "data-render-mode": mode, height: options.size, overflow: "visible", ref: host, style: { display: "block", overflow: "visible" }, viewBox: "-1.55 -1.55 3.1 3.1", width: options.size, children: [_jsxs("defs", { children: [_jsxs("radialGradient", { cx: "-0.18", cy: "0.3", gradientTransform: body, gradientUnits: "userSpaceOnUse", id: `tone-${id}`, r: "1.34", children: [_jsx("stop", { offset: "0", stopColor: shadow, stopOpacity: nadia ? "0.04" : "0.4" }), _jsx("stop", { offset: "1", stopColor: shadow, stopOpacity: nadia ? "0.14" : "0.95" })] }), _jsxs("radialGradient", { cx: "-0.18", cy: "0.3", gradientTransform: body, gradientUnits: "userSpaceOnUse", id: `form-${id}`, r: "1.34", children: [_jsx("stop", { offset: "0", stopColor: INK, stopOpacity: "0.02" }), _jsx("stop", { offset: "0.5", stopColor: INK, stopOpacity: "0.1" }), _jsx("stop", { offset: "0.8", stopColor: INK, stopOpacity: "0.26" }), _jsx("stop", { offset: "1", stopColor: INK, stopOpacity: "0.52" })] }), _jsxs("linearGradient", { gradientTransform: body, gradientUnits: "userSpaceOnUse", id: `floor-${id}`, x1: "0", x2: "0", y1: "1", y2: "-1", children: [_jsx("stop", { offset: "0.45", stopColor: INK, stopOpacity: "0" }), _jsx("stop", { offset: "1", stopColor: INK, stopOpacity: "0.42" })] }), _jsxs("radialGradient", { cx: "0.05", cy: "3.6", gradientTransform: `scale(${w} ${h * 0.3})`, gradientUnits: "userSpaceOnUse", id: `key-${id}`, r: "1.05", children: [_jsx("stop", { offset: "0", stopColor: WHITE, stopOpacity: "0.62" }), _jsx("stop", { offset: "0.4", stopColor: WHITE, stopOpacity: "0.22" }), _jsx("stop", { offset: "1", stopColor: WHITE, stopOpacity: "0" })] }), _jsxs("radialGradient", { cx: "0.5", cy: "0.5", id: `spec-${id}`, r: "0.5", children: [_jsx("stop", { offset: "0", stopColor: WHITE, stopOpacity: "0.85" }), _jsx("stop", { offset: "0.5", stopColor: WHITE, stopOpacity: "0.25" }), _jsx("stop", { offset: "1", stopColor: WHITE, stopOpacity: "0" })] }), _jsxs("radialGradient", { cx: "-0.16", cy: "-0.12", gradientTransform: body, gradientUnits: "userSpaceOnUse", id: `rim-${id}`, r: "1.2", children: [_jsx("stop", { offset: "0.7", stopColor: WHITE, stopOpacity: "0" }), _jsx("stop", { offset: "0.9", stopColor: WHITE, stopOpacity: "0.26" }), _jsx("stop", { offset: "0.975", stopColor: WHITE, stopOpacity: "0.5" }), _jsx("stop", { offset: "1", stopColor: WHITE, stopOpacity: "0.1" })] }), _jsxs("radialGradient", { cx: "0", cy: "0", gradientUnits: "userSpaceOnUse", id: `tube-${id}`, r: "1", children: [_jsx("stop", { offset: "0.64", stopColor: INK, stopOpacity: "0.45" }), _jsx("stop", { offset: "0.7", stopColor: INK, stopOpacity: "0.1" }), _jsx("stop", { offset: "0.76", stopColor: WHITE, stopOpacity: "0" }), _jsx("stop", { offset: "0.82", stopColor: WHITE, stopOpacity: "0.36" }), _jsx("stop", { offset: "0.89", stopColor: WHITE, stopOpacity: "0" }), _jsx("stop", { offset: "0.95", stopColor: INK, stopOpacity: "0" }), _jsx("stop", { offset: "1", stopColor: INK, stopOpacity: "0.45" })] }), _jsxs("radialGradient", { cx: "0.3", cy: "1", gradientTransform: "translate(0.3 1) scale(1 0.6) translate(-0.3 -1)", id: `glass-${id}`, r: "0.7", children: [_jsx("stop", { offset: "0", stopColor: WHITE, stopOpacity: "0.36" }), _jsx("stop", { offset: "0.5", stopColor: WHITE, stopOpacity: "0.14" }), _jsx("stop", { offset: "1", stopColor: WHITE, stopOpacity: "0" })] }), _jsxs("radialGradient", { cx: "0.2", cy: "0.84", gradientTransform: "translate(0.2 0.84) scale(1 0.5) translate(-0.2 -0.84)", id: `streak-${id}`, r: "0.24", children: [_jsx("stop", { offset: "0", stopColor: WHITE, stopOpacity: "0.9" }), _jsx("stop", { offset: "0.62", stopColor: WHITE, stopOpacity: "0.72" }), _jsx("stop", { offset: "1", stopColor: WHITE, stopOpacity: "0" })] }), _jsxs("radialGradient", { cx: "0.98", cy: "0.58", gradientTransform: "translate(0.98 0.58) scale(0.3 1) translate(-0.98 -0.58)", id: `sole-${id}`, r: "0.42", children: [_jsx("stop", { offset: "0", stopColor: WHITE, stopOpacity: "0.28" }), _jsx("stop", { offset: "1", stopColor: WHITE, stopOpacity: "0" })] }), _jsxs("linearGradient", { id: `visorShade-${id}`, x1: "0", x2: "0", y1: "1", y2: "0", children: [_jsx("stop", { offset: "0.45", stopColor: INK, stopOpacity: "0" }), _jsx("stop", { offset: "1", stopColor: INK, stopOpacity: "0.22" })] }), _jsxs("linearGradient", { id: `bezelLight-${id}`, x1: "0", x2: "1", y1: "1", y2: "0", children: [_jsx("stop", { offset: "0", stopColor: WHITE, stopOpacity: "0.5" }), _jsx("stop", { offset: "0.35", stopColor: WHITE, stopOpacity: "0" }), _jsx("stop", { offset: "1", stopColor: INK, stopOpacity: "0.25" })] }), _jsxs("radialGradient", { cx: "0.5", cy: "0.5", id: `halo-${id}`, r: "0.5", children: [_jsx("stop", { offset: "0", stopColor: eye, stopOpacity: "0.55" }), _jsx("stop", { offset: "0.45", stopColor: eye, stopOpacity: "0.18" }), _jsx("stop", { offset: "1", stopColor: eye, stopOpacity: "0" })] }), _jsxs("radialGradient", { cx: "0.5", cy: "0.62", id: `glint-${id}`, r: "0.55", children: [_jsx("stop", { offset: "0", stopColor: WHITE, stopOpacity: "0.6" }), _jsx("stop", { offset: "1", stopColor: WHITE, stopOpacity: "0" })] }), _jsxs("radialGradient", { cx: "0.5", cy: "0.5", id: `pauseHalo-${id}`, r: "0.5", children: [_jsx("stop", { offset: "0", stopColor: "var(--nc-identity-giallo)", stopOpacity: "0.5" }), _jsx("stop", { offset: "0.5", stopColor: "var(--nc-identity-giallo)", stopOpacity: "0.14" }), _jsx("stop", { offset: "1", stopColor: "var(--nc-identity-giallo)", stopOpacity: "0" })] }), _jsxs("linearGradient", { id: `bar-${id}`, x1: "0", x2: "1", y1: "0", y2: "0", children: [_jsx("stop", { offset: "0", stopColor: WHITE, stopOpacity: "0.55" }), _jsx("stop", { offset: "0.4", stopColor: WHITE, stopOpacity: "0.08" }), _jsx("stop", { offset: "0.7", stopColor: INK, stopOpacity: "0.06" }), _jsx("stop", { offset: "1", stopColor: INK, stopOpacity: "0.4" })] }), _jsxs("radialGradient", { cx: "0.36", cy: "0.68", id: `bead-${id}`, r: "0.72", children: [_jsx("stop", { offset: "0", stopColor: WHITE, stopOpacity: "0.65" }), _jsx("stop", { offset: "0.45", stopColor: WHITE, stopOpacity: "0" }), _jsx("stop", { offset: "0.78", stopColor: INK, stopOpacity: "0" }), _jsx("stop", { offset: "1", stopColor: INK, stopOpacity: "0.45" })] }), _jsx("clipPath", { id: `shell-${id}`, children: _jsx("path", { clipRule: "evenodd", ...part(frame, "shellClip") }) }), _jsx("clipPath", { id: `visor-${id}`, children: _jsx("path", { ...part(frame, "visorClip") }) })] }), _jsxs("g", { "data-part": "root", transform: "scale(1 -1)", children: [_jsx("g", { ...part(frame, "effectsBack"), children: renderOrbitDots("Back") }), _jsxs("g", { ...part(frame, "object"), children: [_jsxs("g", { clipPath: url("shell"), children: [_jsx("path", { fill: pigment, fillRule: "evenodd", ...part(frame, "shell") }), nadia ? (_jsxs(_Fragment, { children: [_jsx("path", { fill: "var(--nc-forma-avorio)", ...part(frame, "bandW") }), _jsx("path", { fill: "var(--nc-identity-rosso)", ...part(frame, "bandR") }), _jsx("path", { fill: "var(--nc-forma-sabbia)", ...part(frame, "bandSand") }), _jsx("g", { fill: "none", opacity: "0.82", stroke: "var(--nc-forma-avorio)", strokeLinecap: "round", strokeLinejoin: "round", strokeOpacity: "0.72", strokeWidth: "12", ...part(frame, "hallmark"), children: _jsx("path", { d: ITALY_PATH }) })] })) : null, SHELL_LAYERS.map((layer) => (_jsx("path", { fill: url(layer), fillRule: "evenodd", style: layer === "tone" ? shading : undefined, ...part(frame, layer) }, layer))), _jsx("ellipse", { cx: -0.4 * w, cy: 0.6 * h, fill: url("spec"), rx: 0.27 * w, ry: 0.11 * h, transform: `rotate(-24 ${-0.4 * w} ${0.6 * h})`, ...part(frame, "hot") }), _jsx("path", { fill: url("tube"), fillRule: "evenodd", ...part(frame, "tube") }), _jsx("path", { fill: "var(--nc-forma-sabbia)", fillRule: "evenodd", ...part(frame, "fade") }), _jsxs("g", { ...part(frame, "face"), children: [_jsx("path", { fill: "none", stroke: bezelStroke, strokeWidth: "0.014", ...part(frame, "bezel") }), _jsx("path", { fill: "none", stroke: url("bezelLight"), strokeWidth: "0.014", ...part(frame, "bezelLight") }), _jsx("path", { fill: visorFill, ...part(frame, "visor") }), _jsxs("g", { clipPath: url("visor"), children: [_jsx("path", { fill: url("visorShade"), ...part(frame, "visorShade") }), _jsx("path", { fill: url("glass"), ...part(frame, "glass") }), _jsx("path", { fill: url("streak"), ...part(frame, "streak") }), _jsx("path", { fill: url("sole"), ...part(frame, "sole") }), _jsx("path", { fill: "none", stroke: WHITE, strokeOpacity: "0.05", strokeWidth: "0.04", ...part(frame, "visorEdge") })] }), renderEye("L"), renderEye("R"), [0, 1, 2].map((i) => (_jsx("rect", { fill: eye, height: "0.024", rx: "0.012", width: "0.047", ...part(frame, `tick${i}`) }, i)))] })] }), _jsxs("g", { ...part(frame, "blockedBar"), children: [_jsx("rect", { fill: nadia ? "var(--nc-forma-avorio)" : pigment, height: "1.664", rx: "0.112", transform: "rotate(45)", width: "0.224", x: "-0.112", y: "-0.832" }), nadia ? null : (_jsx("rect", { fill: "var(--nc-forma-sabbia)", height: "1.664", opacity: "0.28", rx: "0.112", transform: "rotate(45)", width: "0.224", x: "-0.112", y: "-0.832" })), _jsx("rect", { fill: url("bar"), height: "1.664", rx: "0.112", transform: "rotate(45)", width: "0.224", x: "-0.112", y: "-0.832" })] })] }), _jsxs("g", { ...part(frame, "effectsFront"), children: [renderOrbitDots("Front"), [0, 1, 2].map((i) => (_jsxs("g", { ...part(frame, `thought${i}`), children: [_jsx("circle", { fill: pigment, r: (0.042 + i * 0.015).toFixed(3) }), _jsx("circle", { fill: url("bead"), r: (0.042 + i * 0.015).toFixed(3) })] }, i))), RIBBON_TILTS.map((tilt, index) => {
|
|
90
|
+
const ribbon = `var(${formaColorVariable[celebrationColors[shade][index]]})`;
|
|
91
|
+
return (_jsx("g", { transform: `rotate(${tilt})`, ...part(frame, `ribbon${index}Plane`), children: _jsxs("g", { ...part(frame, `ribbon${index}Sweep`), children: [_jsx("path", { fill: "none", stroke: ribbon, strokeLinecap: "round", strokeWidth: "0.024", ...part(frame, `ribbon${index}Arc`) }), _jsx("circle", { fill: ribbon, r: "0.03", ...part(frame, `ribbon${index}Head`) }), Array.from({ length: RIBBON_BEADS }, (_, i) => (_jsx("circle", { fill: ribbon, ...part(frame, `ribbon${index}Bead${i}`) }, i)))] }) }, index));
|
|
92
|
+
})] })] })] }) }));
|
|
93
|
+
}
|
|
94
|
+
export { FormaAvatar, };
|
|
95
|
+
export { defaultForm, formDetails, formaChoices, formaColorVariable, formaColors, formaForms, formaStates, } from "../internal/forma/model.js";
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
export interface ConfirmActionGate {
|
|
2
|
-
busy: boolean;
|
|
3
|
-
}
|
|
4
|
-
export declare function requestConfirmAction<T>(gate: ConfirmActionGate, value: T, setPending: (value: T | null) => void): void;
|
|
5
|
-
export declare function cancelConfirmAction<T>(gate: ConfirmActionGate, setPending: (value: T | null) => void): void;
|
|
6
|
-
export declare function runConfirmAction<T>(gate: ConfirmActionGate, pending: T | null, onConfirm: (value: T) => void | Promise<void>, setPending: (value: T | null) => void, setBusy: (busy: boolean) => void): Promise<void>;
|
|
7
1
|
export declare function useConfirmAction<T>({ onConfirm, }: {
|
|
8
2
|
onConfirm: (value: T) => void | Promise<void>;
|
|
9
3
|
}): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-confirm-action.d.ts","sourceRoot":"","sources":["../../src/hooks/use-confirm-action.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-confirm-action.d.ts","sourceRoot":"","sources":["../../src/hooks/use-confirm-action.ts"],"names":[],"mappings":"AAwDA,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,EAClC,SAAS,GACV,EAAE;IACD,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;8BAK2C,CAAC;;;;;;EAqB5C"}
|
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
|
|
4
|
-
// flight so the pending value cannot change under a running action.
|
|
5
|
-
export function requestConfirmAction(gate, value, setPending) {
|
|
3
|
+
function requestConfirmAction(gate, value, setPending) {
|
|
6
4
|
if (gate.busy) {
|
|
7
5
|
return;
|
|
8
6
|
}
|
|
9
7
|
setPending(value);
|
|
10
8
|
}
|
|
11
|
-
|
|
12
|
-
// so the dialog cannot be dismissed mid-action.
|
|
13
|
-
export function cancelConfirmAction(gate, setPending) {
|
|
9
|
+
function cancelConfirmAction(gate, setPending) {
|
|
14
10
|
if (gate.busy) {
|
|
15
11
|
return;
|
|
16
12
|
}
|
|
17
13
|
setPending(null);
|
|
18
14
|
}
|
|
19
|
-
|
|
20
|
-
// success. A thrown error is swallowed so the dialog stays open (the caller
|
|
21
|
-
// surfaces the error inside `onConfirm`); busy always resets in `finally`.
|
|
22
|
-
export async function runConfirmAction(gate, pending, onConfirm, setPending, setBusy) {
|
|
15
|
+
async function confirmOrKeepOpen(gate, pending, onConfirm, setPending, setBusy) {
|
|
23
16
|
if (gate.busy || pending === null) {
|
|
24
17
|
return;
|
|
25
18
|
}
|
|
@@ -30,7 +23,7 @@ export async function runConfirmAction(gate, pending, onConfirm, setPending, set
|
|
|
30
23
|
setPending(null);
|
|
31
24
|
}
|
|
32
25
|
catch {
|
|
33
|
-
|
|
26
|
+
return;
|
|
34
27
|
}
|
|
35
28
|
finally {
|
|
36
29
|
gate.busy = false;
|
|
@@ -47,7 +40,7 @@ export function useConfirmAction({ onConfirm, }) {
|
|
|
47
40
|
const cancel = React.useCallback(() => {
|
|
48
41
|
cancelConfirmAction(gateRef.current, setPending);
|
|
49
42
|
}, []);
|
|
50
|
-
const confirm = React.useCallback(() =>
|
|
43
|
+
const confirm = React.useCallback(() => confirmOrKeepOpen(gateRef.current, pending, onConfirm, setPending, setIsBusy), [pending, onConfirm]);
|
|
51
44
|
return {
|
|
52
45
|
request,
|
|
53
46
|
cancel,
|
package/dist/index.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ export * from "./components/field.js";
|
|
|
60
60
|
export * from "./components/file-type-icon.js";
|
|
61
61
|
export * from "./components/footer.js";
|
|
62
62
|
export * from "./components/form.js";
|
|
63
|
+
export * from "./components/forma-avatar.js";
|
|
63
64
|
export * from "./components/glyph-spinner.js";
|
|
64
65
|
export * from "./components/glass-control.js";
|
|
65
66
|
export * from "./components/handoff-banner.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -60,6 +60,7 @@ export * from "./components/field.js";
|
|
|
60
60
|
export * from "./components/file-type-icon.js";
|
|
61
61
|
export * from "./components/footer.js";
|
|
62
62
|
export * from "./components/form.js";
|
|
63
|
+
export * from "./components/forma-avatar.js";
|
|
63
64
|
export * from "./components/glyph-spinner.js";
|
|
64
65
|
export * from "./components/glass-control.js";
|
|
65
66
|
export * from "./components/handoff-banner.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type FormaMotion, type Pose } from "./motion.js";
|
|
2
|
+
export type PartAttributes = Record<string, string>;
|
|
3
|
+
export type Frame = Record<string, PartAttributes>;
|
|
4
|
+
export declare const SHELL_LAYERS: readonly ["tone", "form", "floor", "key", "rim"];
|
|
5
|
+
export declare const ORBIT_DOTS = 20;
|
|
6
|
+
export declare const RIBBON_BEADS = 8;
|
|
7
|
+
export declare function computeFrame(motion: FormaMotion, pose: Pose, now: number): Frame;
|
|
8
|
+
export declare const RIBBON_TILTS: (48 | -22 | -64)[];
|
|
9
|
+
export declare const ORBIT_TILTS: readonly [-0.36, 0.49];
|
|
10
|
+
//# sourceMappingURL=frame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../../src/internal/forma/frame.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAsB,KAAK,WAAW,EAAE,KAAK,IAAI,EAAE,MAAM,UAAU,CAAC;AAE3E,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACpD,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAanD,eAAO,MAAM,YAAY,kDAAmD,CAAC;AAY7E,eAAO,MAAM,UAAU,KAAK,CAAC;AAC7B,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,CAyGhF;AAOD,eAAO,MAAM,YAAY,oBAAuC,CAAC;AACjE,eAAO,MAAM,WAAW,wBAAyB,CAAC"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { TAU, bodies, closedPath, eyeProfiles, f3, project, shellPath, smooth, stripePath, superellipse, visorPoints, } from "./geometry.js";
|
|
2
|
+
import { HALLMARK_LONGITUDE, HALLMARK_SIZE, ITALY_BOX } from "./hallmark.js";
|
|
3
|
+
import { formDetails } from "./model.js";
|
|
4
|
+
import { easeInOut, easeOut } from "./motion.js";
|
|
5
|
+
const eyeShapes = new Map();
|
|
6
|
+
function eyeShape(form) {
|
|
7
|
+
let shape = eyeShapes.get(form);
|
|
8
|
+
if (!shape) {
|
|
9
|
+
const profile = eyeProfiles[form];
|
|
10
|
+
shape = closedPath(superellipse(profile.width, profile.height, profile.power, 32));
|
|
11
|
+
eyeShapes.set(form, shape);
|
|
12
|
+
}
|
|
13
|
+
return shape;
|
|
14
|
+
}
|
|
15
|
+
export const SHELL_LAYERS = ["tone", "form", "floor", "key", "rim"];
|
|
16
|
+
const VISOR_LAYERS = [
|
|
17
|
+
"visor",
|
|
18
|
+
"visorClip",
|
|
19
|
+
"bezel",
|
|
20
|
+
"bezelLight",
|
|
21
|
+
"visorShade",
|
|
22
|
+
"glass",
|
|
23
|
+
"streak",
|
|
24
|
+
"sole",
|
|
25
|
+
"visorEdge",
|
|
26
|
+
];
|
|
27
|
+
export const ORBIT_DOTS = 20;
|
|
28
|
+
export const RIBBON_BEADS = 8;
|
|
29
|
+
export function computeFrame(motion, pose, now) {
|
|
30
|
+
const body = bodies[motion.form];
|
|
31
|
+
const nadia = motion.form === "nadia";
|
|
32
|
+
const t = (now - motion.entered) / 1000;
|
|
33
|
+
const morph = pose.morph < 0.00005 ? 0 : pose.morph > 0.99995 ? 1 : pose.morph;
|
|
34
|
+
const { yaw, pitch } = pose;
|
|
35
|
+
const frame = {};
|
|
36
|
+
const shell = shellPath(body, morph);
|
|
37
|
+
frame.shell = { d: shell };
|
|
38
|
+
frame.shellClip = { d: shell };
|
|
39
|
+
for (const layer of SHELL_LAYERS)
|
|
40
|
+
frame[layer] = { d: shell };
|
|
41
|
+
frame.tube = { d: shell, opacity: f3(morph) };
|
|
42
|
+
frame.hot = { opacity: f3(1 - morph * 0.6) };
|
|
43
|
+
frame.fade = { d: shell, opacity: f3(nadia ? 0 : morph * 0.28) };
|
|
44
|
+
frame.object = {
|
|
45
|
+
transform: `translate(0 ${f3(pose.lift)}) rotate(${f3((pose.roll * 180) / Math.PI)}) scale(${f3(1 / Math.sqrt(pose.stretch))} ${f3(pose.stretch)})`,
|
|
46
|
+
};
|
|
47
|
+
frame.blockedBar = { opacity: f3(morph > 0.42 ? smooth((morph - 0.42) / 0.4) : 0) };
|
|
48
|
+
if (nadia) {
|
|
49
|
+
frame.bandW = { d: stripePath(body, yaw, -0.36, 0.36) };
|
|
50
|
+
frame.bandR = { d: stripePath(body, yaw, 0.36, Math.PI) };
|
|
51
|
+
frame.bandSand = { d: frame.bandW.d, opacity: f3(morph * 0.28) };
|
|
52
|
+
}
|
|
53
|
+
const faceOpacity = 1 - smooth(morph / 0.52);
|
|
54
|
+
frame.face = { opacity: f3(faceOpacity), display: faceOpacity > 0.001 ? "" : "none" };
|
|
55
|
+
const visor = closedPath(visorPoints.map(([x, y]) => {
|
|
56
|
+
const p = project(body, x, y, yaw, pitch);
|
|
57
|
+
return [p.x, p.y];
|
|
58
|
+
}));
|
|
59
|
+
for (const layer of VISOR_LAYERS)
|
|
60
|
+
frame[layer] = { d: visor };
|
|
61
|
+
if (nadia) {
|
|
62
|
+
const hm = project(body, Math.sin(HALLMARK_LONGITUDE), -0.12, yaw, pitch);
|
|
63
|
+
const scale = HALLMARK_SIZE / ITALY_BOX;
|
|
64
|
+
frame.hallmark = {
|
|
65
|
+
display: hm.front && faceOpacity > 0.001 ? "" : "none",
|
|
66
|
+
transform: `translate(${f3(hm.x)} ${f3(hm.y)}) scale(${f3(hm.sx * scale)} ${f3(-hm.sy * scale)}) translate(${-ITALY_BOX / 2} ${-ITALY_BOX / 2})`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const happiness = smooth(pose.done);
|
|
70
|
+
const pauseAmount = smooth((pose.waiting - 0.15) / 0.85);
|
|
71
|
+
const blinkPhase = ((t % 5.2) - 3.4) / 0.16;
|
|
72
|
+
const blink = !motion.still &&
|
|
73
|
+
motion.state !== "blocked" &&
|
|
74
|
+
motion.state !== "waiting" &&
|
|
75
|
+
blinkPhase > 0 &&
|
|
76
|
+
blinkPhase < 1
|
|
77
|
+
? 1 - Math.sin(blinkPhase * Math.PI)
|
|
78
|
+
: 1;
|
|
79
|
+
const eyeOpen = Math.max(0.055, pose.open * blink);
|
|
80
|
+
const profile = eyeProfiles[motion.form];
|
|
81
|
+
const glow = formDetails[motion.form].eyes === "ink" ? 0 : profile.signature ? 0.95 : 0.65;
|
|
82
|
+
const shape = eyeShape(motion.form);
|
|
83
|
+
for (const [name, side] of [
|
|
84
|
+
["L", -1],
|
|
85
|
+
["R", 1],
|
|
86
|
+
]) {
|
|
87
|
+
const x = side * ((1 - pauseAmount) * profile.spacing + pauseAmount * 0.125) + pose.gazeX;
|
|
88
|
+
const y = profile.y + pose.gazeY;
|
|
89
|
+
const p = project(body, x, y, yaw, pitch);
|
|
90
|
+
frame[`eye${name}`] = {
|
|
91
|
+
display: p.front ? "" : "none",
|
|
92
|
+
transform: `translate(${f3(p.x)} ${f3(p.y)}) scale(${f3(p.sx)} ${f3(p.sy)})`,
|
|
93
|
+
};
|
|
94
|
+
frame[`light${name}`] = {
|
|
95
|
+
d: shape,
|
|
96
|
+
transform: `scale(1 ${f3(eyeOpen)})`,
|
|
97
|
+
opacity: f3((1 - happiness) * (1 - pauseAmount)),
|
|
98
|
+
};
|
|
99
|
+
frame[`glint${name}`] = frame[`light${name}`];
|
|
100
|
+
frame[`halo${name}`] = {
|
|
101
|
+
rx: f3(profile.width * 2.8),
|
|
102
|
+
ry: f3(profile.height * (1.1 + 0.9 * eyeOpen)),
|
|
103
|
+
opacity: f3(glow * (1 - pauseAmount) * (1 - happiness)),
|
|
104
|
+
};
|
|
105
|
+
if (profile.signature)
|
|
106
|
+
frame[`flick${name}`] = {
|
|
107
|
+
transform: `scale(${side} ${f3(eyeOpen)})`,
|
|
108
|
+
opacity: f3((1 - happiness) * (1 - pauseAmount)),
|
|
109
|
+
};
|
|
110
|
+
frame[`happy${name}`] = {
|
|
111
|
+
opacity: f3(happiness * (1 - pauseAmount)),
|
|
112
|
+
transform: `rotate(${f3(side * pose.done * 1.7)})`,
|
|
113
|
+
};
|
|
114
|
+
frame[`pause${name}`] = { opacity: f3(pauseAmount) };
|
|
115
|
+
frame[`pauseHalo${name}`] = { opacity: f3(pauseAmount * 0.7) };
|
|
116
|
+
}
|
|
117
|
+
for (let i = 0; i < 3; i++) {
|
|
118
|
+
const pulse = motion.still ? 0.6 : 0.18 + (0.82 * (Math.sin(t * 5.6 - i * 1.25) + 1)) / 2;
|
|
119
|
+
const p = project(body, (i - 1) * 0.086, -0.266, yaw, pitch);
|
|
120
|
+
frame[`tick${i}`] = {
|
|
121
|
+
transform: `translate(${f3(p.x - 0.0235 * p.sx)} ${f3(p.y - 0.012)}) scale(${f3(p.sx)} 1)`,
|
|
122
|
+
opacity: f3(pose.working * pulse * 0.75 + pose.thinking * pulse * 0.35),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
writeEffects(frame, motion, pose, now);
|
|
126
|
+
return frame;
|
|
127
|
+
}
|
|
128
|
+
const RIBBONS = [
|
|
129
|
+
{ tilt: -22, aspect: 0.62, radius: 1.2, span: 2.85, phase: 0.2, direction: 1 },
|
|
130
|
+
{ tilt: 48, aspect: 0.5, radius: 1.19, span: 2.5, phase: 2.1, direction: -1 },
|
|
131
|
+
{ tilt: -64, aspect: 0.72, radius: 1.24, span: 2.3, phase: 4.0, direction: 1 },
|
|
132
|
+
];
|
|
133
|
+
export const RIBBON_TILTS = RIBBONS.map((ribbon) => ribbon.tilt);
|
|
134
|
+
export const ORBIT_TILTS = [-0.36, 0.49];
|
|
135
|
+
function writeEffects(frame, motion, pose, now) {
|
|
136
|
+
const morph = pose.morph < 0.00005 ? 0 : pose.morph > 0.99995 ? 1 : pose.morph;
|
|
137
|
+
const clock = motion.still ? 0.6 : (now - motion.started) / 1000;
|
|
138
|
+
const celebrationAge = (now - motion.celebratedAt) / 1000;
|
|
139
|
+
const celebrating = !motion.still && celebrationAge > 0.14 && celebrationAge < 2.4;
|
|
140
|
+
const workAmount = pose.working * (1 - morph);
|
|
141
|
+
const thoughtAmount = pose.thinking * (1 - morph);
|
|
142
|
+
const ribbonOpacity = celebrating ? pose.done * (1 - morph) : 0;
|
|
143
|
+
const active = workAmount > 0.003 || thoughtAmount > 0.003 || ribbonOpacity > 0.003;
|
|
144
|
+
frame.effectsBack = { display: active ? "" : "none" };
|
|
145
|
+
frame.effectsFront = {
|
|
146
|
+
display: active ? "" : "none",
|
|
147
|
+
transform: `translate(0 ${f3(pose.lift * 0.45)})`,
|
|
148
|
+
};
|
|
149
|
+
ORBIT_TILTS.forEach((tilt, index) => {
|
|
150
|
+
for (let i = 0; i < ORBIT_DOTS; i++) {
|
|
151
|
+
const tail = i % 10;
|
|
152
|
+
const offset = i < 10 ? 0 : Math.PI;
|
|
153
|
+
const a = clock * (index === 0 ? 2.8 : -2.2) + index * 2.2 + offset - tail * 0.065;
|
|
154
|
+
const x = 1.2 * Math.cos(a);
|
|
155
|
+
const y = 0.49 * Math.sin(a);
|
|
156
|
+
const front = Math.sin(a) > 0;
|
|
157
|
+
const dot = {
|
|
158
|
+
cx: f3(x * Math.cos(tilt) - y * Math.sin(tilt)),
|
|
159
|
+
cy: f3(x * Math.sin(tilt) + y * Math.cos(tilt) - 0.04),
|
|
160
|
+
opacity: f3(workAmount * (tail === 0 ? 0.94 : 0.47 * (1 - tail / 10))),
|
|
161
|
+
};
|
|
162
|
+
frame[`orbit${index}Back${i}`] = { ...dot, display: front ? "none" : "" };
|
|
163
|
+
frame[`orbit${index}Front${i}`] = { ...dot, display: front ? "" : "none" };
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
for (let i = 0; i < 3; i++) {
|
|
167
|
+
const pulse = motion.still ? 0.72 : 0.23 + (0.77 * (Math.sin(clock * 2.4 - i * 0.9) + 1)) / 2;
|
|
168
|
+
frame[`thought${i}`] = {
|
|
169
|
+
transform: `translate(${f3(0.68 + i * 0.23)} ${f3(0.79 + i * 0.13 + (motion.still ? 0 : 0.025 * Math.sin(clock * 2.4 - i * 0.9)))})`,
|
|
170
|
+
opacity: f3(thoughtAmount * pulse),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
RIBBONS.forEach((ribbon, index) => {
|
|
174
|
+
const name = `ribbon${index}`;
|
|
175
|
+
if (ribbonOpacity <= 0.003) {
|
|
176
|
+
frame[`${name}Plane`] = { opacity: "0", display: "none" };
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const age = celebrationAge - 0.14 - index * 0.06;
|
|
180
|
+
const reveal = easeOut(age / 0.46);
|
|
181
|
+
const dissolve = easeInOut((age - 1.1) / 0.95);
|
|
182
|
+
const fade = easeOut(age / 0.2) * (1 - easeOut((age - 1.76) / 0.44)) * ribbonOpacity;
|
|
183
|
+
const a0 = dissolve * ribbon.span;
|
|
184
|
+
const a1 = Math.max(a0, reveal * ribbon.span);
|
|
185
|
+
const points = [];
|
|
186
|
+
for (let i = 0; i <= 24; i++) {
|
|
187
|
+
const a = a0 + ((a1 - a0) * i) / 24;
|
|
188
|
+
points.push(`${i ? "L" : "M"}${f3(Math.cos(a) * ribbon.radius)} ${f3(Math.sin(a) * ribbon.radius * ribbon.aspect)}`);
|
|
189
|
+
}
|
|
190
|
+
frame[`${name}Plane`] = { opacity: f3(fade * 0.88), display: "" };
|
|
191
|
+
frame[`${name}Sweep`] = {
|
|
192
|
+
transform: `rotate(${f3(((ribbon.phase + ribbon.direction * TAU * easeInOut(Math.min(1, Math.max(0, age / 2.12)))) * 180) / Math.PI)})`,
|
|
193
|
+
};
|
|
194
|
+
frame[`${name}Arc`] = { d: a1 > a0 + 0.01 ? points.join("") : "M0 0" };
|
|
195
|
+
frame[`${name}Head`] = {
|
|
196
|
+
cx: f3(Math.cos(a1) * ribbon.radius),
|
|
197
|
+
cy: f3(Math.sin(a1) * ribbon.radius * ribbon.aspect),
|
|
198
|
+
};
|
|
199
|
+
for (let i = 0; i < RIBBON_BEADS; i++) {
|
|
200
|
+
const u = i / 7;
|
|
201
|
+
const angle = a0 - u * (0.95 + 0.48 * dissolve);
|
|
202
|
+
const radial = ribbon.radius + ((i % 3) - 1) * (0.014 + 0.034 * u) + dissolve * u * 0.06;
|
|
203
|
+
frame[`${name}Bead${i}`] = {
|
|
204
|
+
cx: f3(Math.cos(angle) * radial),
|
|
205
|
+
cy: f3(Math.sin(angle) * radial * ribbon.aspect),
|
|
206
|
+
r: f3(0.006 + 0.012 * (1 - u)),
|
|
207
|
+
opacity: f3(dissolve > 0.01 ? 0.78 : 0),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { FormaForm } from "./model.js";
|
|
2
|
+
export declare const TAU: number;
|
|
3
|
+
export declare const clamp: (value: number, low: number, high: number) => number;
|
|
4
|
+
export declare const mix: (a: number, b: number, t: number) => number;
|
|
5
|
+
export declare const smooth: (t: number) => number;
|
|
6
|
+
export declare const f3: (value: number) => string;
|
|
7
|
+
export type Point = readonly [number, number];
|
|
8
|
+
export interface Body {
|
|
9
|
+
height: number;
|
|
10
|
+
width: number;
|
|
11
|
+
hw: (y: number) => number;
|
|
12
|
+
outline: readonly Point[];
|
|
13
|
+
core: {
|
|
14
|
+
cy: number;
|
|
15
|
+
r: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare const bodies: Record<FormaForm, Body>;
|
|
19
|
+
export interface EyeProfile {
|
|
20
|
+
power: number;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
spacing: number;
|
|
24
|
+
y: number;
|
|
25
|
+
signature?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare const eyeProfiles: Record<FormaForm, EyeProfile>;
|
|
28
|
+
export declare function superellipse(hw: number, hh: number, power: number, n: number): Point[];
|
|
29
|
+
export declare const visorPoints: readonly Point[];
|
|
30
|
+
export declare function closedPath(pts: readonly Point[]): string;
|
|
31
|
+
export interface Projected {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
sx: number;
|
|
35
|
+
sy: number;
|
|
36
|
+
front: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function project(body: Body, xs: number, ys: number, yaw: number, pitch: number): Projected;
|
|
39
|
+
export declare function shellPath(body: Body, morph: number): string;
|
|
40
|
+
export declare function stripePath(body: Body, yaw: number, a: number, b: number): string;
|
|
41
|
+
//# sourceMappingURL=geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../../src/internal/forma/geometry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,eAAO,MAAM,GAAG,QAAc,CAAC;AAE/B,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,KAAG,MAC3B,CAAC;AACvC,eAAO,MAAM,GAAG,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAAyB,CAAC;AAChF,eAAO,MAAM,MAAM,GAAI,GAAG,MAAM,KAAG,MAGlC,CAAC;AAGF,eAAO,MAAM,EAAE,GAAI,OAAO,MAAM,KAAG,MAAsD,CAAC;AAE1F,MAAM,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9C,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1B,OAAO,EAAE,SAAS,KAAK,EAAE,CAAC;IAC1B,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACjC;AAuGD,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,IAAI,CAgD1C,CAAC;AAIF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,CAAC,EAAE,MAAM,CAAC;IACV,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,UAAU,CASrD,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,EAAE,CAOtF;AAED,eAAO,MAAM,WAAW,EAAE,SAAS,KAAK,EAKD,CAAC;AAExC,wBAAgB,UAAU,CAAC,GAAG,EAAE,SAAS,KAAK,EAAE,GAAG,MAAM,CAYxD;AAED,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAkBjG;AAgCD,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAU3D;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAWhF"}
|