@dotcraft/avatar 0.6.3 → 0.6.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.
Files changed (61) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +46 -43
  3. package/dist/accessories.css +14 -14
  4. package/dist/avatar.css +62 -62
  5. package/dist/composer/ComposerMascot.d.ts +1 -1
  6. package/dist/composer/ComposerMascot.d.ts.map +1 -1
  7. package/dist/composer/ComposerMascot.js +2 -2
  8. package/dist/composer/ComposerMascot.js.map +1 -1
  9. package/dist/composer/styles/energy.css +99 -99
  10. package/dist/composer/styles/expressions.css +20 -20
  11. package/dist/composer/styles/feedback.css +188 -188
  12. package/dist/composer/styles/focus.css +72 -72
  13. package/dist/composer/styles/foundation.css +301 -287
  14. package/dist/composer/styles/idle.css +438 -438
  15. package/dist/composer/styles/overdrive.css +264 -264
  16. package/dist/composer/styles/props.css +21 -21
  17. package/dist/composer/types.d.ts +1 -0
  18. package/dist/composer/types.d.ts.map +1 -1
  19. package/dist/rig.css +265 -265
  20. package/dist/styles.css +13 -13
  21. package/package.json +56 -55
  22. package/src/AnimatedDecoration.tsx +43 -0
  23. package/src/AppearanceRig.tsx +15 -0
  24. package/src/Avatar.tsx +85 -0
  25. package/src/DecorationShapes.tsx +8 -0
  26. package/src/Decorations.tsx +31 -0
  27. package/src/Faces.tsx +37 -0
  28. package/src/HatDecorations.tsx +75 -0
  29. package/src/HeldDecorations.tsx +48 -0
  30. package/src/MascotRig.tsx +191 -0
  31. package/src/ObjectDecorations.tsx +55 -0
  32. package/src/accessories.css +14 -0
  33. package/src/appearanceModel.ts +65 -0
  34. package/src/avatar.css +62 -0
  35. package/src/characters.ts +5 -0
  36. package/src/composer/ComposerMascot.tsx +521 -0
  37. package/src/composer/ComposerMascotShadow.tsx +5 -0
  38. package/src/composer/constants.ts +68 -0
  39. package/src/composer/mascotHandoff.ts +19 -0
  40. package/src/composer/styles/energy.css +99 -0
  41. package/src/composer/styles/expressions.css +20 -0
  42. package/src/composer/styles/feedback.css +188 -0
  43. package/src/composer/styles/focus.css +72 -0
  44. package/src/composer/styles/foundation.css +301 -0
  45. package/src/composer/styles/idle.css +438 -0
  46. package/src/composer/styles/overdrive.css +264 -0
  47. package/src/composer/styles/props.css +21 -0
  48. package/src/composer/types.ts +32 -0
  49. package/src/composer/useComposerAvatarBehavior.ts +97 -0
  50. package/src/composer/useComposerMotion.ts +15 -0
  51. package/src/composer/useComposerProfile.ts +50 -0
  52. package/src/decorationCatalog.ts +45 -0
  53. package/src/decorationMotion.ts +68 -0
  54. package/src/environment.ts +46 -0
  55. package/src/index.ts +5 -0
  56. package/src/palette.ts +52 -0
  57. package/src/react.ts +6 -0
  58. package/src/rig.css +265 -0
  59. package/src/styles.css +13 -0
  60. package/src/useEventReplay.ts +13 -0
  61. package/src/useGesture.ts +51 -0
