@oxy.so/bloom 7.1.4 → 7.1.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 (220) hide show
  1. package/assets/character-runtime/README.md +19 -114
  2. package/assets/character-runtime/legacy-engine.mjs +54 -406
  3. package/assets/character-runtime/legacy-geometry.mjs +85 -242
  4. package/assets/character-runtime/legacy-worker.mjs +23 -82
  5. package/assets/character-runtime/managed-avatar.mjs +18 -12
  6. package/assets/character-runtime/render-budget.mjs +11 -6
  7. package/assets/character-runtime/runtime.mjs +295 -744
  8. package/docs/agent-avatar.mdx +8 -45
  9. package/docs/agent-creator.mdx +3 -13
  10. package/lib/commonjs/agent-avatar/AgentAvatar.js +1 -1
  11. package/lib/commonjs/agent-avatar/AgentAvatar.js.map +1 -1
  12. package/lib/commonjs/agent-avatar/CharacterAvatar.js +1 -2
  13. package/lib/commonjs/agent-avatar/CharacterAvatar.js.map +1 -1
  14. package/lib/commonjs/agent-avatar/config-character.js +0 -4
  15. package/lib/commonjs/agent-avatar/config-character.js.map +1 -1
  16. package/lib/commonjs/agent-avatar/legacy-recipe.js +6 -19
  17. package/lib/commonjs/agent-avatar/legacy-recipe.js.map +1 -1
  18. package/lib/commonjs/agent-creator/AgentCreatorBase.js +107 -67
  19. package/lib/commonjs/agent-creator/AgentCreatorBase.js.map +1 -1
  20. package/lib/commonjs/agent-creator/CharacterControls.js +5 -23
  21. package/lib/commonjs/agent-creator/CharacterControls.js.map +1 -1
  22. package/lib/commonjs/agent-creator/EmotionPicker.js +1 -8
  23. package/lib/commonjs/agent-creator/EmotionPicker.js.map +1 -1
  24. package/lib/commonjs/agent-creator/ShapeSilhouette.js +1 -8
  25. package/lib/commonjs/agent-creator/ShapeSilhouette.js.map +1 -1
  26. package/lib/commonjs/agent-creator/constants.js +17 -16
  27. package/lib/commonjs/agent-creator/constants.js.map +1 -1
  28. package/lib/commonjs/agent-creator/messages.js +1 -1
  29. package/lib/commonjs/agent-creator/messages.js.map +1 -1
  30. package/lib/commonjs/locale/translations/ar.js +1 -1
  31. package/lib/commonjs/locale/translations/ar.js.map +1 -1
  32. package/lib/commonjs/locale/translations/bn.js +1 -1
  33. package/lib/commonjs/locale/translations/bn.js.map +1 -1
  34. package/lib/commonjs/locale/translations/ca.js +1 -1
  35. package/lib/commonjs/locale/translations/ca.js.map +1 -1
  36. package/lib/commonjs/locale/translations/de.js +1 -1
  37. package/lib/commonjs/locale/translations/de.js.map +1 -1
  38. package/lib/commonjs/locale/translations/es.js +1 -1
  39. package/lib/commonjs/locale/translations/es.js.map +1 -1
  40. package/lib/commonjs/locale/translations/fr.js +1 -1
  41. package/lib/commonjs/locale/translations/fr.js.map +1 -1
  42. package/lib/commonjs/locale/translations/hi.js +1 -1
  43. package/lib/commonjs/locale/translations/hi.js.map +1 -1
  44. package/lib/commonjs/locale/translations/id.js +1 -1
  45. package/lib/commonjs/locale/translations/id.js.map +1 -1
  46. package/lib/commonjs/locale/translations/it.js +1 -1
  47. package/lib/commonjs/locale/translations/it.js.map +1 -1
  48. package/lib/commonjs/locale/translations/ja.js +1 -1
  49. package/lib/commonjs/locale/translations/ja.js.map +1 -1
  50. package/lib/commonjs/locale/translations/pt.js +1 -1
  51. package/lib/commonjs/locale/translations/pt.js.map +1 -1
  52. package/lib/commonjs/locale/translations/ru.js +1 -1
  53. package/lib/commonjs/locale/translations/ru.js.map +1 -1
  54. package/lib/commonjs/locale/translations/tr.js +1 -1
  55. package/lib/commonjs/locale/translations/tr.js.map +1 -1
  56. package/lib/commonjs/locale/translations/zh.js +1 -1
  57. package/lib/commonjs/locale/translations/zh.js.map +1 -1
  58. package/lib/module/agent-avatar/AgentAvatar.js +1 -1
  59. package/lib/module/agent-avatar/AgentAvatar.js.map +1 -1
  60. package/lib/module/agent-avatar/CharacterAvatar.js +1 -2
  61. package/lib/module/agent-avatar/CharacterAvatar.js.map +1 -1
  62. package/lib/module/agent-avatar/config-character.js +0 -4
  63. package/lib/module/agent-avatar/config-character.js.map +1 -1
  64. package/lib/module/agent-avatar/legacy-recipe.js +6 -19
  65. package/lib/module/agent-avatar/legacy-recipe.js.map +1 -1
  66. package/lib/module/agent-creator/AgentCreatorBase.js +109 -69
  67. package/lib/module/agent-creator/AgentCreatorBase.js.map +1 -1
  68. package/lib/module/agent-creator/CharacterControls.js +6 -24
  69. package/lib/module/agent-creator/CharacterControls.js.map +1 -1
  70. package/lib/module/agent-creator/EmotionPicker.js +1 -8
  71. package/lib/module/agent-creator/EmotionPicker.js.map +1 -1
  72. package/lib/module/agent-creator/ShapeSilhouette.js +1 -8
  73. package/lib/module/agent-creator/ShapeSilhouette.js.map +1 -1
  74. package/lib/module/agent-creator/constants.js +16 -8
  75. package/lib/module/agent-creator/constants.js.map +1 -1
  76. package/lib/module/agent-creator/messages.js +1 -1
  77. package/lib/module/agent-creator/messages.js.map +1 -1
  78. package/lib/module/locale/translations/ar.js +1 -1
  79. package/lib/module/locale/translations/ar.js.map +1 -1
  80. package/lib/module/locale/translations/bn.js +1 -1
  81. package/lib/module/locale/translations/bn.js.map +1 -1
  82. package/lib/module/locale/translations/ca.js +1 -1
  83. package/lib/module/locale/translations/ca.js.map +1 -1
  84. package/lib/module/locale/translations/de.js +1 -1
  85. package/lib/module/locale/translations/de.js.map +1 -1
  86. package/lib/module/locale/translations/es.js +1 -1
  87. package/lib/module/locale/translations/es.js.map +1 -1
  88. package/lib/module/locale/translations/fr.js +1 -1
  89. package/lib/module/locale/translations/fr.js.map +1 -1
  90. package/lib/module/locale/translations/hi.js +1 -1
  91. package/lib/module/locale/translations/hi.js.map +1 -1
  92. package/lib/module/locale/translations/id.js +1 -1
  93. package/lib/module/locale/translations/id.js.map +1 -1
  94. package/lib/module/locale/translations/it.js +1 -1
  95. package/lib/module/locale/translations/it.js.map +1 -1
  96. package/lib/module/locale/translations/ja.js +1 -1
  97. package/lib/module/locale/translations/ja.js.map +1 -1
  98. package/lib/module/locale/translations/pt.js +1 -1
  99. package/lib/module/locale/translations/pt.js.map +1 -1
  100. package/lib/module/locale/translations/ru.js +1 -1
  101. package/lib/module/locale/translations/ru.js.map +1 -1
  102. package/lib/module/locale/translations/tr.js +1 -1
  103. package/lib/module/locale/translations/tr.js.map +1 -1
  104. package/lib/module/locale/translations/zh.js +1 -1
  105. package/lib/module/locale/translations/zh.js.map +1 -1
  106. package/lib/typescript/commonjs/agent-avatar/CharacterAvatar.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/agent-avatar/config-character.d.ts +0 -4
  108. package/lib/typescript/commonjs/agent-avatar/config-character.d.ts.map +1 -1
  109. package/lib/typescript/commonjs/agent-avatar/legacy-recipe.d.ts +10 -2
  110. package/lib/typescript/commonjs/agent-avatar/legacy-recipe.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/agent-creator/AgentCreatorBase.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/agent-creator/CharacterControls.d.ts +1 -2
  113. package/lib/typescript/commonjs/agent-creator/CharacterControls.d.ts.map +1 -1
  114. package/lib/typescript/commonjs/agent-creator/EmotionPicker.d.ts +0 -1
  115. package/lib/typescript/commonjs/agent-creator/EmotionPicker.d.ts.map +1 -1
  116. package/lib/typescript/commonjs/agent-creator/ShapeSilhouette.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/agent-creator/constants.d.ts +15 -7
  118. package/lib/typescript/commonjs/agent-creator/constants.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/agent-creator/messages.d.ts.map +1 -1
  120. package/lib/typescript/module/agent-avatar/CharacterAvatar.d.ts.map +1 -1
  121. package/lib/typescript/module/agent-avatar/config-character.d.ts +0 -4
  122. package/lib/typescript/module/agent-avatar/config-character.d.ts.map +1 -1
  123. package/lib/typescript/module/agent-avatar/legacy-recipe.d.ts +10 -2
  124. package/lib/typescript/module/agent-avatar/legacy-recipe.d.ts.map +1 -1
  125. package/lib/typescript/module/agent-creator/AgentCreatorBase.d.ts.map +1 -1
  126. package/lib/typescript/module/agent-creator/CharacterControls.d.ts +1 -2
  127. package/lib/typescript/module/agent-creator/CharacterControls.d.ts.map +1 -1
  128. package/lib/typescript/module/agent-creator/EmotionPicker.d.ts +0 -1
  129. package/lib/typescript/module/agent-creator/EmotionPicker.d.ts.map +1 -1
  130. package/lib/typescript/module/agent-creator/ShapeSilhouette.d.ts.map +1 -1
  131. package/lib/typescript/module/agent-creator/constants.d.ts +15 -7
  132. package/lib/typescript/module/agent-creator/constants.d.ts.map +1 -1
  133. package/lib/typescript/module/agent-creator/messages.d.ts.map +1 -1
  134. package/package.json +1 -1
  135. package/src/agent-avatar/AgentAvatar.tsx +1 -1
  136. package/src/agent-avatar/CharacterAvatar.tsx +1 -4
  137. package/src/agent-avatar/config-character.ts +6 -39
  138. package/src/agent-avatar/legacy-recipe.ts +6 -34
  139. package/src/agent-creator/AgentCreatorBase.tsx +152 -121
  140. package/src/agent-creator/CharacterControls.tsx +14 -39
  141. package/src/agent-creator/EmotionPicker.tsx +16 -55
  142. package/src/agent-creator/ShapeSilhouette.tsx +1 -16
  143. package/src/agent-creator/constants.ts +14 -47
  144. package/src/agent-creator/messages.ts +1 -2
  145. package/src/locale/translations/ar.ts +1 -1
  146. package/src/locale/translations/bn.ts +1 -1
  147. package/src/locale/translations/ca.ts +1 -1
  148. package/src/locale/translations/de.ts +1 -1
  149. package/src/locale/translations/es.ts +1 -1
  150. package/src/locale/translations/fr.ts +1 -1
  151. package/src/locale/translations/hi.ts +1 -1
  152. package/src/locale/translations/id.ts +1 -1
  153. package/src/locale/translations/it.ts +1 -1
  154. package/src/locale/translations/ja.ts +1 -1
  155. package/src/locale/translations/pt.ts +1 -1
  156. package/src/locale/translations/ru.ts +1 -1
  157. package/src/locale/translations/tr.ts +1 -1
  158. package/src/locale/translations/zh.ts +1 -1
  159. package/assets/character-runtime/authored-parts.mjs +0 -754
  160. package/assets/character-runtime/catalog-parts.mjs +0 -362
  161. package/assets/character-runtime/character-recipe.mjs +0 -247
  162. package/assets/character-runtime/clippo-geometry.mjs +0 -679
  163. package/assets/character-runtime/controller-mode.mjs +0 -208
  164. package/assets/character-runtime/cyclops-geometry.mjs +0 -180
  165. package/assets/character-runtime/migrated-contours.mjs +0 -2067
  166. package/assets/character-runtime/migrated-faces.mjs +0 -59
  167. package/assets/character-runtime/pose-transition.mjs +0 -523
  168. package/assets/character-runtime/preparation-cache.mjs +0 -60
  169. package/assets/character-runtime/render-errors.mjs +0 -180
  170. package/assets/character-runtime/resolution.mjs +0 -10
  171. package/assets/character-runtime/sha256.mjs +0 -44
  172. package/assets/character-runtime/shared-programs.mjs +0 -534
  173. package/assets/character-runtime/shared-surface.mjs +0 -490
  174. package/assets/character-runtime/shared-textures.mjs +0 -420
  175. package/assets/character-runtime/thumbnails/eyes/todd.png +0 -0
  176. package/lib/commonjs/agent-avatar/character-colors.js +0 -19
  177. package/lib/commonjs/agent-avatar/character-colors.js.map +0 -1
  178. package/lib/commonjs/agent-avatar/character-shapes.js +0 -106
  179. package/lib/commonjs/agent-avatar/character-shapes.js.map +0 -1
  180. package/lib/commonjs/agent-creator/ClippoArtwork.js +0 -63
  181. package/lib/commonjs/agent-creator/ClippoArtwork.js.map +0 -1
  182. package/lib/commonjs/agent-creator/CyclopsArtwork.js +0 -45
  183. package/lib/commonjs/agent-creator/CyclopsArtwork.js.map +0 -1
  184. package/lib/commonjs/agent-creator/ToddArtwork.js +0 -29
  185. package/lib/commonjs/agent-creator/ToddArtwork.js.map +0 -1
  186. package/lib/module/agent-avatar/character-colors.js +0 -15
  187. package/lib/module/agent-avatar/character-colors.js.map +0 -1
  188. package/lib/module/agent-avatar/character-shapes.js +0 -97
  189. package/lib/module/agent-avatar/character-shapes.js.map +0 -1
  190. package/lib/module/agent-creator/ClippoArtwork.js +0 -59
  191. package/lib/module/agent-creator/ClippoArtwork.js.map +0 -1
  192. package/lib/module/agent-creator/CyclopsArtwork.js +0 -41
  193. package/lib/module/agent-creator/CyclopsArtwork.js.map +0 -1
  194. package/lib/module/agent-creator/ToddArtwork.js +0 -25
  195. package/lib/module/agent-creator/ToddArtwork.js.map +0 -1
  196. package/lib/typescript/commonjs/agent-avatar/character-colors.d.ts +0 -13
  197. package/lib/typescript/commonjs/agent-avatar/character-colors.d.ts.map +0 -1
  198. package/lib/typescript/commonjs/agent-avatar/character-shapes.d.ts +0 -19
  199. package/lib/typescript/commonjs/agent-avatar/character-shapes.d.ts.map +0 -1
  200. package/lib/typescript/commonjs/agent-creator/ClippoArtwork.d.ts +0 -6
  201. package/lib/typescript/commonjs/agent-creator/ClippoArtwork.d.ts.map +0 -1
  202. package/lib/typescript/commonjs/agent-creator/CyclopsArtwork.d.ts +0 -5
  203. package/lib/typescript/commonjs/agent-creator/CyclopsArtwork.d.ts.map +0 -1
  204. package/lib/typescript/commonjs/agent-creator/ToddArtwork.d.ts +0 -6
  205. package/lib/typescript/commonjs/agent-creator/ToddArtwork.d.ts.map +0 -1
  206. package/lib/typescript/module/agent-avatar/character-colors.d.ts +0 -13
  207. package/lib/typescript/module/agent-avatar/character-colors.d.ts.map +0 -1
  208. package/lib/typescript/module/agent-avatar/character-shapes.d.ts +0 -19
  209. package/lib/typescript/module/agent-avatar/character-shapes.d.ts.map +0 -1
  210. package/lib/typescript/module/agent-creator/ClippoArtwork.d.ts +0 -6
  211. package/lib/typescript/module/agent-creator/ClippoArtwork.d.ts.map +0 -1
  212. package/lib/typescript/module/agent-creator/CyclopsArtwork.d.ts +0 -5
  213. package/lib/typescript/module/agent-creator/CyclopsArtwork.d.ts.map +0 -1
  214. package/lib/typescript/module/agent-creator/ToddArtwork.d.ts +0 -6
  215. package/lib/typescript/module/agent-creator/ToddArtwork.d.ts.map +0 -1
  216. package/src/agent-avatar/character-colors.ts +0 -12
  217. package/src/agent-avatar/character-shapes.ts +0 -122
  218. package/src/agent-creator/ClippoArtwork.tsx +0 -49
  219. package/src/agent-creator/CyclopsArtwork.tsx +0 -27
  220. package/src/agent-creator/ToddArtwork.tsx +0 -24
