@dotcraft/avatar 0.7.3 → 0.7.5

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 (77) hide show
  1. package/README.md +1 -1
  2. package/dist/BackDecorations.d.ts.map +1 -1
  3. package/dist/BackDecorations.js +100 -6
  4. package/dist/BackDecorations.js.map +1 -1
  5. package/dist/CloudDragon.d.ts +3 -0
  6. package/dist/CloudDragon.d.ts.map +1 -0
  7. package/dist/CloudDragon.js +70 -0
  8. package/dist/CloudDragon.js.map +1 -0
  9. package/dist/DecorationShapes.d.ts +5 -0
  10. package/dist/DecorationShapes.d.ts.map +1 -1
  11. package/dist/DecorationShapes.js +4 -0
  12. package/dist/DecorationShapes.js.map +1 -1
  13. package/dist/Decorations.d.ts.map +1 -1
  14. package/dist/Decorations.js +3 -2
  15. package/dist/Decorations.js.map +1 -1
  16. package/dist/FaceDecorations.d.ts.map +1 -1
  17. package/dist/FaceDecorations.js +105 -5
  18. package/dist/FaceDecorations.js.map +1 -1
  19. package/dist/HandDecorations.d.ts.map +1 -1
  20. package/dist/HandDecorations.js +19 -2
  21. package/dist/HandDecorations.js.map +1 -1
  22. package/dist/HatDecorations.d.ts.map +1 -1
  23. package/dist/HatDecorations.js +1 -0
  24. package/dist/HatDecorations.js.map +1 -1
  25. package/dist/ObjectDecorations.d.ts.map +1 -1
  26. package/dist/ObjectDecorations.js +59 -0
  27. package/dist/ObjectDecorations.js.map +1 -1
  28. package/dist/SkinDecorations.d.ts.map +1 -1
  29. package/dist/SkinDecorations.js +37 -8
  30. package/dist/SkinDecorations.js.map +1 -1
  31. package/dist/accessories.css +4 -2
  32. package/dist/composer/ComposerMascot.d.ts +1 -1
  33. package/dist/composer/ComposerMascot.d.ts.map +1 -1
  34. package/dist/composer/ComposerMascot.js +5 -4
  35. package/dist/composer/ComposerMascot.js.map +1 -1
  36. package/dist/composer/styles/energy.css +23 -3
  37. package/dist/composer/types.d.ts +3 -0
  38. package/dist/composer/types.d.ts.map +1 -1
  39. package/dist/decorationCatalog.d.ts.map +1 -1
  40. package/dist/decorationCatalog.js +91 -0
  41. package/dist/decorationCatalog.js.map +1 -1
  42. package/dist/decorationMotion.d.ts.map +1 -1
  43. package/dist/decorationMotion.js +5 -2
  44. package/dist/decorationMotion.js.map +1 -1
  45. package/dist/effects.css +65 -4
  46. package/dist/items.d.ts +546 -0
  47. package/dist/items.d.ts.map +1 -1
  48. package/dist/items.js +42 -13
  49. package/dist/items.js.map +1 -1
  50. package/dist/paintMaterials.d.ts +36 -0
  51. package/dist/paintMaterials.d.ts.map +1 -1
  52. package/dist/paintMaterials.js +9 -0
  53. package/dist/paintMaterials.js.map +1 -1
  54. package/dist/spine.d.ts +15 -0
  55. package/dist/spine.d.ts.map +1 -0
  56. package/dist/spine.js +53 -0
  57. package/dist/spine.js.map +1 -0
  58. package/package.json +1 -1
  59. package/src/BackDecorations.tsx +250 -5
  60. package/src/CloudDragon.tsx +110 -0
  61. package/src/DecorationShapes.tsx +4 -0
  62. package/src/Decorations.tsx +4 -3
  63. package/src/FaceDecorations.tsx +254 -11
  64. package/src/HandDecorations.tsx +223 -1
  65. package/src/HatDecorations.tsx +8 -0
  66. package/src/ObjectDecorations.tsx +180 -0
  67. package/src/SkinDecorations.tsx +77 -12
  68. package/src/accessories.css +4 -2
  69. package/src/composer/ComposerMascot.tsx +5 -4
  70. package/src/composer/styles/energy.css +23 -3
  71. package/src/composer/types.ts +3 -0
  72. package/src/decorationCatalog.ts +91 -0
  73. package/src/decorationMotion.ts +5 -2
  74. package/src/effects.css +65 -4
  75. package/src/items.ts +43 -13
  76. package/src/paintMaterials.ts +9 -0
  77. package/src/spine.ts +61 -0