@@ -0,0 +1,68 @@
1
+ export const MASCOT_SIZE = 58
2
+ export const MASCOT_SCALE = 0.75
3
+ export const MASCOT_HIDDEN_RATIO = 0.06
4
+ export const MASCOT_RAISE = 3
5
+ export const MASCOT_SLEEP_AFTER_MS = 90_000
6
+ export const MASCOT_WAVE_DURATION_MS = 1_600
7
+ export const MASCOT_ACTIVE_IDLE_MIN_MS = 35_000
8
+ export const MASCOT_ACTIVE_IDLE_JITTER_MS = 30_000
9
+ export const MASCOT_ACTIVE_IDLE_ACTIVITY_THROTTLE_MS = 500
10
+
11
+ export type MascotActiveIdleMotion = 'hop' | 'rocket' | 'hover'
12
+ export type MascotActiveIdlePhase = 'outbound' | 'away' | 'inbound'
13
+
14
+ export interface MascotActiveIdleState {
15
+ motion: MascotActiveIdleMotion
16
+ phase: MascotActiveIdlePhase
17
+ }
18
+
19
+ export interface MascotProfileTransition {
20
+ fromAccent: string
21
+ toAccent: string
22
+ }
23
+
24
+ export const MASCOT_PROFILE_TRANSITION_SWAP_MS = 620
25
+ export const MASCOT_PROFILE_TRANSITION_DURATION_MS = 1240
26
+
27
+ export const MASCOT_ACTIVE_IDLE_TRAVEL_MS: Record<MascotActiveIdleMotion, number> = {
28
+ hop: 2400,
29
+ rocket: 1800,
30
+ hover: 1800
31
+ }
32
+
33
+ export const MASCOT_ACTIVE_IDLE_HOLD_MS: Record<MascotActiveIdleMotion, number> = {
34
+ hop: 1400,
35
+ rocket: 1400,
36
+ hover: 2800
37
+ }
38
+
39
+ export function pickMascotActiveIdle(random: number, previous: MascotActiveIdleMotion | null): MascotActiveIdleMotion {
40
+ const selected: MascotActiveIdleMotion = random < 0.65 ? 'hop' : random < 0.9 ? 'rocket' : 'hover'
41
+ if (selected !== previous) return selected
42
+ return selected === 'hop' ? 'rocket' : selected === 'rocket' ? 'hop' : 'rocket'
43
+ }
44
+
45
+ export const MASCOT_SPARKLES = Array.from({ length: 7 }, (_, i) => {
46
+ const angle = ((-150 + i * 40) * Math.PI) / 180
47
+ const radius = 26 + (i % 3) * 9
48
+ return {
49
+ dx: `${(Math.cos(angle) * radius).toFixed(1)}px`,
50
+ dy: `${(Math.sin(angle) * radius - 8).toFixed(1)}px`,
51
+ delay: `${i * 40}ms`
52
+ }
53
+ })
54
+
55
+ export function prefersReducedMotion(): boolean {
56
+ const configuredPreference =
57
+ typeof document !== 'undefined' ? document.documentElement.dataset.reduceMotion : undefined
58
+ if (configuredPreference === 'on') return true
59
+ if (configuredPreference === 'off') return false
60
+
61
+ return typeof window !== 'undefined' && typeof window.matchMedia === 'function'
62
+ ? window.matchMedia('(prefers-reduced-motion: reduce)')?.matches ?? false
63
+ : false
64
+ }
65
+
66
+ export function mascotNameKey(name?: string): string {
67
+ return name?.trim().normalize('NFC') ?? ''
68
+ }
@@ -0,0 +1,19 @@
1
+ interface MascotHandoffRecord {
2
+ top: number
3
+ time: number
4
+ }
5
+
6
+ let record: MascotHandoffRecord | null = null
7
+
8
+ export function recordMascotHandoff(el: HTMLElement): void {
9
+ record = { top: el.getBoundingClientRect().top, time: performance.now() }
10
+ }
11
+
12
+ export function consumeMascotHandoff(el: HTMLElement): number | null {
13
+ if (!record) return null
14
+ const { top, time } = record
15
+ record = null
16
+ if (performance.now() - time > 400) return null
17
+ const dy = top - el.getBoundingClientRect().top
18
+ return Math.abs(dy) < 8 ? null : dy
19
+ }
@@ -0,0 +1,99 @@
1
+ [data-mascot-effort] .dca-robot :is(.dca-paint-body, .dca-paint-mark) stop {
2
+ transition: stop-color 360ms ease;
3
+ }
4
+
5
+ [data-mascot-effort='medium'] .dca-robot .dca-paint-body stop:nth-child(1) {
6
+ animation: composer-mascot-energy-medium-dark 6s ease-in-out infinite;
7
+ }
8
+
9
+ [data-mascot-effort='medium'] .dca-robot .dca-paint-body stop:nth-child(2) {
10
+ animation: composer-mascot-energy-medium-mid 6s ease-in-out infinite;
11
+ }
12
+
13
+ [data-mascot-effort='medium'] .dca-robot .dca-paint-body stop:nth-child(3) {
14
+ animation: composer-mascot-energy-medium-light 6s ease-in-out infinite;
15
+ }
16
+
17
+ [data-mascot-effort='high'] .dca-robot .dca-paint-body stop:nth-child(1) {
18
+ animation: composer-mascot-energy-high-dark 3.8s ease-in-out infinite;
19
+ }
20
+
21
+ [data-mascot-effort='high'] .dca-robot .dca-paint-body stop:nth-child(2) {
22
+ animation: composer-mascot-energy-high-mid 3.8s ease-in-out infinite;
23
+ }
24
+
25
+ [data-mascot-effort='high'] .dca-robot .dca-paint-body stop:nth-child(3) {
26
+ animation: composer-mascot-energy-high-light 3.8s ease-in-out infinite;
27
+ }
28
+
29
+ [data-mascot-effort='high'] .dca-robot .dca-paint-mark stop:nth-child(1) {
30
+ animation: composer-mascot-energy-high-mark-dark 3.8s ease-in-out infinite;
31
+ }
32
+
33
+ [data-mascot-effort='high'] .dca-robot .dca-paint-mark stop:nth-child(2),
34
+ [data-mascot-effort='high'] .dca-robot .dca-paint-mark stop:nth-child(3) {
35
+ animation: composer-mascot-energy-high-mark-light 3.8s ease-in-out infinite;
36
+ }
37
+
38
+ [data-mascot-effort='high'] .dca-robot {
39
+ filter: drop-shadow(0 0 5px color-mix(in srgb,
40
+ oklch(from var(--mascot-energy-accent)
41
+ clamp(0.28, calc(l - 0.0112), 0.96)
42
+ clamp(0, calc(c * 1.116), 0.3)
43
+ calc(h - 3.09)) 28%, transparent));
44
+ }
45
+
46
+ [data-mascot-effort='extraHigh'] .dca-robot .dca-paint-body stop:nth-child(1) {
47
+ animation: composer-mascot-energy-extra-dark 2.8s ease-in-out infinite;
48
+ }
49
+
50
+ [data-mascot-effort='extraHigh'] .dca-robot .dca-paint-body stop:nth-child(2) {
51
+ animation: composer-mascot-energy-extra-mid 2.8s ease-in-out infinite;
52
+ }
53
+
54
+ [data-mascot-effort='extraHigh'] .dca-robot .dca-paint-body stop:nth-child(3) {
55
+ animation: composer-mascot-energy-extra-light 2.8s ease-in-out infinite;
56
+ }
57
+
58
+ [data-mascot-effort='extraHigh'] .dca-robot .dca-paint-mark stop:nth-child(1) {
59
+ animation: composer-mascot-energy-extra-mark-dark 2.8s ease-in-out infinite;
60
+ }
61
+
62
+ [data-mascot-effort='extraHigh'] .dca-robot .dca-paint-mark stop:nth-child(2),
63
+ [data-mascot-effort='extraHigh'] .dca-robot .dca-paint-mark stop:nth-child(3) {
64
+ animation: composer-mascot-energy-extra-mark-light 2.8s ease-in-out infinite;
65
+ }
66
+
67
+ [data-mascot-effort='extraHigh'] .dca-robot {
68
+ filter: drop-shadow(0 0 7px color-mix(in srgb,
69
+ oklch(from var(--mascot-energy-accent)
70
+ clamp(0.28, calc(l - 0.0456), 0.96)
71
+ clamp(0, calc(c * 1.231), 0.3)
72
+ calc(h + 27.55)) 36%, transparent));
73
+ }
74
+
75
+ [data-mascot-context='max'] .dca-robot {
76
+ animation: composer-mascot-max-overdrive-edge 3.8s ease-in-out infinite;
77
+ will-change: filter;
78
+ }
79
+
80
+ [data-mascot-context='max'] .dca-robot .dca-paint-body stop:nth-child(1) {
81
+ animation: composer-mascot-max-coat-dark 3.8s ease-in-out infinite;
82
+ }
83
+
84
+ [data-mascot-context='max'] .dca-robot .dca-paint-body stop:nth-child(2) {
85
+ animation: composer-mascot-max-coat-mid 3.8s ease-in-out infinite;
86
+ }
87
+
88
+ [data-mascot-context='max'] .dca-robot .dca-paint-body stop:nth-child(3) {
89
+ animation: composer-mascot-max-coat-light 3.8s ease-in-out infinite;
90
+ }
91
+
92
+ [data-mascot-context='max'] .dca-robot .dca-paint-mark stop:nth-child(1) {
93
+ animation: composer-mascot-max-coat-mark-dark 3.8s ease-in-out infinite;
94
+ }
95
+
96
+ [data-mascot-context='max'] .dca-robot .dca-paint-mark stop:nth-child(2),
97
+ [data-mascot-context='max'] .dca-robot .dca-paint-mark stop:nth-child(3) {
98
+ animation: composer-mascot-max-coat-mark-light 3.8s ease-in-out infinite;
99
+ }
@@ -0,0 +1,20 @@
1
+ @keyframes composer-mascot-nod {
2
+ 0%, 100% { transform: translateY(0); }
3
+ 40% { transform: translateY(2.5px) scale(1.015, 0.985); }
4
+ }
5
+
6
+ .composer-mascot-nod {
7
+ animation: composer-mascot-nod 140ms ease-out;
8
+ transform-origin: bottom center;
9
+ }
10
+
11
+ @keyframes composer-mascot-think-sway {
12
+ 0%, 100% { transform: rotate(-2.2deg); }
13
+ 50% { transform: rotate(2.2deg); }
14
+ }
15
+
16
+ .composer-mascot-think {
17
+ animation: composer-mascot-think-sway 2.6s ease-in-out infinite;
18
+ transform-origin: bottom center;
19
+ will-change: transform;
20
+ }
@@ -0,0 +1,188 @@
1
+ @keyframes composer-mascot-cheer {
2
+ 0% {
3
+ transform: translateY(0) scale(1, 1);
4
+ }
5
+ 12% {
6
+ transform: translateY(2px) scale(1.08, 0.9);
7
+ }
8
+ 30% {
9
+ transform: translateY(-13px) scale(0.96, 1.06);
10
+ }
11
+ 46% {
12
+ transform: translateY(0) scale(1.05, 0.94);
13
+ }
14
+ 60% {
15
+ transform: translateY(-8px) scale(0.98, 1.03);
16
+ }
17
+ 74% {
18
+ transform: translateY(0) scale(1.02, 0.98);
19
+ }
20
+ 100% {
21
+ transform: translateY(0) scale(1, 1);
22
+ }
23
+ }
24
+
25
+ .composer-mascot-cheer {
26
+ animation: composer-mascot-cheer 1s ease-out;
27
+ transform-origin: bottom center;
28
+ }
29
+
30
+ .composer-mascot-sparkles {
31
+ position: absolute;
32
+ inset: 0;
33
+ pointer-events: none;
34
+ }
35
+
36
+ .composer-mascot-sparkles i {
37
+ position: absolute;
38
+ left: 50%;
39
+ top: 6%;
40
+ width: 7px;
41
+ height: 7px;
42
+ background: var(--dca-theme-success);
43
+ clip-path: polygon(50% 0, 64% 36%, 100% 50%, 64% 64%, 50% 100%, 36% 64%, 0 50%, 36% 36%);
44
+ animation: composer-mascot-sparkle 900ms ease-out forwards;
45
+ }
46
+
47
+ @keyframes composer-mascot-sparkle {
48
+ 0% {
49
+ opacity: 0;
50
+ transform: translate(0, 0) scale(0.4) rotate(0);
51
+ }
52
+ 18% {
53
+ opacity: 1;
54
+ }
55
+ 100% {
56
+ opacity: 0;
57
+ transform: translate(var(--dx), var(--dy)) scale(1) rotate(160deg);
58
+ }
59
+ }
60
+
61
+ @keyframes composer-mascot-shake {
62
+ 0%,
63
+ 100% {
64
+ transform: translateX(0) rotate(0);
65
+ }
66
+ 18% {
67
+ transform: translateX(-5px) rotate(-2.4deg);
68
+ }
69
+ 38% {
70
+ transform: translateX(5px) rotate(2.4deg);
71
+ }
72
+ 58% {
73
+ transform: translateX(-4px) rotate(-1.8deg);
74
+ }
75
+ 78% {
76
+ transform: translateX(3px) rotate(1.2deg);
77
+ }
78
+ }
79
+
80
+ .composer-mascot-shake {
81
+ animation: composer-mascot-shake 520ms ease-in-out;
82
+ transform-origin: bottom center;
83
+ }
84
+
85
+ @keyframes composer-mascot-eager-hop {
86
+ 0%,
87
+ 100% {
88
+ transform: translateY(0) scale(1, 1);
89
+ }
90
+ 50% {
91
+ transform: translateY(-4px) scale(0.99, 1.02);
92
+ }
93
+ }
94
+
95
+ .composer-mascot-eager {
96
+ animation: composer-mascot-eager-hop 460ms ease-in-out infinite;
97
+ transform-origin: bottom center;
98
+ }
99
+
100
+ @keyframes composer-mascot-sleep-breathe {
101
+ 0%,
102
+ 100% {
103
+ transform: scale(1);
104
+ }
105
+ 50% {
106
+ transform: scale(1.05, 1.06);
107
+ }
108
+ }
109
+
110
+ .composer-mascot-sleep-breathe {
111
+ animation: composer-mascot-sleep-breathe 5.6s ease-in-out infinite;
112
+ transform-origin: bottom center;
113
+ }
114
+
115
+ .composer-mascot-zzz {
116
+ position: absolute;
117
+ inset: 0;
118
+ pointer-events: none;
119
+ }
120
+
121
+ .composer-mascot-zzz span {
122
+ position: absolute;
123
+ right: -2px;
124
+ top: 2px;
125
+ font-family: Georgia, serif;
126
+ font-size: 13px;
127
+ font-weight: 700;
128
+ color: var(--dca-theme-dimmed);
129
+ opacity: 0;
130
+ animation: composer-mascot-zzz 2.8s ease-out infinite;
131
+ }
132
+
133
+ .composer-mascot-zzz span:nth-child(2) {
134
+ animation-delay: 0.9s;
135
+ right: 2px;
136
+ }
137
+
138
+ .composer-mascot-zzz span:nth-child(3) {
139
+ animation-delay: 1.8s;
140
+ right: 6px;
141
+ }
142
+
143
+ @keyframes composer-mascot-zzz {
144
+ 0% {
145
+ opacity: 0;
146
+ transform: translate(0, 0) scale(0.7);
147
+ }
148
+ 25% {
149
+ opacity: 0.9;
150
+ }
151
+ 100% {
152
+ opacity: 0;
153
+ transform: translate(13px, -26px) scale(1.15);
154
+ }
155
+ }
156
+
157
+ @keyframes composer-mascot-startle {
158
+ 0% {
159
+ transform: translateY(0) scale(1);
160
+ }
161
+ 30% {
162
+ transform: translateY(-7px) scale(1.1, 0.96);
163
+ }
164
+ 60% {
165
+ transform: translateY(0) scale(0.97, 1.03);
166
+ }
167
+ 100% {
168
+ transform: translateY(0) scale(1);
169
+ }
170
+ }
171
+
172
+ .composer-mascot-startle {
173
+ animation: composer-mascot-startle 320ms ease-out;
174
+ transform-origin: bottom center;
175
+ }
176
+
177
+ @keyframes composer-mascot-push-lift {
178
+ 0% { transform: translateY(0) scale(1); }
179
+ 38% { transform: translateY(-7px) scale(0.98, 1.035); }
180
+ 72% { transform: translateY(1px) scale(1.025, 0.975); }
181
+ 88% { transform: translateY(-1px) scale(0.995, 1.01); }
182
+ 100% { transform: translateY(0) scale(1); }
183
+ }
184
+
185
+ .composer-mascot-push-lift {
186
+ animation: composer-mascot-push-lift 360ms cubic-bezier(0.22, 0.72, 0.24, 1);
187
+ transform-origin: bottom center;
188
+ }
@@ -0,0 +1,72 @@
1
+ @keyframes composer-mascot-launch {
2
+ 0% {
3
+ transform: translateY(0) scale(1, 1);
4
+ }
5
+ 14% {
6
+ transform: translateY(3px) scale(1.14, 0.8);
7
+ }
8
+ 38% {
9
+ transform: translateY(-20px) scale(0.9, 1.14);
10
+ }
11
+ 58% {
12
+ transform: translateY(0) scale(1.1, 0.88);
13
+ }
14
+ 74% {
15
+ transform: translateY(-6px) scale(0.98, 1.04);
16
+ }
17
+ 88% {
18
+ transform: translateY(0) scale(1.02, 0.98);
19
+ }
20
+ 100% {
21
+ transform: translateY(0) scale(1, 1);
22
+ }
23
+ }
24
+
25
+ .composer-mascot-launch {
26
+ animation: composer-mascot-launch 820ms cubic-bezier(0.3, 0.9, 0.4, 1);
27
+ transform-origin: bottom center;
28
+ }
29
+
30
+ @keyframes composer-mascot-jelly {
31
+ 0% {
32
+ transform: scale(1, 1);
33
+ }
34
+ 25% {
35
+ transform: scale(1.16, 0.84);
36
+ }
37
+ 45% {
38
+ transform: scale(0.9, 1.1);
39
+ }
40
+ 62% {
41
+ transform: scale(1.06, 0.94);
42
+ }
43
+ 78% {
44
+ transform: scale(0.98, 1.02);
45
+ }
46
+ 100% {
47
+ transform: scale(1, 1);
48
+ }
49
+ }
50
+
51
+ .composer-mascot-jelly {
52
+ transform-origin: bottom center;
53
+ }
54
+
55
+ .composer-mascot-jelly:hover {
56
+ animation: composer-mascot-jelly 600ms ease-out;
57
+ }
58
+
59
+ @keyframes composer-mascot-bubble-in {
60
+ 0% {
61
+ opacity: 0;
62
+ transform: translateY(6px) scale(0.94);
63
+ }
64
+ 100% {
65
+ opacity: 1;
66
+ transform: translateY(0) scale(1);
67
+ }
68
+ }
69
+
70
+ .composer-mascot-bubble {
71
+ animation: composer-mascot-bubble-in 260ms cubic-bezier(0.34, 1.56, 0.64, 1);
72
+ }