@@ -1,19 +1,15 @@
1
- import { useContext, useMemo, useRef, useState } from 'react';
1
+ import { useContext, useMemo, useState } from 'react';
2
2
  import { Platform } from 'react-native';
3
3
  import { AgentAvatar, type AvatarConfig } from '../agent-avatar';
4
- import {
5
- CHARACTER_SHAPES,
6
- NATIVE_CHARACTER_SHAPES,
7
- createConfigForShape,
8
- currentCharacterShape,
9
- materializeCharacter,
10
- } from '../agent-avatar/character-shapes';
11
4
  import type { AvatarCharacterCategory } from '../agent-avatar/config-character';
12
5
  import {
13
6
  CharacterRuntimeContext,
14
7
  CharacterCapabilitiesContext,
15
8
  } from '../agent-avatar/context';
16
- import { legacyCharacterRecipe } from '../agent-avatar/legacy-recipe';
9
+ import {
10
+ legacyCharacterRecipe,
11
+ legacyNativeShape,
12
+ } from '../agent-avatar/legacy-recipe';
17
13
  import { Button } from '../button/Button';
18
14
  import { CloseButton } from '../button/CloseButton';
19
15
  import { useDirectionProps } from '../hooks/use-is-rtl';
@@ -60,86 +56,58 @@ export function AgentCreatorBase(props: AgentCreatorProps) {
60
56
  );