@@ -50,18 +50,51 @@ const copy: Record<ItemId, DecorationCopy> = {
50
50
  'propeller-cap': { name: 'Propeller cap', feature: 'Yellow and red cap dome with a spinning propeller on a button', colors: ['#f6b500', '#e8654f', '#c9d2ff'], mount: 'Dome overlaps the top edge; the propeller spins flat at full size' },
51
51
  'graduation-cap': { name: 'Graduation cap', feature: 'Black mortarboard with a gold button and a swinging tassel', colors: ['#2b2f3a', '#3c4658', '#f6b500'], mount: 'Skull cap rests on the top edge; the board floats above it' },
52
52
  'ice-cream': { name: 'Ice cream', feature: 'Strawberry scoop with an upright cone and a cherry', colors: ['#f2a0b4', '#e0ad84', '#e8654f'], mount: 'Scoop rests on the top edge; the cone points up' },
53
+ 'floppy-disk': { name: 'Floppy disk', feature: 'Navy disk with a silver shutter and a white label', colors: ['#3c4658', '#a7b1c0', '#ffffff'], mount: 'Tilted disk stands half-inserted in the top edge, shutter up' },
53
54
  'flower-crown': { name: 'Flower crown', feature: 'Green wreath with alternating pink and yellow blossoms', colors: ['#89b875', '#f2a0b4', '#f6b500'], mount: 'Wreath follows the top edge without a brim' },
54
55
  'pirate-hat': { name: 'Pirate hat', feature: 'Black tricorn with an upturned brim and a white skull mark', colors: ['#2b2f3a', '#3c4658', '#ffffff'], mount: 'Brim rests on the top edge; points stay clear of the arms' },
56
+ rocket: { name: 'Rocket', feature: 'Pale rocket with a red nose, red fins and a flickering flame', colors: ['#dce8f4', '#e8654f', '#ffb347'], mount: 'Fins rest on the top edge; the flame burns between them' },
55
57
  lightning: { name: 'Lightning', feature: 'Yellow bolt standing upright with a breathing glow', colors: ['#ffcf11', '#fff3c4'], mount: 'Lower tip touches the top edge center' },
56
58
  'crystal-cluster': { name: 'Crystal cluster', feature: 'Three violet crystals with a lighter facet and a breathing glow', colors: ['#8b5cf6', '#a78bfa', '#c4b5fd'], mount: 'Crystal bases sit on the top edge' },
57
59
  ufo: { name: 'UFO', feature: 'Hovering saucer with a glass dome, blinking lights and a tractor beam', colors: ['#8b95a5', '#a2c5d1', '#c7f6ff'], mount: 'Saucer floats above the head; the beam lands on the top edge' },
60
+ 'mini-volcano': { name: 'Mini volcano', feature: 'Dark rock cone with a glowing lava crater and rising smoke', colors: ['#564545', '#e8451f', '#ffb347'], mount: 'Wide base rests flat on the top edge; smoke rises from the crater' },
61
+ 'phoenix-perch': { name: 'Phoenix', feature: 'Orange firebird with a flickering flame crest and flame tail', colors: ['#ee6a2c', '#c2412a', '#ffb02e'], mount: 'Talons grip the top edge; the tail rises clear of both arms' },
62
+ 'satellite-dish': { name: 'Satellite dish', feature: 'Tilted gray dish with a yellow feed on a short mast', colors: ['#8b95a5', '#dce8f4', '#f6b500'], mount: 'Mast foot bolts to the top edge center, where the antenna stood' },
63
+ 'cursor-arrow': { name: 'Cursor arrow', feature: 'Giant black pointer with a white border, clicking the head', colors: ['#2b2f3a', '#5b6577', '#ffffff'], mount: 'Tip touches the top edge; the tail rises to the upper right' },
64
+ 'viking-helmet': { name: 'Viking helmet', feature: 'Steel dome with a tan band and two curved ivory horns', colors: ['#a7b1c0', '#c49b5e', '#fff1dc'], mount: 'Band sits on the top edge; the horns stay within the widest brim' },
65
+ 'wind-up-key': { name: 'Wind-up key', feature: 'Brass toy key whose two round bows slowly turn', colors: ['#efc65c', '#c99139'], mount: 'Shaft plugs into the top edge center' },
66
+ crab: { name: 'Crab', feature: 'Red crab with both pincers raised and snapping', colors: ['#e8654f', '#c24a3a', '#fff4ef'], mount: 'Four legs hook over the top edge; the pincers stay clear of both arms' },
67
+ cupcake: { name: 'Cupcake', feature: 'Pink swirl cupcake in a teal cup with one lit candle', colors: ['#f2a0b4', '#7fc7d9', '#ffb347'], mount: 'Flat cup base rests on the top edge' },
68
+ gears: { name: 'Gears', feature: 'Two meshed steel cogs turning against each other', colors: ['#8b95a5', '#a7b1c0', '#f6b500'], mount: 'Both cogs stand on the top edge and mesh above its center' },
69
+ 'rain-cloud': { name: 'Rain cloud', feature: 'Gray storm cloud raining in three blue columns', colors: ['#9aa6b8', '#dfe5ee', '#4f7cf6'], mount: 'Cloud floats above the head; the rain lands on the top edge' },
70
+ 'maneki-neko': { name: 'Maneki-neko', feature: 'Gold beckoning cat with a red collar, sheen and warm glow', colors: ['#efc65c', '#e8654f', '#fff3c4'], mount: 'Base sits on the top edge center; the raised paw stays clear of the arms' },
58
71
 
59
72
  shades: { name: 'Shades', feature: 'Two dark lenses with a short bridge pushed up on the brow', colors: ['#2b2f3a', '#6b7280'], mount: 'Fits within the forehead band above the screen' },
73
+ headlamp: { name: 'Headlamp', feature: 'Round yellow lamp with a gray bezel on a dark strap', colors: ['#3c4658', '#8b95a5', '#ffd970'], mount: 'Lamp centers in the forehead band; the strap spans the brow' },
60
74
  'forehead-goggles': { name: 'Forehead goggles', feature: 'Two blue-gray lenses with a center bridge', colors: ['#8b7568', '#a2c5d1', '#dec8ae'], mount: 'Fits within the forehead band; bridge anchors above the screen' },
75
+ 'heart-glasses': { name: 'Heart glasses', feature: 'Pink heart lenses with a short bridge pushed up on the brow', colors: ['#c9577a', '#f2a0b4'], mount: 'Fits within the forehead band above the screen' },
76
+ 'snorkel-mask': { name: 'Snorkel mask', feature: 'Wide cyan mask pushed up with an orange snorkel tube', colors: ['#a2c5d1', '#3c4658', '#ed985f'], mount: 'Mask fits the forehead band; the tube rises at the screen-right temple and stows for the question sign' },
77
+ 'bow-tie': { name: 'Bow tie', feature: 'Red bow tie with two flared wings and a round knot', colors: ['#e8654f', '#b94f50'], mount: 'Knot centers on the chin below the screen; the wings overhang the base' },
78
+ neckerchief: { name: 'Neckerchief', feature: 'Yellow triangle scarf with a rolled band, point down', colors: ['#dbaa40', '#f3cf62'], mount: 'Band follows the chin below the screen; the point hangs over the base' },
79
+ 'ear-pencil': { name: 'Ear pencil', feature: 'Yellow pencil tucked at an angle with the pink eraser up', colors: ['#f3cf62', '#e0ad84', '#f2a0b4'], mount: 'Rests at the screen-right temple; stows while the arm holds the question sign' },
80
+ 'bell-collar': { name: 'Bell collar', feature: 'Red collar with a round gold bell at the front', colors: ['#e8654f', '#f6b500', '#805840'], mount: 'Collar follows the chin below the screen; the bell hangs over the base' },
81
+ 'watermelon-glasses': { name: 'Watermelon glasses', feature: 'Two half-moon watermelon lenses with green rinds down and dark seeds', colors: ['#e8654f', '#4fae6a', '#2b2f3a'], mount: 'Fits within the forehead band above the screen' },
82
+ 'star-glasses': { name: 'Star glasses', feature: 'Two gold star lenses with a short bridge; one lens glints', colors: ['#f6b500', '#fff3c4'], mount: 'Fits within the forehead band above the screen' },
83
+ 'sticky-note': { name: 'Sticky note', feature: 'Yellow square note with one curled corner', colors: ['#f6d365', '#e8b93a'], mount: 'Stuck flat on the screen-right temple corner at a slight tilt; stows while the arm holds the question sign' },
84
+ 'lanyard-badge': { name: 'Lanyard badge', feature: 'Conference badge with a role-color header on a yellow lanyard', colors: ['#ffffff', '#3161f7', '#f6b500'], mount: 'Lanyard runs along the chin below the screen; the badge hangs over the base' },
85
+ 'prize-rosette': { name: 'Prize rosette', feature: 'Blue pleated rosette with a gold center and two notched ribbon tails', colors: ['#4f7cf6', '#3161f7', '#f6b500'], mount: 'Pinned at the chin center below the screen; the tails hang over the base' },
86
+ 'cherry-charm': { name: 'Cherry charm', feature: 'Two red cherries on a green V stem with a leaf', colors: ['#e8654f', '#b94f50', '#89b875'], mount: 'Stem hooks over the screen-right temple corner; stows while the arm holds the question sign' },
87
+ songbird: { name: 'Songbird', feature: 'Round bluebird with an orange breast and a bobbing head', colors: ['#4f7cf6', '#ed985f', '#2b2f3a'], mount: 'Perches on the screen-right temple corner; stows while the arm holds the question sign' },
88
+ amulet: { name: 'Amulet', feature: 'Dark cord with a glowing teal teardrop crystal', colors: ['#3c4658', '#5eead4', '#ccfbf1'], mount: 'Cord follows the chin below the screen; the crystal hangs over the base' },
61
89
  'mecha-faceplate': { name: 'Mecha faceplate', feature: 'White face armor with a dark visor band, angular yellow-green eyes, a red chin plate and two vents', colors: ['#f4f6fb', '#1d2433', '#d94a3a'], mount: 'Replaces the face inside the white screen frame; the eyes change light, not shape' },
62
90
  'pixel-screen': { name: 'Pixel screen', feature: 'Dark monitor with phosphor-green pixel eyes: blocks, a blocky smile, a loading bar and dim dashes', colors: ['#1f2a3a', '#7dff8a'], mount: 'Replaces the face inside the white screen frame; pixel glyphs carry all four expressions' },
91
+ 'segment-display': { name: 'Segment display', feature: 'Olive LCD with two seven-segment digit eyes', colors: ['#b7c49a', '#2f3a2b'], mount: 'Replaces the face inside the white screen frame; lit segments carry all four expressions' },
92
+ 'knight-visor': { name: 'Knight visor', feature: 'Steel great-helm plate with a T-shaped slit and ember eyes', colors: ['#a7b1c0', '#1d2433', '#ffb547'], mount: 'Replaces the face inside the white screen frame; light carries all four expressions' },
63
93
  'gold-faceplate': { name: 'Gold faceplate', feature: 'Armored gold plate whose fixed eye slits change light, not shape: cyan idle, warm flare, amber scan, dim ember', colors: ['#efc04a', '#bf8a2a', '#6fdcff'], mount: 'Replaces the face inside the white screen frame; light carries all four expressions' },
94
+ 'porthole-helmet': { name: 'Porthole helmet', feature: 'Copper diving helmet with a round porthole, LED eyes and rising bubbles', colors: ['#c47a4a', '#1f4a5a', '#c7f6ff'], mount: 'Replaces the face inside the white screen frame; the LED pair carries all four expressions' },
64
95
  'neon-visor': { name: 'Neon visor', feature: 'Full smoked visor with a pair of LED eyes that lid, squint and rest, plus a passing scan line', colors: ['#1d2433', '#4de3ff', '#c7f6ff'], mount: 'Replaces the face inside the white screen frame; the LED pair carries all four expressions' },
96
+ 'dragon-visor': { name: 'Dragon visor', feature: 'Dark scaled plate with slit-pupil eyes that smolder, flare, scan and dim', colors: ['#1f2b2c', '#2f4543', '#ffb547'], mount: 'Replaces the face inside the white screen frame; light carries all four expressions' },
97
+ 'code-rain': { name: 'Code rain', feature: 'Black screen of falling green code behind a pair of LED eyes', colors: ['#0b1410', '#7dff8a', '#d9ffe0'], mount: 'Replaces the face inside the white screen frame; the LED pair carries all four expressions' },
65
98
 
66
99
  'task-board': { name: 'Task board', feature: 'White planning board with connected role-color markers and a yellow accent', colors: ['#ffffff', '#3161f7', '#f6b500'], mount: held },
67
100
  wrench: { name: 'Wrench', feature: 'White open-ended wrench with role-color outlines and a yellow handle inset', colors: ['#ffffff', '#3161f7', '#f6b500'], mount: held },
@@ -71,32 +104,90 @@ const copy: Record<ItemId, DecorationCopy> = {
71
104
  'control-panel': { name: 'Control panel', feature: 'White control panel with dark rails and role-color and yellow knobs', colors: ['#ffffff', '#3161f7', '#f6b500'], mount: held },
72
105
  'magic-wand': { name: 'Magic wand', feature: 'Dark wand tipped with a gold star and drifting sparkles', colors: ['#3b2f5c', '#f6b500', '#fff3c4'], mount: held },
73
106
  'energy-blade': { name: 'Energy blade', feature: 'Compact hilt with a blade that cycles cyan, magenta and green while it extends and retracts', colors: ['#2a3140', '#4de3ff', '#e6fbff'], mount: `${held}; the blade rises beside the body, never across the screen` },
107
+ 'thunder-hammer': { name: 'Thunder hammer', feature: 'Steel hammer with a role-color band and crackling lightning arcs', colors: ['#aab4c3', '#3161f7', '#4de3ff'], mount: `${held}; the head rises beside the body, never across the screen` },
74
108
  paintbrush: { name: 'Paintbrush', feature: 'Wooden brush with a steel ferrule and a role-color paint tip', colors: ['#e0ad84', '#8b95a5', '#3161f7'], mount: held },
75
109
  'boba-tea': { name: 'Boba tea', feature: 'White cup of milk tea with dark pearls and a yellow straw', colors: ['#ffffff', '#e0ad84', '#3c4658'], mount: held },
76
110
  gamepad: { name: 'Gamepad', feature: 'White controller with a role-color d-pad and two buttons', colors: ['#ffffff', '#3161f7', '#f6b500'], mount: held },
77
111
  flag: { name: 'Flag', feature: 'Steel pole with a waving role-color pennant', colors: ['#8b95a5', '#3161f7'], mount: `${held}; the pennant rises beside the body` },
78
112
  lantern: { name: 'Lantern', feature: 'Red paper lantern with dark caps and a warm breathing glow', colors: ['#e8654f', '#3c4658', '#ffd970'], mount: held },
79
113
  staff: { name: 'Staff', feature: 'Wooden staff topped with a glowing orb that shifts violet, cyan and pink', colors: ['#8a6520', '#8b5cf6', '#c4b5fd'], mount: `${held}; the orb rises beside the body` },
114
+ umbrella: { name: 'Umbrella', feature: 'Open red umbrella with a scalloped canopy and a hooked handle', colors: ['#e8654f', '#b94f50', '#3c4658'], mount: `${held}; the canopy rises beside the body` },
115
+ 'pizza-slice': { name: 'Pizza slice', feature: 'Cheese slice pointing up with a crust and pepperoni dots', colors: ['#f3cf62', '#e0ad84', '#e8654f'], mount: held },
116
+ sunflower: { name: 'Sunflower', feature: 'Scalloped yellow bloom with a brown center on a leafy stem', colors: ['#f6b500', '#805840', '#89b875'], mount: `${held}; the bloom rises beside the body` },
117
+ camera: { name: 'Camera', feature: 'White compact camera with a big role-color lens ring and yellow shutter', colors: ['#ffffff', '#3161f7', '#f6b500'], mount: held },
118
+ binoculars: { name: 'Binoculars', feature: 'Two upright green barrels with cyan lenses facing out', colors: ['#537f59', '#3c4658', '#a2c5d1'], mount: held },
119
+ 'goldfish-bag': { name: 'Goldfish bag', feature: 'Knotted water bag with an orange goldfish inside', colors: ['#c7f6ff', '#a2c5d1', '#ed985f'], mount: `${held}; the bag hangs below the hand` },
120
+ megaphone: { name: 'Megaphone', feature: 'White cone angled up and out with a role-color rim', colors: ['#ffffff', '#3161f7', '#f6b500'], mount: `${held}; the bell rises beside the body` },
121
+ parrot: { name: 'Parrot', feature: 'Green parrot with a red face and a bobbing head', colors: ['#4fae6a', '#e8654f', '#f6b500'], mount: `${held}; the parrot perches beside the body` },
122
+ 'firefly-jar': { name: 'Firefly jar', feature: 'Glass jar with a gray lid and three blinking fireflies', colors: ['#c7f6ff', '#8b95a5', '#d9ff5a'], mount: held },
123
+ trophy: { name: 'Trophy', feature: 'Gold two-handled cup on a dark base with sparkles and a glow', colors: ['#efc65c', '#c99139', '#3c4658'], mount: held },
124
+ 'walkie-talkie': { name: 'Walkie-talkie', feature: 'White handset with a stubby antenna, role-color grille and yellow knob', colors: ['#ffffff', '#3161f7', '#f6b500'], mount: held },
125
+ potion: { name: 'Potion', feature: 'Round glass flask of magenta liquid with a cork stopper', colors: ['#e05aa8', '#ffffff', '#c49b5e'], mount: `${held}; the flask hangs below the hand` },
126
+ 'genie-lamp': { name: 'Genie lamp', feature: 'Gold oil lamp with a domed lid, curled handle and long spout', colors: ['#efc65c', '#c99139', '#fff3c4'], mount: `${held}; the spout points away from the body` },
127
+ lollipop: { name: 'Lollipop', feature: 'Big pink swirl lollipop on a white stick', colors: ['#f2a0b4', '#e8654f', '#ffffff'], mount: `${held}; the candy rises beside the body` },
128
+ pickaxe: { name: 'Pickaxe', feature: 'Curved steel pick head on a wooden handle with a yellow grip', colors: ['#8b95a5', '#e0ad84', '#f6b500'], mount: `${held}; the head rises beside the body, never across the screen` },
129
+ 'bug-net': { name: 'Bug net', feature: 'Role-color hoop net on a pole with a wriggling beetle inside', colors: ['#ffffff', '#3161f7', '#2b2f3a'], mount: `${held}; the hoop rises beside the body` },
130
+ 'hot-pepper': { name: 'Hot pepper', feature: 'Red chili with a green cap and a flame at its tip', colors: ['#e8654f', '#4fae6a', '#ffb347'], mount: `${held}; the pepper curves up beside the body` },
131
+ 'plasma-globe': { name: 'Plasma globe', feature: 'Dark glass ball on a black base with flickering violet tendrils', colors: ['#1d2433', '#c084fc', '#f5d0fe'], mount: held },
132
+ 'master-key': { name: 'Master key', feature: 'Giant golden key, teeth up, with a sweeping sheen and a glow', colors: ['#efc65c', '#c99139', '#fff3c4'], mount: `${held}; the shaft rises beside the body, never across the screen` },
133
+ 'pet-dragon': { name: 'Pet dragon', feature: 'Small green dragon flapping its wings and puffing fire outward', colors: ['#4fae6a', '#2f7a4f', '#ffb347'], mount: `${held}; the dragon perches beside the body` },
80
134
 
81
135
  cape: { name: 'Cape', feature: 'Crimson cape that flares wide below the body with a lighter lining', colors: ['#b23a48', '#d65a68'], mount: 'Collar sits at the shoulders behind the body; the flares show beside and below the base' },
82
136
  'crescent-moon': { name: 'Crescent moon', feature: 'Yellow crescent rising behind the head with a small star', colors: ['#f6d365', '#fff3c4'], mount: 'Moon shows behind the upper-left of the head' },
137
+ kite: { name: 'Kite', feature: 'Red and yellow diamond kite flying on a string', colors: ['#e8654f', '#f6b500', '#8b95a5'], mount: 'Kite flies above the right shoulder; the string meets behind the body' },
138
+ 'solar-panels': { name: 'Solar panels', feature: 'Two dark-blue cell panels raised on steel masts', colors: ['#2f4a8a', '#5b7fd6', '#8b95a5'], mount: 'Masts bolt in behind both shoulders; the panels tilt outward above the arms' },
139
+ 'power-cord': { name: 'Power cord', feature: 'Dark plug pushed into the body with a coiled cord', colors: ['#3c4658', '#5b6577'], mount: 'Plug seats in the lower-right edge of the base; the cord coils beside it' },
140
+ surfboard: { name: 'Surfboard', feature: 'Long yellow board with a red stripe', colors: ['#f3cf62', '#e8654f'], mount: 'Board leans behind the right side and shows above and below the arm' },
83
141
  jetpack: { name: 'Jetpack', feature: 'Two steel tanks with red caps and flickering flames', colors: ['#8b95a5', '#e8654f', '#ffb347'], mount: 'Tanks show beside both arms behind the body; flames flicker below' },
84
142
  'star-trail': { name: 'Star trail', feature: 'Three gold four-point stars twinkling beside the body', colors: ['#f6b500', '#fff3c4'], mount: 'Stars float beside the body and twinkle at full size' },
85
143
  balloons: { name: 'Balloons', feature: 'Three balloons on strings rising beside the head', colors: ['#e8654f', '#f6b500', '#4f7cf6'], mount: 'Strings meet behind the left shoulder; the bunch bobs at full size' },
144
+ 'fox-tail': { name: 'Fox tail', feature: 'Bushy orange tail with a cream tip that sways', colors: ['#ed985f', '#c96c43', '#fff1dc'], mount: 'Root emerges from behind the lower-right edge; the tail curls up beside the arm' },
145
+ 'hero-scarf': { name: 'Hero scarf', feature: 'Red scarf whose two forked tails stream and wave', colors: ['#e8654f', '#b94f50'], mount: 'Knot sits behind the left shoulder; the tails stream beside the arm' },
146
+ guitar: { name: 'Guitar', feature: 'Acoustic guitar slung on a strap', colors: ['#e0ad84', '#8b5a2b', '#f6d365'], mount: 'Body shows at the lower left; the neck rises above the right shoulder' },
86
147
  'bat-wings': { name: 'Bat wings', feature: 'Two scalloped violet wings with darker membranes that flap', colors: ['#5b3a8c', '#3d2563'], mount: 'Wings spread from behind the shoulders and stay clear of the head' },
87
148
  'butterfly-wings': { name: 'Butterfly wings', feature: 'Two pink two-lobed wings with cream inner planes that flap', colors: ['#f2a0b4', '#fff3c4'], mount: 'Wings spread from behind the shoulders' },
149
+ comet: { name: 'Comet', feature: 'Gold comet curling a periwinkle tail around the head, softly glowing', colors: ['#f6b500', '#b9c4ff', '#fff3c4'], mount: 'Head passes the right shoulder; the tail curls in behind the antenna or hat' },
88
150
  halo: { name: 'Halo', feature: 'Flat golden aureole centered on the antenna light with a breathing glow', colors: ['#f6b500', '#ffe08a', '#fff7dc'], mount: 'Ring sits behind the light and hats; the light disc covers its center' },
89
151
  'angel-wings': { name: 'Angel wings', feature: 'Two white feathered wings with a soft glow and a slow flap', colors: ['#ffffff', '#eef1ff', '#c9d2ff'], mount: 'Wings spread from behind the shoulders' },
90
152
  'sun-rays': { name: 'Sun rays', feature: 'Twelve alternating gold rays turning slowly behind the body', colors: ['#f6b500', '#ffcf11'], mount: 'Rays radiate from the body center' },
153
+ 'koi-orbit': { name: 'Koi orbit', feature: 'Two orange koi swimming circles in front of and behind the robot', colors: ['#ed985f', '#fff4ef', '#e8654f'], mount: 'Koi circle low around the body; they pass in front below the screen' },
91
154
  'orbit-ring': { name: 'Orbit ring', feature: 'Tilted orbit belt with three bodies that circle in front of and behind the robot', colors: ['#b9c4ff', '#f6b500', '#ff9ad9'], mount: 'Belt crosses low around the body; bodies pass in front below the screen' },
92
155
  'dragon-wings': { name: 'Dragon wings', feature: 'Two large crimson wings with dark membranes and ember tips that flap', colors: ['#b23a48', '#7f2634', '#ffb347'], mount: 'Wings spread wide from behind the shoulders' },
156
+ 'twin-blades': { name: 'Twin blades', feature: 'Black and pale teal swords crossed, edges glowing with a sheen', colors: ['#1f2330', '#8fdccf', '#9fb2ff'], mount: 'Hilts rise from clasps above both shoulders; the blades cross behind and the tips show below the lower corners' },
157
+ 'peeking-cat': { name: 'Peeking cat', feature: 'Orange tabby head with pointed ears peering over the shoulder', colors: ['#ed985f', '#c96c43', '#fff1dc'], mount: 'Head rises from behind the upper-right corner; the chin stays hidden behind the outline' },
158
+ 'donut-floatie': { name: 'Donut floatie', feature: 'Pink-iced donut swim ring that bobs around the body', colors: ['#dbab70', '#f2a0b4', '#ffffff'], mount: 'Ring circles the base; the far half passes behind the body and the near half in front below the screen' },
159
+ ladybug: { name: 'Ladybug', feature: 'Red spotted ladybug perched over the left shoulder', colors: ['#e8654f', '#2b2f3a'], mount: 'Shell rests on the upper-left corner behind the outline; head and feelers point up' },
160
+ 'dragon-tail': { name: 'Dragon tail', feature: 'Crimson tail with dark spines and a spade tip that sways', colors: ['#b23a48', '#7f2634'], mount: 'Root emerges from behind the lower-left edge; the tail curls up beside the arm' },
161
+ 'drone-buddy': { name: 'Drone buddy', feature: 'White quadcopter with a camera eye and spinning rotors', colors: ['#f4f6fb', '#3c4658', '#4de3ff'], mount: 'Hovers above the left shoulder on a cable that meets behind the body' },
162
+ 'shade-tree': { name: 'Shade tree', feature: 'Round leafy canopy on a short trunk, leaves gently swaying', colors: ['#6fbf73', '#3f8f4f', '#805840'], mount: 'Trunk rises from behind the top edge; the canopy spreads behind the antenna or hat' },
163
+ 'tesla-coils': { name: 'Tesla coils', feature: 'Two coil towers with ring tops and a crackling arc between them', colors: ['#c9d2ff', '#8b95a5', '#4de3ff'], mount: 'Towers stand on struts behind both shoulders; the arc bridges their tops above the head' },
164
+ 'great-wave': { name: 'Great wave', feature: 'Prussian-blue wave whose clawed curl breaks toward the robot', colors: ['#1e3f8f', '#5fb3ec', '#fff8e6'], mount: 'Swell carries the base; the curl rises beside the left arm' },
165
+ 'cloud-dragon': { name: 'Cloud dragon', feature: 'Front-facing jade dragon riding auspicious clouds; its tail whips and its eyes glow', colors: ['#2f9e7a', '#a7e8cf', '#f6b500'], mount: 'Head over the left shoulder with a foreleg hooked on it; the body arches behind the antenna and down the right side, with one cloud in front and two behind' },
93
166
 
94
167
  stripes: { name: 'Stripes', feature: 'Diagonal light stripes across the body and arms', colors: ['#ffffff'], mount: 'One continuous overlay across the body and arms; reads through every palette' },
168
+ split: { name: 'Split', feature: 'Two-tone body with a lighter right half', colors: ['#ffffff'], mount: 'One overlay across the body and arms; each arm keeps its half in every pose' },
169
+ hoops: { name: 'Hoops', feature: 'Wide light bands ringing the body and arms', colors: ['#ffffff'], mount: 'Bands stay level on the body; a raised arm crosses them at an angle' },
170
+ sunburst: { name: 'Sunburst', feature: 'Light rays fanning out from the screen across the rim', colors: ['#ffffff'], mount: 'Rays radiate from the screen center over the body and arms' },
171
+ dipped: { name: 'Dipped', feature: 'Lower body dipped in a deeper shade of its color', colors: ['#0b1020'], mount: 'The dip line runs level across the lower arms; a raised arm lifts out of it' },
172
+ pinwheel: { name: 'Pinwheel', feature: 'Four broad light blades slowly turning around the screen', colors: ['#ffffff'], mount: 'Blades turn about the screen center over the body and arms' },
173
+ quartered: { name: 'Quartered', feature: 'Opposite quarters of the body in a lighter shade', colors: ['#ffffff'], mount: 'Quarters meet at the screen center; each arm is half light, half base' },
174
+ bowtie: { name: 'Bowtie', feature: 'Light wedges spreading from the screen to both arms', colors: ['#ffffff'], mount: 'Wedges meet at the screen center; the arms stay light in every pose' },
175
+ sonar: { name: 'Sonar', feature: 'Rings of light pulsing outward from the screen', colors: ['#ffffff'], mount: 'Rings are centered on the screen and reach the arms last' },
176
+ tide: { name: 'Tide', feature: 'A deeper shade whose wavy waterline rises and ebbs up the body', colors: ['#0b1020'], mount: 'Waves cross both lower arms at rest and climb the body as the tide rises' },
95
177
  chrome: { name: 'Chrome', feature: 'Mirror steel with a horizon line and a sweeping sheen', colors: ['#f4f7fb', '#9aa6b8', '#3c4658'], mount: 'Replaces the body and arm paint; face marks keep the palette' },
96
178
  gold: { name: 'Gold', feature: 'Polished gold with a sweeping sheen', colors: ['#f6d365', '#b8862c', '#fff3c4'], mount: 'Replaces the body and arm paint; face marks keep the palette' },
97
179
  lava: { name: 'Lava', feature: 'Molten rock whose glow flows between ember red and bright orange', colors: ['#e8451f', '#ffb347', '#4a1208'], mount: 'Replaces the body and arm paint; face marks keep the palette' },
98
180
  holographic: { name: 'Holographic', feature: 'Iridescent foil whose colors flow across the body', colors: ['#ff8ad4', '#8ad8ff', '#c9ff8a'], mount: 'Replaces the body and arm paint; face marks keep the palette' },
99
181
  galaxy: { name: 'Galaxy', feature: 'Deep space nebula that drifts between violet and blue with twinkling stars', colors: ['#4c1d95', '#2563eb', '#ffffff'], mount: 'Replaces the body and arm paint; stars twinkle on the surface' },
182
+ bumblebee: { name: 'Bumblebee', feature: 'Warm yellow and charcoal bands across the body', colors: ['#f6c343', '#2b2f3a'], mount: 'Replaces the body and arm paint; face marks keep the palette' },
183
+ aurora: { name: 'Aurora', feature: 'Night sky whose green and violet light ripples across the body', colors: ['#1fbf8f', '#5eead4', '#7c6cf0'], mount: 'Replaces the body and arm paint; face marks keep the palette' },
184
+ terminal: { name: 'Terminal', feature: 'Dark console casing with a phosphor scan line rolling down', colors: ['#111a22', '#7dff8a'], mount: 'Replaces the body and arm paint; the scan line rolls over the surface' },
185
+ patina: { name: 'Copper patina', feature: 'Aged copper with verdigris that creeps up from below', colors: ['#b8733f', '#58b39a', '#f2b27a'], mount: 'Replaces the body and arm paint; face marks keep the palette' },
186
+ thermal: { name: 'Thermal', feature: 'False-color heat that runs hot while the agent reasons', colors: ['#5b1fa8', '#c02a6b', '#ff7a1f'], mount: 'Replaces the body and arm paint; face marks keep the palette' },
187
+ candy: { name: 'Candy', feature: 'Deep candy-apple red with a glossy sheen', colors: ['#c8102e', '#ff4d63', '#2a0309'], mount: 'Replaces the body and arm paint; face marks keep the palette' },
188
+ racer: { name: 'Racer', feature: 'Powder blue with a marigold racing band', colors: ['#66a9d8', '#f28a1c'], mount: 'Replaces the body and arm paint; the band runs level across the lower arms' },
189
+ prism: { name: 'Prism', feature: 'Smoked glass that splits light into a sliding rainbow', colors: ['#4f5580', '#ff5f6d', '#ffe066', '#4fb4ff'], mount: 'Replaces the body and arm paint; the rainbow sweeps across the surface' },
190
+ void: { name: 'Void', feature: 'Black body circled by a glowing accretion arc', colors: ['#030206', '#6a3fd0', '#ffb04a'], mount: 'Replaces the body and arm paint; the arc circles the screen over the rim and arms' },
100
191
  }
101
192
 
102
193
  export const decorations: Decoration[] = items.map(item => ({ ...item, ...copy[item.id] }))
@@ -11,14 +11,17 @@ export const decorationMotionProfiles: Record<HeadId, MotionProfile> = {
11
11
  'top-hat': lift(), 'wizard-hat': lift(), 'chef-hat': lift(), 'party-hat': lift(), crown: lift(),
12
12
  'hard-hat': fitted(), nightcap: fitted(493), 'straw-hat': lift(), 'ringed-planet': { ...lift(512, 335), shadow: 110 },
13
13
  'cowboy-hat': lift(), 'graduation-cap': lift(), 'pirate-hat': lift(), lightning: { ...lift(), shadow: 40 }, ufo: { ...lift(512, 300), shadow: 120 },
14
- 'cat-ears': fitted(), 'shark-fin': fitted(), 'propeller-cap': fitted(), 'flower-crown': fitted(), 'crystal-cluster': fitted(),
14
+ 'floppy-disk': lift(), rocket: { ...lift(), shadow: 60 },
15
+ 'cat-ears': fitted(), 'shark-fin': fitted(), 'propeller-cap': fitted(), 'flower-crown': fitted(), 'crystal-cluster': fitted(), 'mini-volcano': fitted(),
15
16
  mushroom: bounce(), 'ice-cream': bounce(),
16
- poop: bounce(), 'rubber-duck': bounce(520), donut: { ...bounce(), shadow: 40 },
17
+ poop: bounce(), 'rubber-duck': bounce(520), 'phoenix-perch': bounce(522), donut: { ...bounce(), shadow: 40 },
17
18
  banana: { kind: 'rock', x: 487, y: 402, height: 0, angle: 5, shadow: 0 },
18
19
  'paper-boat': { kind: 'rock', x: 512, y: 399, height: 0, angle: 5, shadow: 0 },
19
20
  'traffic-cone': { kind: 'rock', x: 512, y: 399, height: 0, angle: 2.5, shadow: 0 },
20
21
  'fried-egg': { kind: 'squish', x: 512, y: 400, height: 0, angle: 0, shadow: 0 },
21
22
  sprout: { kind: 'sprout', x: 512, y: 399, height: 0, angle: 6, shadow: 0 },
23
+ 'satellite-dish': fitted(), 'cursor-arrow': bounce(540), 'viking-helmet': fitted(), 'wind-up-key': fitted(),
24
+ crab: bounce(), cupcake: bounce(), gears: fitted(), 'rain-cloud': { ...lift(512, 290), shadow: 110 }, 'maneki-neko': lift(),
22
25
  }
23
26
  export const motionKindLabels: Record<MotionKind, string> = {
24
27
  lift: 'Gentle lift', fitted: 'Fitted sway', bounce: 'Bounce and settle', rock: 'Rocking response', squish: 'Soft wobble', sprout: 'Nod and unfurl',
package/src/effects.css CHANGED
@@ -1,11 +1,15 @@
1
1
  .dca-robot[data-effects='off'] .dca-fx { display: none; }
2
2
 
3
3
  .dca-fx-sheen { transform: translateX(120px); }
4
+ .dca-fx-prism { transform: translateX(-40px); }
5
+ .dca-fx-scanline { transform: translateY(260px); }
4
6
  .dca-fx-orbit-a { transform: translate(450px, 0); }
5
7
  .dca-fx-orbit-b { transform: translate(-450px, 0); }
6
8
  .dca-fx-orbit-c { transform: translate(0, 104px); }
7
9
  .dca-fx-orbit-back { opacity: 0; }
8
10
  .dca-fx-sparkle > path { transform-box: fill-box; transform-origin: center; }
11
+ .dca-fx-spray { opacity: 0; transform-box: fill-box; transform-origin: center; }
12
+ .dca-fx-eye-flare { opacity: .25; }
9
13
 
10
14
  @keyframes dca-fx-pulse { 0%, 100% { opacity: .5; } 50% { opacity: 1; } }
11
15
  @keyframes dca-fx-flare { 0%, 100% { opacity: .8; } 12% { opacity: 1; } 20% { opacity: .7; } 28% { opacity: 1; } 60% { opacity: .9; } }
@@ -21,22 +25,54 @@
21
25
  @keyframes dca-fx-hover { 0% { transform: translateY(0); } 100% { transform: translateY(-14px); } }
22
26
  @keyframes dca-fx-wave { 0% { transform: skewY(-6deg); } 100% { transform: skewY(6deg); } }
23
27
  @keyframes dca-fx-rotate { to { transform: rotate(360deg); } }
28
+ @keyframes dca-fx-void-hot { from { transform: rotate(0deg); fill: #9fdcff; } to { transform: rotate(360deg); fill: #9fdcff; } }
29
+ @keyframes dca-fx-sonar {
30
+ 0% { transform: scale(1); } 25% { transform: scale(1.157); } 50% { transform: scale(1.339); } 75% { transform: scale(1.549); } 100% { transform: scale(1.793); }
31
+ }
32
+ @keyframes dca-fx-tide { 0% { transform: translateY(70px); } 100% { transform: translateY(-150px); } }
24
33
  @keyframes dca-fx-lava {
25
34
  0%, 100% { stop-color: #4a1208; } 20% { stop-color: #e8451f; } 40% { stop-color: #ffb347; } 60% { stop-color: #ff6a2a; } 80% { stop-color: #7a1f0e; }
26
35
  }
27
36
  @keyframes dca-fx-galaxy {
28
37
  0%, 100% { stop-color: #1b1040; } 20% { stop-color: #4c1d95; } 40% { stop-color: #2563eb; } 60% { stop-color: #0b3d62; } 80% { stop-color: #7c3aed; }
29
38
  }
39
+ @keyframes dca-fx-aurora {
40
+ 0%, 100% { stop-color: #0a1f33; } 20% { stop-color: #1fbf8f; } 40% { stop-color: #5eead4; } 60% { stop-color: #7c6cf0; } 80% { stop-color: #122046; }
41
+ }
42
+ @keyframes dca-fx-thermal { 0%, 100% { stop-color: #1a0b5e; } 25% { stop-color: #5b1fa8; } 50% { stop-color: #c02a6b; } 75% { stop-color: #3b1488; } }
43
+ @keyframes dca-fx-thermal-hot { 0%, 100% { stop-color: #c02a6b; } 25% { stop-color: #ff7a1f; } 50% { stop-color: #ffb020; } 75% { stop-color: #ffd23f; } }
44
+ @keyframes dca-fx-patina { to { stop-color: #58b39a; } }
45
+ @keyframes dca-fx-scanline { 0% { transform: translateY(0); } 100% { transform: translateY(560px); } }
30
46
  @keyframes dca-fx-scan { 0% { transform: translateX(-30px); } 100% { transform: translateX(290px); } }
31
47
  @keyframes dca-fx-steam { 0% { opacity: 0; transform: translateY(12px); } 35% { opacity: .8; } 100% { opacity: 0; transform: translateY(-28px); } }
32
48
  @keyframes dca-fx-sparkle { 0%, 100% { opacity: .15; transform: scale(.55); } 50% { opacity: 1; transform: scale(1.15); } }
33
49
  @keyframes dca-fx-flame { 0% { transform: scaleY(.82) scaleX(.94); opacity: .85; } 100% { transform: scaleY(1.12) scaleX(1.04); opacity: 1; } }
34
50
  @keyframes dca-fx-node { 0%, 100% { opacity: .3; } 50% { opacity: 1; } }
35
51
  @keyframes dca-fx-sheen { 0% { transform: translateX(-440px); } 55% { transform: translateX(740px); } 100% { transform: translateX(740px); } }
52
+ @keyframes dca-fx-prism-echo { 0%, 45% { transform: translateX(-440px); } 100% { transform: translateX(740px); } }
36
53
  @keyframes dca-fx-holo {
37
54
  0% { stop-color: #ff8ad4; } 20% { stop-color: #ffd28a; } 40% { stop-color: #c9ff8a; }
38
55
  60% { stop-color: #8ad8ff; } 80% { stop-color: #c98aff; } 100% { stop-color: #ff8ad4; }
39
56
  }
57
+ @keyframes dca-fx-lean { 0%, 100% { transform: rotate(-2.4deg); } 50% { transform: rotate(3.2deg); } }
58
+ @keyframes dca-fx-swell { 0%, 100% { transform: translateY(10px) rotate(1.2deg); } 50% { transform: translateY(-10px) rotate(-1.2deg); } }
59
+ @keyframes dca-fx-reach { 0%, 100% { transform: scale(.8); } 40% { transform: scale(1.05); } }
60
+ @keyframes dca-fx-foam { 0%, 100% { opacity: .55; } 40% { opacity: 1; } }
61
+ @keyframes dca-fx-spray { 0% { opacity: 0; transform: translate(0, 10px) scale(.4); } 25% { opacity: 1; transform: translate(10px, -12px) scale(1); } 100% { opacity: 0; transform: translate(44px, -76px) scale(.2); } }
62
+ @keyframes dca-fx-coil { 0%, 100% { transform: rotate(calc(var(--dca-coil) * -1deg)); } 50% { transform: rotate(calc(var(--dca-coil) * 1deg)); } }
63
+ @keyframes dca-fx-whisker { 0%, 100% { transform: rotate(-5deg); } 50% { transform: rotate(6deg); } }
64
+ @keyframes dca-fx-whisker-tip { 0%, 100% { transform: rotate(-9deg); } 50% { transform: rotate(10deg); } }
65
+ @keyframes dca-fx-frill-l { 0% { transform: rotate(-3deg); } 100% { transform: rotate(3deg); } }
66
+ @keyframes dca-fx-frill-r { 0% { transform: rotate(3deg); } 100% { transform: rotate(-3deg); } }
67
+ @keyframes dca-fx-frill-flare { 0%, 24%, 50%, 100% { transform: scale(1); } 30% { transform: scale(1.1); } 40% { transform: scale(1.06); } }
68
+ @keyframes dca-fx-rear {
69
+ 0%, 100% { transform: translate(0, 0) rotate(0deg) scale(1); } 12% { transform: translate(0, -6px) rotate(-1.2deg) scale(1); }
70
+ 23% { transform: translate(0, 4px) rotate(.8deg) scale(.99); } 30% { transform: translate(0, -12px) rotate(-2deg) scale(1.05); }
71
+ 40% { transform: translate(0, -9px) rotate(-1.6deg) scale(1.04); } 54% { transform: translate(0, 0) rotate(0deg) scale(1); }
72
+ 77% { transform: translate(0, -6px) rotate(-1deg) scale(1); }
73
+ }
74
+ @keyframes dca-fx-eye-flare { 0%, 22%, 54%, 100% { opacity: .25; } 28%, 42% { opacity: 1; } }
75
+ @keyframes dca-fx-drift { 0% { transform: translateX(-10px); } 100% { transform: translateX(10px); } }
40
76
  @keyframes dca-fx-orbit {
41
77
  0% { transform: translate(450px, 0); } 8.33% { transform: translate(390px, 52px); } 16.67% { transform: translate(225px, 90px); }
42
78
  25% { transform: translate(0, 104px); } 33.33% { transform: translate(-225px, 90px); } 41.67% { transform: translate(-390px, 52px); }
@@ -62,13 +98,23 @@
62
98
  .dca-robot[data-effects='live'] .dca-fx-hover { animation: dca-fx-hover 2.2s ease-in-out infinite alternate; }
63
99
  .dca-robot[data-effects='live'] .dca-fx-wave { animation: dca-fx-wave 1.1s ease-in-out infinite alternate; }
64
100
  .dca-robot[data-effects='live'] .dca-fx-rotate { animation: dca-fx-rotate 28s linear infinite; }
101
+ .dca-robot[data-effects='live'] .dca-fx-rotate-fast { animation: dca-fx-rotate 6s linear infinite; }
102
+ .dca-robot[data-effects='live'] .dca-fx-spin-flat-slow { animation: dca-fx-spin-flat 2.4s linear infinite; }
65
103
  .dca-robot[data-effects='live'] .dca-fx-flap-slow { animation: dca-fx-flap 2.6s ease-in-out infinite alternate; }
66
104
  .dca-robot[data-effects='live'] .dca-skin-lava stop { animation: dca-fx-lava 7s ease-in-out infinite; }
67
105
  .dca-robot[data-effects='live'] .dca-skin-galaxy stop { animation: dca-fx-galaxy 9s ease-in-out infinite; }
68
- .dca-robot[data-effects='live'] :is(.dca-skin-lava, .dca-skin-galaxy) stop:nth-child(2) { animation-delay: -1.4s; }
69
- .dca-robot[data-effects='live'] :is(.dca-skin-lava, .dca-skin-galaxy) stop:nth-child(3) { animation-delay: -2.8s; }
70
- .dca-robot[data-effects='live'] :is(.dca-skin-lava, .dca-skin-galaxy) stop:nth-child(4) { animation-delay: -4.2s; }
71
- .dca-robot[data-effects='live'] :is(.dca-skin-lava, .dca-skin-galaxy) stop:nth-child(5) { animation-delay: -5.6s; }
106
+ .dca-robot[data-effects='live'] .dca-skin-aurora stop { animation: dca-fx-aurora 8s ease-in-out infinite; }
107
+ .dca-robot[data-effects='live'] .dca-skin-thermal stop { animation: dca-fx-thermal 8s ease-in-out infinite; }
108
+ .dca-robot[data-effects='live'] :is(.dca-skin-lava, .dca-skin-galaxy, .dca-skin-aurora, .dca-skin-thermal) stop:nth-child(2) { animation-delay: -1.4s; }
109
+ .dca-robot[data-effects='live'] :is(.dca-skin-lava, .dca-skin-galaxy, .dca-skin-aurora, .dca-skin-thermal) stop:nth-child(3) { animation-delay: -2.8s; }
110
+ .dca-robot[data-effects='live'] :is(.dca-skin-lava, .dca-skin-galaxy, .dca-skin-aurora, .dca-skin-thermal) stop:nth-child(4) { animation-delay: -4.2s; }
111
+ .dca-robot[data-effects='live'] :is(.dca-skin-lava, .dca-skin-galaxy, .dca-skin-aurora, .dca-skin-thermal) stop:nth-child(5) { animation-delay: -5.6s; }
112
+ .dca-robot[data-effects='live'] .dca-skin-patina stop:nth-child(3) { animation: dca-fx-patina 12s ease-in-out infinite alternate; }
113
+ .dca-robot[data-effects='live'] .dca-fx-pinwheel { animation: dca-fx-rotate 24s linear infinite; }
114
+ .dca-robot[data-effects='live'] .dca-fx-sonar { animation: dca-fx-sonar 3.6s linear infinite; }
115
+ .dca-robot[data-effects='live'] .dca-fx-tide { animation: dca-fx-tide 7s ease-in-out infinite alternate; }
116
+ .dca-robot[data-effects='live'] .dca-fx-void { animation: dca-fx-rotate 12s linear infinite; }
117
+ .dca-robot[data-effects='live'] .dca-fx-scanline { animation: dca-fx-scanline 3.6s linear infinite; }
72
118
  .dca-robot[data-effects='live'] .dca-fx-scan { animation: dca-fx-scan 2.4s cubic-bezier(.45, 0, .55, 1) infinite alternate; }
73
119
  .dca-robot[data-effects='live'] .dca-fx-steam { animation: dca-fx-steam 2.6s ease-out infinite; }
74
120
  .dca-robot[data-effects='live'] .dca-fx-sparkle > path { animation: dca-fx-sparkle 1.8s ease-in-out infinite; }
@@ -77,8 +123,23 @@
77
123
  .dca-robot[data-effects='live'] .dca-fx-flame { animation: dca-fx-flame .42s ease-in-out infinite alternate; }
78
124
  .dca-robot[data-effects='live'] .dca-fx-node { animation: dca-fx-node 1.8s ease-in-out infinite; }
79
125
  .dca-robot[data-effects='live'] .dca-fx-sheen { animation: dca-fx-sheen 4.8s cubic-bezier(.5, .05, .5, .95) infinite; }
126
+ .dca-robot[data-effects='live'] .dca-fx-prism-echo { animation-name: dca-fx-prism-echo; }
80
127
  .dca-robot[data-effects='live'] .dca-fx-orbit-front { animation: dca-fx-orbit 8s linear infinite, dca-fx-orbit-show-front 8s linear infinite; }
81
128
  .dca-robot[data-effects='live'] .dca-fx-orbit-back { animation: dca-fx-orbit 8s linear infinite, dca-fx-orbit-show-back 8s linear infinite; }
129
+ .dca-robot[data-effects='live'] .dca-fx-lean { animation: dca-fx-lean 8.4s ease-in-out infinite; }
130
+ .dca-robot[data-effects='live'] .dca-fx-swell { animation: dca-fx-swell 6s ease-in-out infinite; }
131
+ .dca-robot[data-effects='live'] .dca-fx-reach { animation: dca-fx-reach 5.6s ease-in-out infinite; }
132
+ .dca-robot[data-effects='live'] .dca-fx-foam { animation: dca-fx-foam 5.6s ease-in-out infinite; animation-delay: inherit; }
133
+ .dca-robot[data-effects='live'] .dca-fx-spray { animation: dca-fx-spray 4.8s ease-out infinite; }
134
+ .dca-robot[data-effects='live'] .dca-fx-coil { animation: dca-fx-coil 3.6s ease-in-out infinite; }
135
+ .dca-robot[data-effects='live'] .dca-fx-whisker { animation: dca-fx-whisker 3.6s ease-in-out infinite; }
136
+ .dca-robot[data-effects='live'] .dca-fx-whisker-tip { animation: dca-fx-whisker-tip 3.6s ease-in-out infinite; }
137
+ .dca-robot[data-effects='live'] .dca-fx-frill-l { animation: dca-fx-frill-l 1.8s ease-in-out infinite alternate; }
138
+ .dca-robot[data-effects='live'] .dca-fx-frill-r { animation: dca-fx-frill-r 1.8s ease-in-out infinite alternate; }
139
+ .dca-robot[data-effects='live'] .dca-fx-frill-flare { animation: dca-fx-frill-flare 7.2s ease-in-out infinite; }
140
+ .dca-robot[data-effects='live'] .dca-fx-rear { animation: dca-fx-rear 7.2s ease-in-out infinite; }
141
+ .dca-robot[data-effects='live'] .dca-fx-eye-flare { animation: dca-fx-eye-flare 7.2s ease-in-out infinite; }
142
+ .dca-robot[data-effects='live'] .dca-fx-drift { animation: dca-fx-drift 6s ease-in-out infinite alternate; }
82
143
  .dca-robot[data-effects='live'] .dca-skin-holo stop { animation: dca-fx-holo 6s linear infinite; }
83
144
  .dca-robot[data-effects='live'] .dca-skin-holo stop:nth-child(2) { animation-delay: -1.2s; }
84
145
  .dca-robot[data-effects='live'] .dca-skin-holo stop:nth-child(3) { animation-delay: -2.4s; }
package/src/items.ts CHANGED
@@ -21,6 +21,8 @@ const object = <I extends string>(id: I, rarity: Rarity, series: Series) =>
21
21
  ({ id, slot: 'head', rarity, series, zones: ['top'] }) as const satisfies ItemSpec<'head', I>
22
22
  const brow = <I extends string>(id: I, rarity: Rarity, series: Series) =>
23
23
  ({ id, slot: 'face', rarity, series, zones: ['brow'] }) as const satisfies ItemSpec<'face', I>
24
+ const rim = <I extends string>(id: I, rarity: Rarity, series: Series) =>
25
+ ({ id, slot: 'face', rarity, series, zones: ['rim'] }) as const satisfies ItemSpec<'face', I>
24
26
  const faceplate = <I extends string>(id: I, rarity: Rarity, series: Series) =>
25
27
  ({ id, slot: 'face', rarity, series, zones: ['screen'] }) as const satisfies ItemSpec<'face', I>
26
28
  const hand = <I extends string>(id: I, rarity: Rarity, series: Series) =>
@@ -30,7 +32,7 @@ const back = <I extends string>(id: I, rarity: Rarity, series: Series) =>
30
32
  const skin = <I extends string>(id: I, rarity: Rarity, series: Series) =>
31
33
  ({ id, slot: 'skin', rarity, series, zones: ['body'] }) as const satisfies ItemSpec<'skin', I>
32
34
 
33
- // Registry order is presentation order only; sampling is weighted by rarity, then uniform.
35
+ // Registry order is presentation order and also decides which item a name draws within its rarity.
34
36
  export const items = [
35
37
  hat('baseball-cap', 'common'), hat('bucket-hat', 'common'), hat('beret', 'uncommon'), hat('beanie', 'common'),
36
38
  hat('top-hat', 'rare'), hat('wizard-hat', 'rare', 'fantasy'), hat('chef-hat', 'uncommon', 'snack'), hat('party-hat', 'uncommon'),
@@ -40,28 +42,56 @@ export const items = [
40
42
  object('sprout', 'common', 'nature'), object('donut', 'uncommon', 'snack'), object('ringed-planet', 'legendary', 'tech'),
41
43
  object('cat-ears', 'common', 'critters'), hat('cowboy-hat', 'common'), object('mushroom', 'common', 'nature'),
42
44
  object('shark-fin', 'uncommon', 'critters'), hat('propeller-cap', 'uncommon'), hat('graduation-cap', 'uncommon', 'dev'),
43
- object('ice-cream', 'uncommon', 'snack'), object('flower-crown', 'rare', 'nature'), hat('pirate-hat', 'rare', 'fantasy'),
44
- object('lightning', 'epic', 'tech'), object('crystal-cluster', 'epic', 'fantasy'), object('ufo', 'legendary', 'tech'),
45
+ object('ice-cream', 'uncommon', 'snack'), object('floppy-disk', 'uncommon', 'dev'), object('flower-crown', 'rare', 'nature'), hat('pirate-hat', 'rare', 'fantasy'),
46
+ object('rocket', 'rare', 'dev'), object('lightning', 'epic', 'tech'), object('crystal-cluster', 'epic', 'fantasy'), object('ufo', 'legendary', 'tech'),
47
+ object('mini-volcano', 'legendary', 'nature'), object('phoenix-perch', 'legendary', 'fantasy'),
48
+ object('satellite-dish', 'common', 'tech'), object('cursor-arrow', 'common', 'dev'), hat('viking-helmet', 'uncommon', 'fantasy'),
49
+ object('wind-up-key', 'rare', 'tech'), object('crab', 'rare', 'critters'), object('cupcake', 'rare', 'snack'),
50
+ object('gears', 'epic', 'dev'), object('rain-cloud', 'epic', 'nature'), object('maneki-neko', 'legendary', 'critters'),
45
51
 
46
- brow('shades', 'common', 'everyday'), brow('forehead-goggles', 'uncommon', 'tech'),
47
- faceplate('mecha-faceplate', 'rare', 'tech'), faceplate('pixel-screen', 'rare', 'dev'),
48
- faceplate('gold-faceplate', 'epic', 'tech'), faceplate('neon-visor', 'legendary', 'tech'),
52
+ brow('shades', 'common', 'everyday'), brow('headlamp', 'common', 'nature'),
53
+ brow('forehead-goggles', 'uncommon', 'tech'), brow('heart-glasses', 'uncommon', 'everyday'), { ...brow('snorkel-mask', 'uncommon', 'nature'), zones: ['brow', 'rim'] },
54
+ brow('watermelon-glasses', 'uncommon', 'snack'), brow('star-glasses', 'rare', 'everyday'),
55
+ rim('bow-tie', 'common', 'everyday'), rim('neckerchief', 'common', 'everyday'), rim('ear-pencil', 'common', 'dev'), rim('sticky-note', 'common', 'everyday'),
56
+ rim('bell-collar', 'uncommon', 'critters'), rim('lanyard-badge', 'uncommon', 'dev'), rim('prize-rosette', 'uncommon', 'everyday'), rim('cherry-charm', 'uncommon', 'snack'),
57
+ rim('songbird', 'rare', 'critters'), rim('amulet', 'epic', 'fantasy'),
58
+ faceplate('mecha-faceplate', 'rare', 'tech'), faceplate('pixel-screen', 'rare', 'dev'), faceplate('segment-display', 'rare', 'dev'), faceplate('knight-visor', 'rare', 'fantasy'),
59
+ faceplate('gold-faceplate', 'epic', 'tech'), faceplate('porthole-helmet', 'epic', 'nature'), faceplate('neon-visor', 'legendary', 'tech'), faceplate('dragon-visor', 'legendary', 'fantasy'),
60
+ faceplate('code-rain', 'legendary', 'dev'),
49
61
 
50
62
  hand('task-board', 'common', 'dev'), hand('wrench', 'common', 'dev'), hand('coffee-mug', 'common', 'dev'),
51
63
  hand('paintbrush', 'common', 'everyday'), hand('boba-tea', 'common', 'snack'),
64
+ hand('umbrella', 'common', 'everyday'), hand('pizza-slice', 'common', 'snack'), hand('sunflower', 'common', 'nature'), hand('camera', 'common', 'everyday'),
65
+ hand('walkie-talkie', 'common', 'tech'), hand('potion', 'common', 'fantasy'),
52
66
  hand('shield', 'uncommon', 'dev'), hand('magnifier', 'uncommon', 'dev'), hand('gamepad', 'uncommon', 'dev'), hand('flag', 'uncommon', 'everyday'),
53
- hand('control-panel', 'rare', 'tech'), hand('lantern', 'rare', 'fantasy'),
54
- hand('magic-wand', 'epic', 'fantasy'), hand('staff', 'epic', 'fantasy'), hand('energy-blade', 'legendary', 'tech'),
67
+ hand('binoculars', 'uncommon', 'nature'), hand('goldfish-bag', 'uncommon', 'critters'), hand('megaphone', 'uncommon', 'everyday'),
68
+ hand('genie-lamp', 'uncommon', 'fantasy'), hand('lollipop', 'uncommon', 'snack'),
69
+ hand('control-panel', 'rare', 'tech'), hand('lantern', 'rare', 'fantasy'), hand('parrot', 'rare', 'critters'), hand('firefly-jar', 'rare', 'nature'),
70
+ hand('pickaxe', 'rare', 'everyday'), hand('bug-net', 'rare', 'dev'),
71
+ hand('magic-wand', 'epic', 'fantasy'), hand('staff', 'epic', 'fantasy'), hand('trophy', 'epic', 'everyday'), hand('hot-pepper', 'epic', 'snack'), hand('plasma-globe', 'epic', 'tech'),
72
+ hand('energy-blade', 'legendary', 'tech'), hand('thunder-hammer', 'legendary', 'tech'), hand('master-key', 'legendary', 'dev'), hand('pet-dragon', 'legendary', 'fantasy'),
55
73
 
56
74
  back('cape', 'common', 'fantasy'), back('crescent-moon', 'common', 'nature'),
75
+ back('kite', 'common', 'everyday'), back('solar-panels', 'common', 'tech'), back('power-cord', 'common', 'dev'), back('surfboard', 'common', 'nature'),
76
+ back('peeking-cat', 'common', 'critters'),
57
77
  back('jetpack', 'uncommon', 'tech'), back('star-trail', 'uncommon', 'fantasy'), back('balloons', 'uncommon', 'everyday'),
58
- back('bat-wings', 'rare', 'critters'), back('butterfly-wings', 'rare', 'critters'),
59
- back('halo', 'epic', 'fantasy'), back('angel-wings', 'epic', 'fantasy'), back('sun-rays', 'epic', 'nature'),
60
- back('orbit-ring', 'legendary', 'tech'), back('dragon-wings', 'legendary', 'fantasy'),
78
+ back('fox-tail', 'uncommon', 'critters'), back('hero-scarf', 'uncommon', 'everyday'), back('guitar', 'uncommon', 'everyday'),
79
+ back('donut-floatie', 'uncommon', 'snack'), back('ladybug', 'uncommon', 'critters'),
80
+ back('bat-wings', 'rare', 'critters'), back('butterfly-wings', 'rare', 'critters'), back('comet', 'rare', 'tech'),
81
+ back('dragon-tail', 'rare', 'fantasy'), back('drone-buddy', 'rare', 'tech'), back('shade-tree', 'rare', 'nature'),
82
+ back('halo', 'epic', 'fantasy'), back('angel-wings', 'epic', 'fantasy'), back('sun-rays', 'epic', 'nature'), back('koi-orbit', 'epic', 'critters'),
83
+ back('tesla-coils', 'epic', 'tech'),
84
+ back('orbit-ring', 'legendary', 'tech'), back('dragon-wings', 'legendary', 'fantasy'), back('twin-blades', 'legendary', 'fantasy'),
85
+ back('great-wave', 'legendary', 'nature'), back('cloud-dragon', 'legendary', 'critters'),
61
86
 
62
- skin('stripes', 'common', 'everyday'),
87
+ skin('stripes', 'common', 'everyday'), skin('split', 'common', 'everyday'), skin('hoops', 'common', 'everyday'),
88
+ skin('sunburst', 'uncommon', 'nature'), skin('dipped', 'uncommon', 'everyday'), skin('quartered', 'uncommon', 'everyday'), skin('bowtie', 'uncommon', 'everyday'),
89
+ skin('pinwheel', 'rare', 'everyday'), skin('sonar', 'rare', 'tech'), skin('tide', 'rare', 'nature'),
63
90
  skin('chrome', 'epic', 'tech'), skin('gold', 'epic', 'fantasy'), skin('lava', 'epic', 'nature'),
64
- skin('holographic', 'legendary', 'tech'), skin('galaxy', 'legendary', 'fantasy'),
91
+ skin('bumblebee', 'epic', 'critters'), skin('aurora', 'epic', 'nature'), skin('terminal', 'epic', 'dev'), skin('patina', 'epic', 'nature'),
92
+ skin('candy', 'epic', 'everyday'), skin('racer', 'epic', 'everyday'),
93
+ skin('holographic', 'legendary', 'tech'), skin('galaxy', 'legendary', 'fantasy'), skin('thermal', 'legendary', 'tech'),
94
+ skin('prism', 'legendary', 'tech'), skin('void', 'legendary', 'fantasy'),
65
95
  ] as const
66
96
 
67
97
  export type Item = typeof items[number]
@@ -11,6 +11,15 @@ export const paintMaterials = {
11
11
  gold: { shadow: '#5a3a10', accent: '#f6d365' },
12
12
  lava: { shadow: '#3a0a05', accent: '#ff6a2a' },
13
13
  galaxy: { shadow: '#0b0620', accent: '#7c3aed' },
14
+ bumblebee: { shadow: '#3a2c08', accent: '#f6c343' },
15
+ aurora: { shadow: '#06131f', accent: '#34d399' },
16
+ terminal: { shadow: '#020604', accent: '#7dff8a' },
17
+ patina: { shadow: '#123a33', accent: '#58b39a' },
18
+ thermal: { shadow: '#12072e', accent: '#ff7a1f' },
19
+ candy: { shadow: '#2a0309', accent: '#ff4d63' },
20
+ racer: { shadow: '#1f4f73', accent: '#f28a1c' },
21
+ prism: { shadow: '#151830', accent: '#a276ff' },
22
+ void: { shadow: '#030206', accent: '#8b5cf6' },
14
23
  } as const satisfies Partial<Record<SkinId, PaintMaterial>>
15
24
 
16
25
  export type PaintSkinId = keyof typeof paintMaterials
package/src/spine.ts ADDED
@@ -0,0 +1,61 @@
1
+ export type Pt = readonly [number, number]
2
+
3
+ export const n0 = (v: number) => Math.round(v)
4
+ const cr = (a: number, b: number, c: number, d: number, t: number) => .5 * (2 * b + (c - a) * t + (2 * a - 5 * b + 4 * c - d) * t * t + (3 * b - a - 3 * c + d) * t * t * t)
5
+ const dcr = (a: number, b: number, c: number, d: number, t: number) => .5 * ((c - a) + 2 * (2 * a - 5 * b + 4 * c - d) * t + 3 * (3 * b - a - 3 * c + d) * t * t)
6
+
7
+ export function along(spine: readonly Pt[], t: number) {
8
+ const seg = spine.length - 1, u = Math.min(Math.max(t, 0), 1) * seg, k = Math.min(Math.floor(u), seg - 1), f = u - k
9
+ const p = (i: number) => spine[Math.min(Math.max(i, 0), seg)]
10
+ const a = p(k - 1), b = p(k), c = p(k + 1), d = p(k + 2)
11
+ const tx = dcr(a[0], b[0], c[0], d[0], f), ty = dcr(a[1], b[1], c[1], d[1], f), len = Math.hypot(tx, ty) || 1
12
+ return { x: cr(a[0], b[0], c[0], d[0], f), y: cr(a[1], b[1], c[1], d[1], f), tx: tx / len, ty: ty / len }
13
+ }
14
+
15
+ function smooth(points: Pt[]) {
16
+ const mid = (i: number) => { const a = points[(i + points.length) % points.length], b = points[(i + 1) % points.length]; return `${n0((a[0] + b[0]) / 2)} ${n0((a[1] + b[1]) / 2)}` }
17
+ return `M${mid(-1)}${points.map((p, i) => `Q${n0(p[0])} ${n0(p[1])} ${mid(i)}`).join('')}Z`
18
+ }
19
+
20
+ // A tapered band along a Catmull-Rom spine; offset and share place it across the width (positive = the spine's outer normal).
21
+ export function tube(spine: readonly Pt[], width: (t: number) => number, from = 0, to = 1, offset = 0, share = 1, steps = 36) {
22
+ const left: Pt[] = [], right: Pt[] = []
23
+ for (let i = 0; i <= steps; i++) {
24
+ const t = from + ((to - from) * i) / steps, s = along(spine, t), w = width(t), c = offset * w, h = (share * w) / 2
25
+ left.push([s.x - s.ty * (c + h), s.y + s.tx * (c + h)]); right.push([s.x - s.ty * (c - h), s.y + s.tx * (c - h)])
26
+ }
27
+ return smooth([...left, ...right.reverse()])
28
+ }
29
+
30
+ export function ridge(spine: readonly Pt[], width: (t: number) => number, from: number, to: number, count: number, height: number, side = 1, lean = .5) {
31
+ let d = ''
32
+ for (let i = 0; i < count; i++) {
33
+ const t = from + ((to - from) * (i + .5)) / count, s = along(spine, t), w = width(t), e = w * .4 * side, h = height * w / 100, b = h * .6
34
+ const bx = s.x - s.ty * e, by = s.y + s.tx * e, nx = -s.ty * side, ny = s.tx * side
35
+ d += `M${n0(bx - s.tx * b)} ${n0(by - s.ty * b)}L${n0(bx + nx * h + s.tx * lean * h)} ${n0(by + ny * h + s.ty * lean * h)}L${n0(bx + s.tx * b)} ${n0(by + s.ty * b)}Z`
36
+ }
37
+ return d
38
+ }
39
+
40
+ // A tapered flame lock from (x, y) toward angle; bend curls the tip clockwise when positive.
41
+ export function lock(x: number, y: number, angle: number, len: number, w: number, bend = .4) {
42
+ const a = (angle * Math.PI) / 180, c = Math.cos(a), s = Math.sin(a)
43
+ const at = (u: number, v: number): Pt => [x + u * c - v * s, y + u * s + v * c]
44
+ return tube([at(0, 0), at(len * .35, -bend * len * .1), at(len * .7, bend * len * .06), at(len, bend * len * .32)], t => w * (1 - t) + 3, 0, 1, 0, 1, 14)
45
+ }
46
+
47
+ export function talon(x: number, y: number, angle: number, len: number, w: number, curl = 1) {
48
+ const a = (angle * Math.PI) / 180, c = Math.cos(a), s = Math.sin(a)
49
+ const p = (u: number, v: number) => `${n0(x + u * c - v * curl * s)} ${n0(y + u * s + v * curl * c)}`
50
+ return `M${p(0, -w / 2)}Q${p(len * .7, -w * .6)} ${p(len, w * .35)}Q${p(len * .45, w * .2)} ${p(0, w / 2)}Z`
51
+ }
52
+
53
+ export const oval = (cx: number, cy: number, rx: number, ry = rx) => `M${cx - rx} ${cy}a${rx} ${ry} 0 1 0 ${2 * rx} 0a${rx} ${ry} 0 1 0 ${-2 * rx} 0Z`
54
+
55
+ // Mirrors an absolute path made of M, L, C, Q and Z about x = 0.
56
+ export function mirror(d: string) {
57
+ return d.replace(/([MLCQ])([^MLCQZ]*)/g, (_, cmd: string, args: string) => {
58
+ const nums = args.trim().split(/[\s,]+|(?=-)/).filter(Boolean).map(Number)
59
+ return cmd + nums.map((v, i) => (i % 2 ? v : -v)).join(' ')
60
+ })
61
+ }