61
57
  const { runtimeUrl } = runtime;
62
58
  const beta = Boolean(c.character && c.character.preset !== 'bloom');
59
+ const nativeShape = legacyNativeShape(c);
63
60
  const key = JSON.stringify(beta ? c.character : legacyCharacterRecipe(c));
64
- const currentCapabilities =
61
+ const capabilities =
65
62
  catalog.capabilitiesByKey?.get(key) ??
66
63
  (catalog.capabilities?.key === key ? catalog.capabilities : undefined);
67
- const retained = useRef<{ owner: string; selected: Record<string, string> }>({
68
- owner: '',
69
- selected: {},
70
- });
71
- const owner = JSON.stringify([agent.id, c.character?.preset ?? 'bloom']);
72
- if (retained.current.owner !== owner)
73
- retained.current = { owner, selected: {} };
74
- if (currentCapabilities)
75
- retained.current.selected = currentCapabilities.selected;
76
- const capabilities = currentCapabilities ?? {
77
- selected: retained.current.selected,
78
- available: {},
79
- };
80
64
  const betaColor =
81
- c.character?.selections?.color ?? capabilities?.selected.color;
82
- const betaBodyColor =
83
- c.character?.bodyColor ??
84
- (c.character?.preset === 'clippo' && !c.character.selections?.color
85
- ? '#999b9d'
86
- : undefined) ??
87
- (!betaColor ? capabilities?.selected.bodyColor : undefined);
65
+ capabilities?.selected.color ?? c.character?.selections?.color;
88
66
  const customColorValue = beta
89
- ? (betaBodyColor ??
90
- (c.character?.selections?.color
91
- ? CHARACTER_COLORS[
92
- c.character.selections.color as keyof typeof CHARACTER_COLORS
93
- ]
94
- : undefined) ??
95
- capabilities?.selected.bodyColor ??
67
+ ? (c.character?.bodyColor ??
96
68
  CHARACTER_COLORS[betaColor as keyof typeof CHARACTER_COLORS] ??
97
69
  avatarHex(c))
98
- : (c.character?.bodyColor ??
99
- CHARACTER_COLORS[
100
- c.character?.selections?.color as keyof typeof CHARACTER_COLORS
101
- ] ??
102
- avatarHex(c));
70
+ : (c.character?.bodyColor ?? avatarHex(c));
103
71
  const selectedEyes =
104
- c.character?.selections?.eyes ??
105
- capabilities?.selected.eyes ??
106
- (c.character?.preset === 'clippo'
107
- ? 'clippo'
108
- : c.character?.preset === 'lime_frog'
109
- ? 'todd'
110
- : 'oval');
111
- const selectedEyewear =
112
- c.character?.selections?.eyewear ?? capabilities?.selected.eyewear;
113
- const selectedAccessory =
114
- c.character?.selections?.accessory ?? capabilities?.selected.accessory;
115
- const selectedParts = {
116
- ...(c.character?.selections?.eyes ||
117
- capabilities.selected.eyes ||
118
- c.character?.preset === 'clippo' ||
119
- c.character?.preset === 'lime_frog'
120
- ? { eyes: selectedEyes }
121
- : {}),
122
- ...(selectedEyewear ? { eyewear: selectedEyewear } : {}),
123
- ...(selectedAccessory ? { accessory: selectedAccessory } : {}),
124
- };
72
+ capabilities?.selected.eyes ?? c.character?.selections?.eyes ?? 'oval';
125
73
  const characterConfig = (
126
74
  category: AvatarCharacterCategory,
127
75
  id: string,
128
76
  ): AvatarConfig => {
129
- if (category === 'shape')
130
- return createConfigForShape(c, id, capabilities.selected);
131
- const character = materializeCharacter(c, capabilities.selected);
132
- // Choosing a body retains the edited face and the preset's effective parts.
133
- // Presets supply defaults; their catalog restrictions do not own edits.
134
- const selections = {
135
- ...selectedParts,
136
- ...character.selections,
137
- [category]: id,
138
- };
139
- const next = {
140
- ...character,
141
- selections,
142
- };
77
+ if (
78
+ !beta &&
79
+ (category === 'eyes' ||
80
+ category === 'eyewear' ||
81
+ category === 'accessory')
82
+ )
83
+ return {
84
+ ...c,
85
+ character: {
86
+ ...c.character,
87
+ preset: 'bloom',
88
+ selections: { ...c.character?.selections, [category]: id },
89
+ },
90
+ };
91
+ const character = beta
92
+ ? c.character!
93
+ : {
94
+ preset: 'blue_beret',
95
+ bodyColor: customColorValue,
96
+ selections: {
97
+ eyewear:
98
+ c.character?.selections?.eyewear ??
99
+ capabilities?.selected.eyewear ??
100
+ 'none',
101
+ accessory:
102
+ c.character?.selections?.accessory ??
103
+ capabilities?.selected.accessory ??
104
+ 'none',
105
+ eyes: selectedEyes,
106
+ },
107
+ };
108
+ const selections = { ...character.selections, [category]: id };
109
+ if (category === 'shape') delete selections.eyes;
110
+ const next = { ...character, selections };
143
111
  if (category === 'color') delete next.bodyColor;
144
112
  return { ...c, character: next };
145
113
  };
@@ -149,37 +117,103 @@ export function AgentCreatorBase(props: AgentCreatorProps) {
149
117
  ? runtimeUrl.slice(0, runtimeUrl.lastIndexOf('/') + 1)
150
118
  : '';
151
119
  const eyeBackground = customColorValue;
120
+ const migratedShapes = [
121
+ ...(['slender', 'pocket', 'petal', 'star', 'cloud', 'shield'] as const).map(
122
+ (shape) => ({
123
+ family: 'fold' as const,
124
+ shape,
125
+ title: messages.shapes[shape],
126
+ }),
127
+ ),
128
+ ...(
129
+ [
130
+ ['pebble', 'Pebble'],
131
+ ['squircle', 'Squircle'],
132
+ ] as const
133
+ ).map(([shape, title]) => ({
134
+ family: 'blob' as const,
135
+ shape,
136
+ title: messages.characterOption('shape', shape, title),
137
+ })),
138
+ ];
139
+ const migratedConfig = (
140
+ family: 'fold' | 'blob',
141
+ shape: string,
142
+ ): AvatarConfig => {
143
+ const { character: _character, ...existing } = c;
144
+ const color = hexAppearance(customColorValue);
145
+ return {
146
+ ...existing,
147
+ ...color,
148
+ lightEyes: beta ? false : c.lightEyes,
149
+ family,
150
+ ...(family === 'fold'
151
+ ? { foldShape: shape as AvatarConfig['foldShape'] }
152
+ : { shape: shape as AvatarConfig['shape'] }),
153
+ ...(runtimeUrl
154
+ ? {
155
+ character: {
156
+ preset: 'bloom',
157
+ selections: {
158
+ eyes: selectedEyes,
159
+ ...((capabilities?.selected.eyewear ??
160
+ c.character?.selections?.eyewear)
161
+ ? {
162
+ eyewear:
163
+ capabilities?.selected.eyewear ??
164
+ c.character?.selections?.eyewear,
165
+ }
166
+ : {}),
167
+ ...((capabilities?.selected.accessory ??
168
+ c.character?.selections?.accessory)
169
+ ? {
170
+ accessory:
171
+ capabilities?.selected.accessory ??
172
+ c.character?.selections?.accessory,
173
+ }
174
+ : {}),
175
+ },
176
+ },
177
+ }
178
+ : {}),
179
+ };
180
+ };
152
181
  const shapeChoices = runtimeUrl
153
- ? CHARACTER_SHAPES.map(([id, title]) => ({
154
- id,
155
- label:
156
- id in messages.shapes
157
- ? messages.shapes[id as keyof typeof messages.shapes]
158
- : messages.characterOption('shape', id, title),
159
- config: createConfigForShape(c, id, capabilities.selected),
160
- ...(NATIVE_CHARACTER_SHAPES.some(([shape]) => shape === id) &&
161
- id !== 'clippo' &&
162
- id !== 'todd'
163
- ? { thumbnail: `${thumbnailBase}thumbnails/shapes/${id}.png` }
164
- : {}),
165
- }))
182
+ ? [
183
+ ...CHARACTER_OPTIONS.shape.map(([id, title]) => ({
184
+ id,
185
+ label: messages.characterOption('shape', id, title),
186
+ config: characterConfig('shape', id),
187
+ thumbnail: `${thumbnailBase}thumbnails/shapes/${id}.png`,
188
+ })),
189
+ ...migratedShapes.map(({ family, shape, title }) => ({
190
+ id: `legacy:${family}:${shape}`,
191
+ label: title,
192
+ config: migratedConfig(family, shape),
193
+ })),
194
+ ]
166
195
  : undefined;
167
- const shapeValue =
168
- c.character?.selections?.shape ??
169
- (beta
170
- ? (capabilities.selected.shape ??
171
- (c.character?.preset === 'clippo' ? 'clippo' : undefined))
172
- : currentCharacterShape(c));
173
- const selectShape = (id: string) => selectCharacter('shape', id);
196
+ const shapeValue = beta
197
+ ? (capabilities?.selected.shape ?? c.character?.selections?.shape)
198
+ : (nativeShape ??
199
+ `legacy:${c.family}:${c.family === 'fold' ? c.foldShape : c.shape}`);
200
+ const selectShape = (id: string) => {
201
+ if (id.startsWith('legacy:')) {
202
+ const [, family, shape] = id.split(':');
203
+ onChange({
204
+ ...agent,
205
+ avatar: migratedConfig(family as 'fold' | 'blob', shape!),
206
+ });
207
+ } else selectCharacter('shape', id);
208
+ };
174
209
  const eyeChoices = runtimeUrl
175
210
  ? CHARACTER_OPTIONS.eyes.map(([id, title]) => ({
176
211
  id,
177
212
  label: messages.characterOption('eyes', id, title),
178
213
  config: characterConfig('eyes', id),
179
- ...(id === 'clippo' || id === 'cyclops'
180
- ? { artwork: id }
181
- : { thumbnail: `${thumbnailBase}thumbnails/eyes/${id}.png` }),
182
- disabled: false,
214
+ thumbnail: `${thumbnailBase}thumbnails/eyes/${id}.png`,
215
+ disabled:
216
+ !capabilities || capabilities.available[`eyes:${id}`] === false,
183
217
  }))
184
218
  : undefined;
185
219
  const appearance = (patch: Partial<AvatarConfig>) => {
@@ -333,15 +367,8 @@ export function AgentCreatorBase(props: AgentCreatorProps) {
333
367
  </StyledView>
334
368
  )}
335
369
  <CharacterControls
336
- character={
337
- runtimeUrl
338
- ? beta
339
- ? c.character
340
- : materializeCharacter(c, capabilities.selected)
341
- : c.character
342
- }
370
+ character={c.character ?? { preset: 'bloom' }}
343
371
  capabilitiesKey={key}
344
- selected={capabilities.selected}
345
372
  onChange={(character) => {
346
373
  if (character)
347
374
  onChange({ ...agent, avatar: { ...c, character } });
@@ -359,7 +386,7 @@ export function AgentCreatorBase(props: AgentCreatorProps) {
359
386
  boxShadow: '0 1px 2px rgba(0,0,0,.05)',
360
387
  }}
361
388
  >
362
- {!runtimeUrl &&
389
+ {!beta &&
363
390
  AVATAR_COLORS.map(([hue, saturation, name, center, edge]) => {
364
391
  const active =
365
392
  c.hue === hue &&
@@ -388,11 +415,15 @@ export function AgentCreatorBase(props: AgentCreatorProps) {
388
415
  </StyledPressable>
389
416
  );
390
417
  })}
391
- {runtimeUrl &&
418
+ {beta &&
392
419
  CHARACTER_OPTIONS.color.map(([id, title]) => {
393
- const active = !betaBodyColor && betaColor === id;
420
+ const active =
421
+ !c.character?.bodyColor &&
422
+ capabilities?.selected.color === id;
394
423
  const hex = CHARACTER_COLORS[id];
395
- const disabled = false;
424
+ const disabled =
425
+ !capabilities ||
426
+ capabilities.available[`color:${id}`] === false;
396
427
  return (
397
428
  <StyledPressable
398
429
  key={id}
@@ -415,18 +446,18 @@ export function AgentCreatorBase(props: AgentCreatorProps) {
415
446
  <PopoverTrigger asChild label={messages.customColor}>
416
447
  <StyledPressable
417
448
  aria-pressed={
418
- runtimeUrl ? Boolean(betaBodyColor) : undefined
449
+ beta ? Boolean(c.character?.bodyColor) : undefined
419
450
  }
420
451
  accessibilityState={{
421
- selected: Boolean(runtimeUrl) && Boolean(betaBodyColor),
452
+ selected: beta && Boolean(c.character?.bodyColor),
422
453
  }}
423
454
  className="relative size-[26px] shrink-0 overflow-hidden rounded-full"
424
455
  >
425
456
  <GlossArt
426
- rainbow={!runtimeUrl || !betaBodyColor}
427
- center={runtimeUrl ? betaBodyColor : undefined}
428
- edge={runtimeUrl ? betaBodyColor : undefined}
429
- active={Boolean(runtimeUrl) && Boolean(betaBodyColor)}
457
+ rainbow={!beta || !c.character?.bodyColor}
458
+ center={beta ? c.character?.bodyColor : undefined}
459
+ edge={beta ? c.character?.bodyColor : undefined}
460
+ active={beta && Boolean(c.character?.bodyColor)}
430
461
  />
431
462
  </StyledPressable>
432
463
  </PopoverTrigger>
@@ -442,14 +473,14 @@ export function AgentCreatorBase(props: AgentCreatorProps) {
442
473
  onChange={(hex) => {
443
474
  const next = hexAppearance(hex);
444
475
  if (!next) return;
445
- if (runtimeUrl)
476
+ if (beta)
446
477
  onChange({
447
478
  ...agent,
448
479
  avatar: {
449
480
  ...c,
450
481
  ...next,
451
482
  character: {
452
- ...materializeCharacter(c, capabilities.selected),
483
+ ...c.character!,
453
484
  bodyColor: hex.toLowerCase(),
454
485
  },
455
486
  },
@@ -1,4 +1,4 @@
1
- import { useContext, useRef } from 'react';
1
+ import { useContext } from 'react';
2
2
  import {
3
3
  CharacterRuntimeContext,
4
4
  CharacterCapabilitiesContext,
@@ -16,12 +16,10 @@ import { useAgentCreatorBindings, useAgentCreatorMessages } from './context';
16
16
  export function CharacterControls({
17
17
  character,
18
18
  capabilitiesKey,
19
- selected,
20
19
  onChange,
21
20
  }: {
22
21
  character?: AvatarCharacterConfig;
23
22
  capabilitiesKey?: string;
24
- selected?: Readonly<Record<string, string>>;
25
23
  onChange: (character: AvatarCharacterConfig | undefined) => void;
26
24
  }) {
27
25
  const messages = useAgentCreatorMessages();
@@ -38,21 +36,11 @@ export function CharacterControls({
38
36
  SelectItem,
39
37
  SelectItemText,
40
38
  } = useAgentCreatorBindings();
39
+ if (!runtimeUrl) return null;
41
40
  const key = capabilitiesKey ?? JSON.stringify(character);
42
- const current =
41
+ const resolved =
43
42
  capabilitiesByKey?.get(key) ??
44
43
  (character && capabilities?.key === key ? capabilities : undefined);
45
- const retained = useRef<{
46
- preset?: string;
47
- selected: Readonly<Record<string, string>>;
48
- }>({ selected: {} });
49
- if (retained.current.preset !== character?.preset)
50
- retained.current = { preset: character?.preset, selected: {} };
51
- if (current) retained.current.selected = current.selected;
52
- const resolved = {
53
- selected: selected ?? current?.selected ?? retained.current.selected,
54
- };
55
- if (!runtimeUrl) return null;
56
44
  const row = (
57
45
  label: string,
58
46
  value: string,
@@ -102,14 +90,11 @@ export function CharacterControls({
102
90
  if (character && !presets.some((item) => item.id === character.preset))
103
91
  presets.push({
104
92
  id: character.preset,
105
- title:
106
- character.preset === 'legacy'
107
- ? messages.proceduralAvatar
108
- : messages.characterOption(
109
- 'preset',
110
- character.preset,
111
- character.preset,
112
- ),
93
+ title: messages.characterOption(
94
+ 'preset',
95
+ character.preset,
96
+ character.preset,
97
+ ),
113
98
  });
114
99
  const labels: Record<AvatarCharacterCategory, string> = {
115
100
  shape: messages.shape,
@@ -135,8 +120,8 @@ export function CharacterControls({
135
120
  (category) =>
136
121
  row(
137
122
  labels[category],
138
- character.selections?.[category] ??
139
- resolved?.selected[category] ??
123
+ resolved?.selected[category] ??
124
+ character.selections?.[category] ??
140
125
  'none',
141
126
  (category === 'accessory'
142
127
  ? [
@@ -147,24 +132,14 @@ export function CharacterControls({
147
132
  ).map(([id, title]) => ({
148
133
  id,
149
134
  title: messages.characterOption(category, id, title),
150
- disabled: false,
135
+ disabled:
136
+ !resolved ||
137
+ resolved.available[`${category}:${id}`] === false,
151
138
  })),
152
139
  (id) => {
153
140
  onChange({
154
141
  ...character,
155
- selections: {
156
- ...(['eyes', 'eyewear', 'accessory'] as const).reduce(
157
- (parts, part) => ({
158
- ...parts,
159
- ...(resolved?.selected[part]
160
- ? { [part]: resolved.selected[part] }
161
- : {}),
162
- }),
163
- {},
164
- ),
165
- ...character.selections,
166
- [category]: id,
167
- },
142
+ selections: { ...character.selections, [category]: id },
168
143
  });
169
144
  },
170
145
  labels[category],
@@ -12,26 +12,13 @@ import Animated, {
12
12
  } from 'react-native-reanimated';
13
13
  import { EYES, type AvatarConfig } from '../agent-avatar';
14
14
  import { AgentFace } from '../agent-avatar/AgentFace';
15
- import { ClippoArtwork } from './ClippoArtwork';
16
- import { CyclopsArtwork } from './CyclopsArtwork';
17
- import {
18
- StyledImage,
19
- StyledPressable,
20
- StyledView,
21
- } from '../styles/styled-primitives';
15
+ import { StyledImage, StyledPressable, StyledView } from '../styles/styled-primitives';
22
16
  import { useTheme } from '../theme/use-theme';
23
17
  import { useAgentCreatorMessages } from './context';
24
18
  import { useTrackEvents } from './use-track-events';
25
19
 
26
20
  const AnimatedView = Animated.createAnimatedComponent(StyledView);
27
- export type EmotionChoice = {
28
- id: string;
29
- label: string;
30
- config: AvatarConfig;
31
- disabled?: boolean;
32
- thumbnail?: string;
33
- artwork?: 'clippo' | 'cyclops';
34
- };
21
+ export type EmotionChoice = { id: string; label: string; config: AvatarConfig; disabled?: boolean; thumbnail?: string };
35
22
  function Emotion({
36
23
  config,
37
24
  choice,
@@ -84,40 +71,21 @@ function Emotion({
84
71
  pointerEvents="auto"
85
72
  className={`pointer-events-auto flex size-[34px] cursor-pointer items-center justify-center overflow-hidden rounded-full border-2 outline-none transition-[border-color,box-shadow] hover:ring-2 hover:ring-border-button-hover focus-visible:ring-2 focus-visible:ring-border-focus-ring ${active ? 'border-foreground-icon-secondary' : 'border-transparent'}`}
86
73
  style={{
87
- backgroundColor:
88
- backgroundColor ??
89
- `hsl(${config.hue}, ${config.saturation}%, ${config.lightness ?? 80}%)`,
74
+ backgroundColor: backgroundColor ?? `hsl(${config.hue}, ${config.saturation}%, ${config.lightness ?? 80}%)`,
90
75
  borderColor: active ? colors.icon : 'transparent',
91
76
  }}
92
77
  >
93
- {choice.artwork === 'cyclops' ? (
94
- <CyclopsArtwork opacity={choice.disabled ? 0.35 : 1} />
95
- ) : choice.artwork === 'clippo' ? (
96
- <ClippoArtwork kind="eyes" opacity={choice.disabled ? 0.35 : 1} />
97
- ) : choice.thumbnail ? (
98
- <StyledImage
99
- source={{ uri: choice.thumbnail }}
100
- resizeMode="contain"
101
- accessible={false}
102
- style={{
103
- width: 27,
104
- height: 27,
105
- opacity: choice.disabled ? 0.35 : 1,
106
- }}
107
- />
108
- ) : (
109
- <AgentFace
110
- config={{
111
- ...choice.config,
112
- eyeSize: 24,
113
- eyeGap: 36,
114
- lookAt: 'center',
115
- motion: 0,
116
- idle: false,
117
- }}
118
- size={34}
119
- />
120
- )}
78
+ {choice.thumbnail ? <StyledImage source={{ uri: choice.thumbnail }} resizeMode="contain" accessible={false} style={{ width: 27, height: 27, opacity: choice.disabled ? 0.35 : 1 }} /> : <AgentFace
79
+ config={{
80
+ ...choice.config,
81
+ eyeSize: 24,
82
+ eyeGap: 36,
83
+ lookAt: 'center',
84
+ motion: 0,
85
+ idle: false,
86
+ }}
87
+ size={34}
88
+ />}
121
89
  </StyledPressable>
122
90
  </AnimatedView>
123
91
  </StyledView>
@@ -141,16 +109,9 @@ export function EmotionPicker({
141
109
  onChange: (eyes: AvatarConfig['eyes']) => void;
142
110
  }) {
143
111
  const messages = useAgentCreatorMessages();
144
- const choices =
145
- choicesProp ??
146
- EYES.map((eyes) => ({
147
- id: eyes,
148
- label: messages.emotions[eyes],
149
- config: { ...config, eyes },
150
- }));
112
+ const choices = choicesProp ?? EYES.map((eyes) => ({ id: eyes, label: messages.emotions[eyes], config: { ...config, eyes } }));
151
113
  const value = valueProp ?? config.eyes;
152
- const emit = (id: string) =>
153
- onSelect ? onSelect(id) : onChange(id as AvatarConfig['eyes']);
114
+ const emit = (id: string) => onSelect ? onSelect(id) : onChange(id as AvatarConfig['eyes']);
154
115
  const ref = useRef<View>(null);
155
116
  const visible = useSharedValue(true);
156
117
  const target = useSharedValue(0);
@@ -1,12 +1,9 @@
1
- import { characterShapeGeometry } from '../agent-avatar/character-shapes';
2
1
  import { memo } from 'react';
3
2
  import Svg, { Path } from 'react-native-svg';
4
3
  import { legacyContours } from '../agent-avatar/legacy-contours';
5
4
  import { radialContour } from '../agent-avatar/legacy-recipe';
6
5
  import type { AvatarConfig } from '../agent-avatar/model';
7
6
  import { CHARACTER_COLORS } from './constants';
8
- import { ClippoArtwork } from './ClippoArtwork';
9
- import { ToddArtwork } from './ToddArtwork';
10
7
 
11
8
  const silhouettes = new Map<string, { path: string; viewBox: string }>();
12
9
 
@@ -52,19 +49,7 @@ export const ShapeSilhouette = memo(function ShapeSilhouette({
52
49
  }: {
53
50
  config: AvatarConfig;
54
51
  }) {
55
- if (
56
- config.character?.selections?.shape === 'todd' ||
57
- (config.character?.preset === 'lime_frog' &&
58
- !config.character.selections?.shape)
59
- )
60
- return <ToddArtwork />;
61
- if (
62
- config.character?.selections?.shape === 'clippo' ||
63
- (config.character?.preset === 'clippo' &&
64
- !config.character.selections?.shape)
65
- )
66
- return <ClippoArtwork kind="shape" />;
67
- const { path, viewBox } = silhouette(characterShapeGeometry(config));
52
+ const { path, viewBox } = silhouette(config);
68
53
  return (
69
54
  <Svg
70
55
  width={56}