@oxy.so/bloom 7.0.1 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (517) hide show
  1. package/assets/character-runtime/README.md +55 -0
  2. package/assets/character-runtime/appearance-codec.mjs +149 -0
  3. package/assets/character-runtime/integrity.json +6 -0
  4. package/assets/character-runtime/legacy-engine.mjs +105 -0
  5. package/assets/character-runtime/legacy-geometry.mjs +166 -0
  6. package/assets/character-runtime/legacy-worker.mjs +85 -0
  7. package/assets/character-runtime/orbit-characters.data +0 -0
  8. package/assets/character-runtime/orbit-characters.mjs +30 -0
  9. package/assets/character-runtime/orbit-characters.wasm +0 -0
  10. package/assets/character-runtime/preparation-worker.mjs +19 -0
  11. package/assets/character-runtime/runtime.mjs +820 -0
  12. package/assets/character-runtime/thumbnails/eyes/crescent_inset.png +0 -0
  13. package/assets/character-runtime/thumbnails/eyes/dots.png +0 -0
  14. package/assets/character-runtime/thumbnails/eyes/double_highlights.png +0 -0
  15. package/assets/character-runtime/thumbnails/eyes/highlight_capsules.png +0 -0
  16. package/assets/character-runtime/thumbnails/eyes/oval.png +0 -0
  17. package/assets/character-runtime/thumbnails/eyes/round_inset.png +0 -0
  18. package/assets/character-runtime/thumbnails/eyes/sleepy_lids.png +0 -0
  19. package/assets/character-runtime/thumbnails/eyes/sparkle_capsules.png +0 -0
  20. package/assets/character-runtime/thumbnails/eyes/swept_lids.png +0 -0
  21. package/assets/character-runtime/thumbnails/shapes/capsule.png +0 -0
  22. package/assets/character-runtime/thumbnails/shapes/circle.png +0 -0
  23. package/assets/character-runtime/thumbnails/shapes/circle_two_ears.png +0 -0
  24. package/assets/character-runtime/thumbnails/shapes/four_lobed_butterfly.png +0 -0
  25. package/assets/character-runtime/thumbnails/shapes/heart.png +0 -0
  26. package/assets/character-runtime/thumbnails/shapes/rounded_cube.png +0 -0
  27. package/assets/character-runtime/thumbnails/shapes/rounded_diamond.png +0 -0
  28. package/assets/character-runtime/thumbnails/shapes/rounded_head_two_ears.png +0 -0
  29. package/assets/character-runtime/thumbnails/shapes/rounded_triangle.png +0 -0
  30. package/assets/character-runtime/thumbnails/shapes/six_lobed_flower.png +0 -0
  31. package/assets/character-runtime/thumbnails/shapes/twelve_scalloped_rosette.png +0 -0
  32. package/docs/adoption-matrix.mdx +9 -9
  33. package/docs/agent-avatar.mdx +35 -10
  34. package/docs/agent-creator.mdx +12 -4
  35. package/docs/multi-agent-chat.mdx +12 -0
  36. package/docs/project-board.mdx +3 -3
  37. package/lib/commonjs/agent-avatar/AgentAvatar.js +15 -203
  38. package/lib/commonjs/agent-avatar/AgentAvatar.js.map +1 -1
  39. package/lib/commonjs/agent-avatar/AgentAvatarProvider.js +64 -0
  40. package/lib/commonjs/agent-avatar/AgentAvatarProvider.js.map +1 -0
  41. package/lib/commonjs/agent-avatar/CharacterAvatar.js +211 -0
  42. package/lib/commonjs/agent-avatar/CharacterAvatar.js.map +1 -0
  43. package/lib/commonjs/agent-avatar/ProceduralAvatar.js +216 -0
  44. package/lib/commonjs/agent-avatar/ProceduralAvatar.js.map +1 -0
  45. package/lib/commonjs/agent-avatar/artwork.js +4 -3
  46. package/lib/commonjs/agent-avatar/artwork.js.map +1 -1
  47. package/lib/commonjs/agent-avatar/avatar-color.js +16 -0
  48. package/lib/commonjs/agent-avatar/avatar-color.js.map +1 -0
  49. package/lib/commonjs/agent-avatar/character-html.js +25 -0
  50. package/lib/commonjs/agent-avatar/character-html.js.map +1 -0
  51. package/lib/commonjs/agent-avatar/character-runtime-module.js +39 -0
  52. package/lib/commonjs/agent-avatar/character-runtime-module.js.map +1 -0
  53. package/lib/commonjs/agent-avatar/character-webview.js +18 -0
  54. package/lib/commonjs/agent-avatar/character-webview.js.map +1 -0
  55. package/lib/commonjs/agent-avatar/config-character.js +36 -0
  56. package/lib/commonjs/agent-avatar/config-character.js.map +1 -0
  57. package/lib/commonjs/agent-avatar/context.js +3 -1
  58. package/lib/commonjs/agent-avatar/context.js.map +1 -1
  59. package/lib/commonjs/agent-avatar/index.js +7 -0
  60. package/lib/commonjs/agent-avatar/index.js.map +1 -1
  61. package/lib/commonjs/agent-avatar/legacy-contours.js +57 -0
  62. package/lib/commonjs/agent-avatar/legacy-contours.js.map +1 -0
  63. package/lib/commonjs/agent-avatar/legacy-recipe.js +112 -0
  64. package/lib/commonjs/agent-avatar/legacy-recipe.js.map +1 -0
  65. package/lib/commonjs/agent-avatar/messages.js +2 -1
  66. package/lib/commonjs/agent-avatar/messages.js.map +1 -1
  67. package/lib/commonjs/agent-avatar/model.js +2 -0
  68. package/lib/commonjs/agent-avatar/model.js.map +1 -1
  69. package/lib/commonjs/agent-creator/AgentCreatorBase.js +245 -21
  70. package/lib/commonjs/agent-creator/AgentCreatorBase.js.map +1 -1
  71. package/lib/commonjs/agent-creator/CharacterControls.js +113 -0
  72. package/lib/commonjs/agent-creator/CharacterControls.js.map +1 -0
  73. package/lib/commonjs/agent-creator/EmotionPicker.js +51 -18
  74. package/lib/commonjs/agent-creator/EmotionPicker.js.map +1 -1
  75. package/lib/commonjs/agent-creator/ShapeArc.js +56 -26
  76. package/lib/commonjs/agent-creator/ShapeArc.js.map +1 -1
  77. package/lib/commonjs/agent-creator/ShapeSilhouette.js +62 -0
  78. package/lib/commonjs/agent-creator/ShapeSilhouette.js.map +1 -0
  79. package/lib/commonjs/agent-creator/constants.js +48 -1
  80. package/lib/commonjs/agent-creator/constants.js.map +1 -1
  81. package/lib/commonjs/agent-creator/messages.js +9 -0
  82. package/lib/commonjs/agent-creator/messages.js.map +1 -1
  83. package/lib/commonjs/agent-creator/shared.js +7 -10
  84. package/lib/commonjs/agent-creator/shared.js.map +1 -1
  85. package/lib/commonjs/agent-thinking/AgentThinking.js +4 -62
  86. package/lib/commonjs/agent-thinking/AgentThinking.js.map +1 -1
  87. package/lib/commonjs/agent-thinking/ThinkingLabel.js +77 -0
  88. package/lib/commonjs/agent-thinking/ThinkingLabel.js.map +1 -0
  89. package/lib/commonjs/chart-cards/TokensChartCard.js +20 -5
  90. package/lib/commonjs/chart-cards/TokensChartCard.js.map +1 -1
  91. package/lib/commonjs/locale/translations/ar.js +11 -1
  92. package/lib/commonjs/locale/translations/ar.js.map +1 -1
  93. package/lib/commonjs/locale/translations/bn.js +11 -1
  94. package/lib/commonjs/locale/translations/bn.js.map +1 -1
  95. package/lib/commonjs/locale/translations/ca.js +11 -1
  96. package/lib/commonjs/locale/translations/ca.js.map +1 -1
  97. package/lib/commonjs/locale/translations/de.js +11 -1
  98. package/lib/commonjs/locale/translations/de.js.map +1 -1
  99. package/lib/commonjs/locale/translations/es.js +11 -1
  100. package/lib/commonjs/locale/translations/es.js.map +1 -1
  101. package/lib/commonjs/locale/translations/fr.js +11 -1
  102. package/lib/commonjs/locale/translations/fr.js.map +1 -1
  103. package/lib/commonjs/locale/translations/hi.js +11 -1
  104. package/lib/commonjs/locale/translations/hi.js.map +1 -1
  105. package/lib/commonjs/locale/translations/id.js +11 -1
  106. package/lib/commonjs/locale/translations/id.js.map +1 -1
  107. package/lib/commonjs/locale/translations/it.js +11 -1
  108. package/lib/commonjs/locale/translations/it.js.map +1 -1
  109. package/lib/commonjs/locale/translations/ja.js +11 -1
  110. package/lib/commonjs/locale/translations/ja.js.map +1 -1
  111. package/lib/commonjs/locale/translations/pt.js +11 -1
  112. package/lib/commonjs/locale/translations/pt.js.map +1 -1
  113. package/lib/commonjs/locale/translations/ru.js +11 -1
  114. package/lib/commonjs/locale/translations/ru.js.map +1 -1
  115. package/lib/commonjs/locale/translations/tr.js +11 -1
  116. package/lib/commonjs/locale/translations/tr.js.map +1 -1
  117. package/lib/commonjs/locale/translations/zh.js +11 -1
  118. package/lib/commonjs/locale/translations/zh.js.map +1 -1
  119. package/lib/commonjs/multi-agent-chat/AvatarHandoff.js +2 -4
  120. package/lib/commonjs/multi-agent-chat/AvatarHandoff.js.map +1 -1
  121. package/lib/commonjs/multi-agent-chat/ChatAgentProfile.js +97 -0
  122. package/lib/commonjs/multi-agent-chat/ChatAgentProfile.js.map +1 -0
  123. package/lib/commonjs/multi-agent-chat/EditorRail.js +4 -0
  124. package/lib/commonjs/multi-agent-chat/EditorRail.js.map +1 -1
  125. package/lib/commonjs/multi-agent-chat/MultiAgentChatBase.js +89 -126
  126. package/lib/commonjs/multi-agent-chat/MultiAgentChatBase.js.map +1 -1
  127. package/lib/commonjs/multi-agent-chat/ReplyRow.js +8 -11
  128. package/lib/commonjs/multi-agent-chat/ReplyRow.js.map +1 -1
  129. package/lib/commonjs/multi-agent-chat/handoff.js +5 -5
  130. package/lib/commonjs/multi-agent-chat/handoff.js.map +1 -1
  131. package/lib/commonjs/project-board/CreateTicketDialog.js +33 -2
  132. package/lib/commonjs/project-board/CreateTicketDialog.js.map +1 -1
  133. package/lib/commonjs/project-board/ProjectBoardBase.js +137 -38
  134. package/lib/commonjs/project-board/ProjectBoardBase.js.map +1 -1
  135. package/lib/commonjs/project-board/TicketCornerGenieSurface.web.js +34 -28
  136. package/lib/commonjs/project-board/TicketCornerGenieSurface.web.js.map +1 -1
  137. package/lib/commonjs/project-board/TicketDetailDialog.js +6 -3
  138. package/lib/commonjs/project-board/TicketDetailDialog.js.map +1 -1
  139. package/lib/commonjs/project-board/TicketGenieSurface.js +17 -7
  140. package/lib/commonjs/project-board/TicketGenieSurface.js.map +1 -1
  141. package/lib/commonjs/project-board/TicketGenieSurface.web.js +1 -1
  142. package/lib/commonjs/project-board/TicketGenieSurface.web.js.map +1 -1
  143. package/lib/commonjs/project-board/TicketPresence.js +125 -0
  144. package/lib/commonjs/project-board/TicketPresence.js.map +1 -0
  145. package/lib/commonjs/project-board/TicketTokenChart.js +23 -0
  146. package/lib/commonjs/project-board/TicketTokenChart.js.map +1 -0
  147. package/lib/commonjs/project-board/context.js +3 -1
  148. package/lib/commonjs/project-board/context.js.map +1 -1
  149. package/lib/commonjs/search/Search.js +12 -3
  150. package/lib/commonjs/search/Search.js.map +1 -1
  151. package/lib/commonjs/sidebar/SidebarToolbar.js +28 -14
  152. package/lib/commonjs/sidebar/SidebarToolbar.js.map +1 -1
  153. package/lib/commonjs/text-field/TextField.js +13 -7
  154. package/lib/commonjs/text-field/TextField.js.map +1 -1
  155. package/lib/module/agent-avatar/AgentAvatar.js +16 -204
  156. package/lib/module/agent-avatar/AgentAvatar.js.map +1 -1
  157. package/lib/module/agent-avatar/AgentAvatarProvider.js +60 -0
  158. package/lib/module/agent-avatar/AgentAvatarProvider.js.map +1 -0
  159. package/lib/module/agent-avatar/CharacterAvatar.js +207 -0
  160. package/lib/module/agent-avatar/CharacterAvatar.js.map +1 -0
  161. package/lib/module/agent-avatar/ProceduralAvatar.js +212 -0
  162. package/lib/module/agent-avatar/ProceduralAvatar.js.map +1 -0
  163. package/lib/module/agent-avatar/artwork.js +3 -3
  164. package/lib/module/agent-avatar/artwork.js.map +1 -1
  165. package/lib/module/agent-avatar/avatar-color.js +12 -0
  166. package/lib/module/agent-avatar/avatar-color.js.map +1 -0
  167. package/lib/module/agent-avatar/character-html.js +19 -0
  168. package/lib/module/agent-avatar/character-html.js.map +1 -0
  169. package/lib/module/agent-avatar/character-runtime-module.js +35 -0
  170. package/lib/module/agent-avatar/character-runtime-module.js.map +1 -0
  171. package/lib/module/agent-avatar/character-webview.js +14 -0
  172. package/lib/module/agent-avatar/character-webview.js.map +1 -0
  173. package/lib/module/agent-avatar/config-character.js +32 -0
  174. package/lib/module/agent-avatar/config-character.js.map +1 -0
  175. package/lib/module/agent-avatar/context.js +2 -0
  176. package/lib/module/agent-avatar/context.js.map +1 -1
  177. package/lib/module/agent-avatar/index.js +1 -0
  178. package/lib/module/agent-avatar/index.js.map +1 -1
  179. package/lib/module/agent-avatar/legacy-contours.js +53 -0
  180. package/lib/module/agent-avatar/legacy-contours.js.map +1 -0
  181. package/lib/module/agent-avatar/legacy-recipe.js +104 -0
  182. package/lib/module/agent-avatar/legacy-recipe.js.map +1 -0
  183. package/lib/module/agent-avatar/messages.js +2 -1
  184. package/lib/module/agent-avatar/messages.js.map +1 -1
  185. package/lib/module/agent-avatar/model.js +2 -0
  186. package/lib/module/agent-avatar/model.js.map +1 -1
  187. package/lib/module/agent-creator/AgentCreatorBase.js +242 -18
  188. package/lib/module/agent-creator/AgentCreatorBase.js.map +1 -1
  189. package/lib/module/agent-creator/CharacterControls.js +110 -0
  190. package/lib/module/agent-creator/CharacterControls.js.map +1 -0
  191. package/lib/module/agent-creator/EmotionPicker.js +52 -19
  192. package/lib/module/agent-creator/EmotionPicker.js.map +1 -1
  193. package/lib/module/agent-creator/ShapeArc.js +58 -28
  194. package/lib/module/agent-creator/ShapeArc.js.map +1 -1
  195. package/lib/module/agent-creator/ShapeSilhouette.js +57 -0
  196. package/lib/module/agent-creator/ShapeSilhouette.js.map +1 -0
  197. package/lib/module/agent-creator/constants.js +47 -0
  198. package/lib/module/agent-creator/constants.js.map +1 -1
  199. package/lib/module/agent-creator/messages.js +9 -0
  200. package/lib/module/agent-creator/messages.js.map +1 -1
  201. package/lib/module/agent-creator/shared.js +1 -9
  202. package/lib/module/agent-creator/shared.js.map +1 -1
  203. package/lib/module/agent-thinking/AgentThinking.js +5 -63
  204. package/lib/module/agent-thinking/AgentThinking.js.map +1 -1
  205. package/lib/module/agent-thinking/ThinkingLabel.js +73 -0
  206. package/lib/module/agent-thinking/ThinkingLabel.js.map +1 -0
  207. package/lib/module/chart-cards/TokensChartCard.js +20 -5
  208. package/lib/module/chart-cards/TokensChartCard.js.map +1 -1
  209. package/lib/module/locale/translations/ar.js +11 -1
  210. package/lib/module/locale/translations/ar.js.map +1 -1
  211. package/lib/module/locale/translations/bn.js +11 -1
  212. package/lib/module/locale/translations/bn.js.map +1 -1
  213. package/lib/module/locale/translations/ca.js +11 -1
  214. package/lib/module/locale/translations/ca.js.map +1 -1
  215. package/lib/module/locale/translations/de.js +11 -1
  216. package/lib/module/locale/translations/de.js.map +1 -1
  217. package/lib/module/locale/translations/es.js +11 -1
  218. package/lib/module/locale/translations/es.js.map +1 -1
  219. package/lib/module/locale/translations/fr.js +11 -1
  220. package/lib/module/locale/translations/fr.js.map +1 -1
  221. package/lib/module/locale/translations/hi.js +11 -1
  222. package/lib/module/locale/translations/hi.js.map +1 -1
  223. package/lib/module/locale/translations/id.js +11 -1
  224. package/lib/module/locale/translations/id.js.map +1 -1
  225. package/lib/module/locale/translations/it.js +11 -1
  226. package/lib/module/locale/translations/it.js.map +1 -1
  227. package/lib/module/locale/translations/ja.js +11 -1
  228. package/lib/module/locale/translations/ja.js.map +1 -1
  229. package/lib/module/locale/translations/pt.js +11 -1
  230. package/lib/module/locale/translations/pt.js.map +1 -1
  231. package/lib/module/locale/translations/ru.js +11 -1
  232. package/lib/module/locale/translations/ru.js.map +1 -1
  233. package/lib/module/locale/translations/tr.js +11 -1
  234. package/lib/module/locale/translations/tr.js.map +1 -1
  235. package/lib/module/locale/translations/zh.js +11 -1
  236. package/lib/module/locale/translations/zh.js.map +1 -1
  237. package/lib/module/multi-agent-chat/AvatarHandoff.js +3 -5
  238. package/lib/module/multi-agent-chat/AvatarHandoff.js.map +1 -1
  239. package/lib/module/multi-agent-chat/ChatAgentProfile.js +94 -0
  240. package/lib/module/multi-agent-chat/ChatAgentProfile.js.map +1 -0
  241. package/lib/module/multi-agent-chat/EditorRail.js +4 -0
  242. package/lib/module/multi-agent-chat/EditorRail.js.map +1 -1
  243. package/lib/module/multi-agent-chat/MultiAgentChatBase.js +89 -126
  244. package/lib/module/multi-agent-chat/MultiAgentChatBase.js.map +1 -1
  245. package/lib/module/multi-agent-chat/ReplyRow.js +9 -12
  246. package/lib/module/multi-agent-chat/ReplyRow.js.map +1 -1
  247. package/lib/module/multi-agent-chat/handoff.js +4 -4
  248. package/lib/module/multi-agent-chat/handoff.js.map +1 -1
  249. package/lib/module/project-board/CreateTicketDialog.js +33 -2
  250. package/lib/module/project-board/CreateTicketDialog.js.map +1 -1
  251. package/lib/module/project-board/ProjectBoardBase.js +138 -39
  252. package/lib/module/project-board/ProjectBoardBase.js.map +1 -1
  253. package/lib/module/project-board/TicketCornerGenieSurface.web.js +35 -29
  254. package/lib/module/project-board/TicketCornerGenieSurface.web.js.map +1 -1
  255. package/lib/module/project-board/TicketDetailDialog.js +7 -4
  256. package/lib/module/project-board/TicketDetailDialog.js.map +1 -1
  257. package/lib/module/project-board/TicketGenieSurface.js +17 -7
  258. package/lib/module/project-board/TicketGenieSurface.js.map +1 -1
  259. package/lib/module/project-board/TicketGenieSurface.web.js +1 -1
  260. package/lib/module/project-board/TicketGenieSurface.web.js.map +1 -1
  261. package/lib/module/project-board/TicketPresence.js +119 -0
  262. package/lib/module/project-board/TicketPresence.js.map +1 -0
  263. package/lib/module/project-board/TicketTokenChart.js +20 -0
  264. package/lib/module/project-board/TicketTokenChart.js.map +1 -0
  265. package/lib/module/project-board/context.js +2 -0
  266. package/lib/module/project-board/context.js.map +1 -1
  267. package/lib/module/search/Search.js +12 -3
  268. package/lib/module/search/Search.js.map +1 -1
  269. package/lib/module/sidebar/SidebarToolbar.js +29 -15
  270. package/lib/module/sidebar/SidebarToolbar.js.map +1 -1
  271. package/lib/module/text-field/TextField.js +13 -7
  272. package/lib/module/text-field/TextField.js.map +1 -1
  273. package/lib/typescript/commonjs/agent-avatar/AgentAvatar.d.ts +2 -2
  274. package/lib/typescript/commonjs/agent-avatar/AgentAvatar.d.ts.map +1 -1
  275. package/lib/typescript/commonjs/agent-avatar/AgentAvatarProvider.d.ts +4 -0
  276. package/lib/typescript/commonjs/agent-avatar/AgentAvatarProvider.d.ts.map +1 -0
  277. package/lib/typescript/commonjs/agent-avatar/CharacterAvatar.d.ts +4 -0
  278. package/lib/typescript/commonjs/agent-avatar/CharacterAvatar.d.ts.map +1 -0
  279. package/lib/typescript/commonjs/agent-avatar/ProceduralAvatar.d.ts +6 -0
  280. package/lib/typescript/commonjs/agent-avatar/ProceduralAvatar.d.ts.map +1 -0
  281. package/lib/typescript/commonjs/agent-avatar/artwork.d.ts +2 -1
  282. package/lib/typescript/commonjs/agent-avatar/artwork.d.ts.map +1 -1
  283. package/lib/typescript/commonjs/agent-avatar/avatar-color.d.ts +3 -0
  284. package/lib/typescript/commonjs/agent-avatar/avatar-color.d.ts.map +1 -0
  285. package/lib/typescript/commonjs/agent-avatar/character-html.d.ts +4 -0
  286. package/lib/typescript/commonjs/agent-avatar/character-html.d.ts.map +1 -0
  287. package/lib/typescript/commonjs/agent-avatar/character-runtime-module.d.ts +2 -0
  288. package/lib/typescript/commonjs/agent-avatar/character-runtime-module.d.ts.map +1 -0
  289. package/lib/typescript/commonjs/agent-avatar/character-webview.d.ts +24 -0
  290. package/lib/typescript/commonjs/agent-avatar/character-webview.d.ts.map +1 -0
  291. package/lib/typescript/commonjs/agent-avatar/config-character.d.ts +11 -0
  292. package/lib/typescript/commonjs/agent-avatar/config-character.d.ts.map +1 -0
  293. package/lib/typescript/commonjs/agent-avatar/context.d.ts +12 -0
  294. package/lib/typescript/commonjs/agent-avatar/context.d.ts.map +1 -1
  295. package/lib/typescript/commonjs/agent-avatar/index.d.ts +3 -1
  296. package/lib/typescript/commonjs/agent-avatar/index.d.ts.map +1 -1
  297. package/lib/typescript/commonjs/agent-avatar/legacy-contours.d.ts +5 -0
  298. package/lib/typescript/commonjs/agent-avatar/legacy-contours.d.ts.map +1 -0
  299. package/lib/typescript/commonjs/agent-avatar/legacy-recipe.d.ts +40 -0
  300. package/lib/typescript/commonjs/agent-avatar/legacy-recipe.d.ts.map +1 -0
  301. package/lib/typescript/commonjs/agent-avatar/messages.d.ts +1 -0
  302. package/lib/typescript/commonjs/agent-avatar/messages.d.ts.map +1 -1
  303. package/lib/typescript/commonjs/agent-avatar/model.d.ts +3 -0
  304. package/lib/typescript/commonjs/agent-avatar/model.d.ts.map +1 -1
  305. package/lib/typescript/commonjs/agent-avatar/types.d.ts +10 -1
  306. package/lib/typescript/commonjs/agent-avatar/types.d.ts.map +1 -1
  307. package/lib/typescript/commonjs/agent-creator/AgentCreatorBase.d.ts.map +1 -1
  308. package/lib/typescript/commonjs/agent-creator/CharacterControls.d.ts +8 -0
  309. package/lib/typescript/commonjs/agent-creator/CharacterControls.d.ts.map +1 -0
  310. package/lib/typescript/commonjs/agent-creator/EmotionPicker.d.ts +12 -1
  311. package/lib/typescript/commonjs/agent-creator/EmotionPicker.d.ts.map +1 -1
  312. package/lib/typescript/commonjs/agent-creator/ShapeArc.d.ts +11 -2
  313. package/lib/typescript/commonjs/agent-creator/ShapeArc.d.ts.map +1 -1
  314. package/lib/typescript/commonjs/agent-creator/ShapeSilhouette.d.ts +6 -0
  315. package/lib/typescript/commonjs/agent-creator/ShapeSilhouette.d.ts.map +1 -0
  316. package/lib/typescript/commonjs/agent-creator/constants.d.ts +44 -0
  317. package/lib/typescript/commonjs/agent-creator/constants.d.ts.map +1 -1
  318. package/lib/typescript/commonjs/agent-creator/context.d.ts +9 -0
  319. package/lib/typescript/commonjs/agent-creator/context.d.ts.map +1 -1
  320. package/lib/typescript/commonjs/agent-creator/messages.d.ts +10 -0
  321. package/lib/typescript/commonjs/agent-creator/messages.d.ts.map +1 -1
  322. package/lib/typescript/commonjs/agent-creator/shared.d.ts +1 -1
  323. package/lib/typescript/commonjs/agent-creator/shared.d.ts.map +1 -1
  324. package/lib/typescript/commonjs/agent-thinking/AgentThinking.d.ts.map +1 -1
  325. package/lib/typescript/commonjs/agent-thinking/ThinkingLabel.d.ts +7 -0
  326. package/lib/typescript/commonjs/agent-thinking/ThinkingLabel.d.ts.map +1 -0
  327. package/lib/typescript/commonjs/chart-cards/TokensChartCard.d.ts +3 -1
  328. package/lib/typescript/commonjs/chart-cards/TokensChartCard.d.ts.map +1 -1
  329. package/lib/typescript/commonjs/locale/translations/ar.d.ts.map +1 -1
  330. package/lib/typescript/commonjs/locale/translations/bn.d.ts.map +1 -1
  331. package/lib/typescript/commonjs/locale/translations/ca.d.ts.map +1 -1
  332. package/lib/typescript/commonjs/locale/translations/de.d.ts.map +1 -1
  333. package/lib/typescript/commonjs/locale/translations/es.d.ts.map +1 -1
  334. package/lib/typescript/commonjs/locale/translations/fr.d.ts.map +1 -1
  335. package/lib/typescript/commonjs/locale/translations/hi.d.ts.map +1 -1
  336. package/lib/typescript/commonjs/locale/translations/id.d.ts.map +1 -1
  337. package/lib/typescript/commonjs/locale/translations/it.d.ts.map +1 -1
  338. package/lib/typescript/commonjs/locale/translations/ja.d.ts.map +1 -1
  339. package/lib/typescript/commonjs/locale/translations/pt.d.ts.map +1 -1
  340. package/lib/typescript/commonjs/locale/translations/ru.d.ts.map +1 -1
  341. package/lib/typescript/commonjs/locale/translations/tr.d.ts.map +1 -1
  342. package/lib/typescript/commonjs/locale/translations/zh.d.ts.map +1 -1
  343. package/lib/typescript/commonjs/multi-agent-chat/AvatarHandoff.d.ts.map +1 -1
  344. package/lib/typescript/commonjs/multi-agent-chat/ChatAgentProfile.d.ts +11 -0
  345. package/lib/typescript/commonjs/multi-agent-chat/ChatAgentProfile.d.ts.map +1 -0
  346. package/lib/typescript/commonjs/multi-agent-chat/EditorRail.d.ts.map +1 -1
  347. package/lib/typescript/commonjs/multi-agent-chat/MultiAgentChatBase.d.ts.map +1 -1
  348. package/lib/typescript/commonjs/multi-agent-chat/ReplyRow.d.ts.map +1 -1
  349. package/lib/typescript/commonjs/multi-agent-chat/handoff.d.ts +2 -2
  350. package/lib/typescript/commonjs/multi-agent-chat/handoff.d.ts.map +1 -1
  351. package/lib/typescript/commonjs/project-board/CreateTicketDialog.d.ts.map +1 -1
  352. package/lib/typescript/commonjs/project-board/ProjectBoardBase.d.ts.map +1 -1
  353. package/lib/typescript/commonjs/project-board/TicketCornerGenieSurface.web.d.ts.map +1 -1
  354. package/lib/typescript/commonjs/project-board/TicketDetailDialog.d.ts.map +1 -1
  355. package/lib/typescript/commonjs/project-board/TicketGenieSurface.d.ts.map +1 -1
  356. package/lib/typescript/commonjs/project-board/TicketPresence.d.ts +17 -0
  357. package/lib/typescript/commonjs/project-board/TicketPresence.d.ts.map +1 -0
  358. package/lib/typescript/commonjs/project-board/TicketTokenChart.d.ts +5 -0
  359. package/lib/typescript/commonjs/project-board/TicketTokenChart.d.ts.map +1 -0
  360. package/lib/typescript/commonjs/project-board/context.d.ts +2 -0
  361. package/lib/typescript/commonjs/project-board/context.d.ts.map +1 -1
  362. package/lib/typescript/commonjs/search/Search.d.ts.map +1 -1
  363. package/lib/typescript/commonjs/sidebar/SidebarToolbar.d.ts.map +1 -1
  364. package/lib/typescript/commonjs/text-field/TextField.d.ts.map +1 -1
  365. package/lib/typescript/module/agent-avatar/AgentAvatar.d.ts +2 -2
  366. package/lib/typescript/module/agent-avatar/AgentAvatar.d.ts.map +1 -1
  367. package/lib/typescript/module/agent-avatar/AgentAvatarProvider.d.ts +4 -0
  368. package/lib/typescript/module/agent-avatar/AgentAvatarProvider.d.ts.map +1 -0
  369. package/lib/typescript/module/agent-avatar/CharacterAvatar.d.ts +4 -0
  370. package/lib/typescript/module/agent-avatar/CharacterAvatar.d.ts.map +1 -0
  371. package/lib/typescript/module/agent-avatar/ProceduralAvatar.d.ts +6 -0
  372. package/lib/typescript/module/agent-avatar/ProceduralAvatar.d.ts.map +1 -0
  373. package/lib/typescript/module/agent-avatar/artwork.d.ts +2 -1
  374. package/lib/typescript/module/agent-avatar/artwork.d.ts.map +1 -1
  375. package/lib/typescript/module/agent-avatar/avatar-color.d.ts +3 -0
  376. package/lib/typescript/module/agent-avatar/avatar-color.d.ts.map +1 -0
  377. package/lib/typescript/module/agent-avatar/character-html.d.ts +4 -0
  378. package/lib/typescript/module/agent-avatar/character-html.d.ts.map +1 -0
  379. package/lib/typescript/module/agent-avatar/character-runtime-module.d.ts +2 -0
  380. package/lib/typescript/module/agent-avatar/character-runtime-module.d.ts.map +1 -0
  381. package/lib/typescript/module/agent-avatar/character-webview.d.ts +24 -0
  382. package/lib/typescript/module/agent-avatar/character-webview.d.ts.map +1 -0
  383. package/lib/typescript/module/agent-avatar/config-character.d.ts +11 -0
  384. package/lib/typescript/module/agent-avatar/config-character.d.ts.map +1 -0
  385. package/lib/typescript/module/agent-avatar/context.d.ts +12 -0
  386. package/lib/typescript/module/agent-avatar/context.d.ts.map +1 -1
  387. package/lib/typescript/module/agent-avatar/index.d.ts +3 -1
  388. package/lib/typescript/module/agent-avatar/index.d.ts.map +1 -1
  389. package/lib/typescript/module/agent-avatar/legacy-contours.d.ts +5 -0
  390. package/lib/typescript/module/agent-avatar/legacy-contours.d.ts.map +1 -0
  391. package/lib/typescript/module/agent-avatar/legacy-recipe.d.ts +40 -0
  392. package/lib/typescript/module/agent-avatar/legacy-recipe.d.ts.map +1 -0
  393. package/lib/typescript/module/agent-avatar/messages.d.ts +1 -0
  394. package/lib/typescript/module/agent-avatar/messages.d.ts.map +1 -1
  395. package/lib/typescript/module/agent-avatar/model.d.ts +3 -0
  396. package/lib/typescript/module/agent-avatar/model.d.ts.map +1 -1
  397. package/lib/typescript/module/agent-avatar/types.d.ts +10 -1
  398. package/lib/typescript/module/agent-avatar/types.d.ts.map +1 -1
  399. package/lib/typescript/module/agent-creator/AgentCreatorBase.d.ts.map +1 -1
  400. package/lib/typescript/module/agent-creator/CharacterControls.d.ts +8 -0
  401. package/lib/typescript/module/agent-creator/CharacterControls.d.ts.map +1 -0
  402. package/lib/typescript/module/agent-creator/EmotionPicker.d.ts +12 -1
  403. package/lib/typescript/module/agent-creator/EmotionPicker.d.ts.map +1 -1
  404. package/lib/typescript/module/agent-creator/ShapeArc.d.ts +11 -2
  405. package/lib/typescript/module/agent-creator/ShapeArc.d.ts.map +1 -1
  406. package/lib/typescript/module/agent-creator/ShapeSilhouette.d.ts +6 -0
  407. package/lib/typescript/module/agent-creator/ShapeSilhouette.d.ts.map +1 -0
  408. package/lib/typescript/module/agent-creator/constants.d.ts +44 -0
  409. package/lib/typescript/module/agent-creator/constants.d.ts.map +1 -1
  410. package/lib/typescript/module/agent-creator/context.d.ts +9 -0
  411. package/lib/typescript/module/agent-creator/context.d.ts.map +1 -1
  412. package/lib/typescript/module/agent-creator/messages.d.ts +10 -0
  413. package/lib/typescript/module/agent-creator/messages.d.ts.map +1 -1
  414. package/lib/typescript/module/agent-creator/shared.d.ts +1 -1
  415. package/lib/typescript/module/agent-creator/shared.d.ts.map +1 -1
  416. package/lib/typescript/module/agent-thinking/AgentThinking.d.ts.map +1 -1
  417. package/lib/typescript/module/agent-thinking/ThinkingLabel.d.ts +7 -0
  418. package/lib/typescript/module/agent-thinking/ThinkingLabel.d.ts.map +1 -0
  419. package/lib/typescript/module/chart-cards/TokensChartCard.d.ts +3 -1
  420. package/lib/typescript/module/chart-cards/TokensChartCard.d.ts.map +1 -1
  421. package/lib/typescript/module/locale/translations/ar.d.ts.map +1 -1
  422. package/lib/typescript/module/locale/translations/bn.d.ts.map +1 -1
  423. package/lib/typescript/module/locale/translations/ca.d.ts.map +1 -1
  424. package/lib/typescript/module/locale/translations/de.d.ts.map +1 -1
  425. package/lib/typescript/module/locale/translations/es.d.ts.map +1 -1
  426. package/lib/typescript/module/locale/translations/fr.d.ts.map +1 -1
  427. package/lib/typescript/module/locale/translations/hi.d.ts.map +1 -1
  428. package/lib/typescript/module/locale/translations/id.d.ts.map +1 -1
  429. package/lib/typescript/module/locale/translations/it.d.ts.map +1 -1
  430. package/lib/typescript/module/locale/translations/ja.d.ts.map +1 -1
  431. package/lib/typescript/module/locale/translations/pt.d.ts.map +1 -1
  432. package/lib/typescript/module/locale/translations/ru.d.ts.map +1 -1
  433. package/lib/typescript/module/locale/translations/tr.d.ts.map +1 -1
  434. package/lib/typescript/module/locale/translations/zh.d.ts.map +1 -1
  435. package/lib/typescript/module/multi-agent-chat/AvatarHandoff.d.ts.map +1 -1
  436. package/lib/typescript/module/multi-agent-chat/ChatAgentProfile.d.ts +11 -0
  437. package/lib/typescript/module/multi-agent-chat/ChatAgentProfile.d.ts.map +1 -0
  438. package/lib/typescript/module/multi-agent-chat/EditorRail.d.ts.map +1 -1
  439. package/lib/typescript/module/multi-agent-chat/MultiAgentChatBase.d.ts.map +1 -1
  440. package/lib/typescript/module/multi-agent-chat/ReplyRow.d.ts.map +1 -1
  441. package/lib/typescript/module/multi-agent-chat/handoff.d.ts +2 -2
  442. package/lib/typescript/module/multi-agent-chat/handoff.d.ts.map +1 -1
  443. package/lib/typescript/module/project-board/CreateTicketDialog.d.ts.map +1 -1
  444. package/lib/typescript/module/project-board/ProjectBoardBase.d.ts.map +1 -1
  445. package/lib/typescript/module/project-board/TicketCornerGenieSurface.web.d.ts.map +1 -1
  446. package/lib/typescript/module/project-board/TicketDetailDialog.d.ts.map +1 -1
  447. package/lib/typescript/module/project-board/TicketGenieSurface.d.ts.map +1 -1
  448. package/lib/typescript/module/project-board/TicketPresence.d.ts +17 -0
  449. package/lib/typescript/module/project-board/TicketPresence.d.ts.map +1 -0
  450. package/lib/typescript/module/project-board/TicketTokenChart.d.ts +5 -0
  451. package/lib/typescript/module/project-board/TicketTokenChart.d.ts.map +1 -0
  452. package/lib/typescript/module/project-board/context.d.ts +2 -0
  453. package/lib/typescript/module/project-board/context.d.ts.map +1 -1
  454. package/lib/typescript/module/search/Search.d.ts.map +1 -1
  455. package/lib/typescript/module/sidebar/SidebarToolbar.d.ts.map +1 -1
  456. package/lib/typescript/module/text-field/TextField.d.ts.map +1 -1
  457. package/package.json +8 -3
  458. package/src/agent-avatar/AgentAvatar.tsx +16 -296
  459. package/src/agent-avatar/AgentAvatarProvider.tsx +72 -0
  460. package/src/agent-avatar/CharacterAvatar.tsx +255 -0
  461. package/src/agent-avatar/ProceduralAvatar.tsx +303 -0
  462. package/src/agent-avatar/artwork.ts +3 -3
  463. package/src/agent-avatar/avatar-color.ts +17 -0
  464. package/src/agent-avatar/character-html.ts +21 -0
  465. package/src/agent-avatar/character-runtime-module.ts +37 -0
  466. package/src/agent-avatar/character-webview.ts +24 -0
  467. package/src/agent-avatar/config-character.ts +40 -0
  468. package/src/agent-avatar/context.ts +13 -0
  469. package/src/agent-avatar/index.ts +4 -1
  470. package/src/agent-avatar/legacy-contours.ts +76 -0
  471. package/src/agent-avatar/legacy-recipe.ts +123 -0
  472. package/src/agent-avatar/messages.ts +1 -0
  473. package/src/agent-avatar/model.ts +5 -0
  474. package/src/agent-avatar/types.ts +12 -1
  475. package/src/agent-creator/AgentCreatorBase.tsx +329 -38
  476. package/src/agent-creator/CharacterControls.tsx +146 -0
  477. package/src/agent-creator/EmotionPicker.tsx +43 -21
  478. package/src/agent-creator/ShapeArc.tsx +83 -29
  479. package/src/agent-creator/ShapeSilhouette.tsx +65 -0
  480. package/src/agent-creator/constants.ts +30 -0
  481. package/src/agent-creator/messages.ts +19 -0
  482. package/src/agent-creator/shared.ts +1 -15
  483. package/src/agent-thinking/AgentThinking.tsx +7 -70
  484. package/src/agent-thinking/ThinkingLabel.tsx +74 -0
  485. package/src/chart-cards/TokensChartCard.tsx +10 -6
  486. package/src/locale/translations/ar.ts +10 -1
  487. package/src/locale/translations/bn.ts +10 -1
  488. package/src/locale/translations/ca.ts +10 -1
  489. package/src/locale/translations/de.ts +10 -1
  490. package/src/locale/translations/es.ts +10 -1
  491. package/src/locale/translations/fr.ts +10 -1
  492. package/src/locale/translations/hi.ts +10 -1
  493. package/src/locale/translations/id.ts +10 -1
  494. package/src/locale/translations/it.ts +10 -1
  495. package/src/locale/translations/ja.ts +10 -1
  496. package/src/locale/translations/pt.ts +10 -1
  497. package/src/locale/translations/ru.ts +10 -1
  498. package/src/locale/translations/tr.ts +10 -1
  499. package/src/locale/translations/zh.ts +10 -1
  500. package/src/multi-agent-chat/AvatarHandoff.tsx +2 -5
  501. package/src/multi-agent-chat/ChatAgentProfile.tsx +116 -0
  502. package/src/multi-agent-chat/EditorRail.tsx +2 -0
  503. package/src/multi-agent-chat/MultiAgentChatBase.tsx +123 -175
  504. package/src/multi-agent-chat/ReplyRow.tsx +10 -9
  505. package/src/multi-agent-chat/handoff.ts +4 -4
  506. package/src/project-board/CreateTicketDialog.tsx +28 -4
  507. package/src/project-board/ProjectBoardBase.tsx +115 -21
  508. package/src/project-board/TicketCornerGenieSurface.web.tsx +11 -6
  509. package/src/project-board/TicketDetailDialog.tsx +5 -4
  510. package/src/project-board/TicketGenieSurface.tsx +9 -0
  511. package/src/project-board/TicketGenieSurface.web.tsx +1 -1
  512. package/src/project-board/TicketPresence.tsx +137 -0
  513. package/src/project-board/TicketTokenChart.tsx +14 -0
  514. package/src/project-board/context.ts +2 -0
  515. package/src/search/Search.tsx +6 -5
  516. package/src/sidebar/SidebarToolbar.tsx +27 -15
  517. package/src/text-field/TextField.tsx +12 -7
@@ -12,16 +12,18 @@ import {
12
12
  ScrollView,
13
13
  useWindowDimensions,
14
14
  type View,
15
+ type ViewStyle,
15
16
  } from 'react-native';
16
17
  import { Gesture, GestureDetector } from 'react-native-gesture-handler';
17
18
  import Animated, {
18
- FadeIn,
19
- FadeOut,
20
- LinearTransition,
19
+ cancelAnimation,
20
+ Easing,
21
21
  runOnJS,
22
22
  useAnimatedStyle,
23
23
  useReducedMotion,
24
24
  useSharedValue,
25
+ withTiming,
26
+ type AnimatedStyle,
25
27
  } from 'react-native-reanimated';
26
28
  import { Avatar } from '../avatar';
27
29
  import { Breadcrumb, BreadcrumbItem } from '../breadcrumb';
@@ -48,6 +50,7 @@ import {
48
50
  SourceView,
49
51
  } from './SourcePrimitives';
50
52
  import { TicketDetailDialog } from './TicketDetailDialog';
53
+ import { TicketPresenceList } from './TicketPresence';
51
54
  import { cx, PRIORITY_STYLES } from './constants';
52
55
  import { useProjectBoardPlatform } from './context';
53
56
  import { PROJECT_BOARD_MESSAGES } from './messages';
@@ -260,18 +263,34 @@ export function ProjectBoardBase({
260
263
  const boardRect = useRef<BoardRect | null>(null);
261
264
  const boardNode = useRef<View>(null);
262
265
  const [activeTicket, setActiveTicket] = useState<ProjectTicket | null>(null);
266
+ const [droppingId, setDroppingId] = useState<string | null>(null);
263
267
  const activeId = useRef<string | null>(null);
264
268
  const [over, setOver] = useState<{ columnId: string; index: number } | null>(
265
269
  null,
266
270
  );
267
271
  const targetRef = useRef<typeof over>(null);
268
272
  const beforeKeyboard = useRef<ProjectColumn[] | null>(null);
273
+ const beforePointer = useRef<ProjectColumn[] | null>(null);
269
274
  const keyboardActive = useRef(false);
270
275
  const lastDragEnd = useRef(0);
271
276
  const draggedWidth = useSharedValue(261);
272
277
  const dragX = useSharedValue(0);
273
278
  const dragY = useSharedValue(0);
274
279
  const fingerOffset = useRef({ x: 0, y: 0 });
280
+ const dropFrame = useRef(0);
281
+ const dropGeneration = useRef(0);
282
+ useEffect(() => () => {
283
+ dropGeneration.current++;
284
+ if (dropFrame.current) cancelAnimationFrame(dropFrame.current);
285
+ cancelAnimation(dragX);
286
+ cancelAnimation(dragY);
287
+ cancelAnimation(draggedWidth);
288
+ }, [dragX, dragY, draggedWidth]);
289
+ const finishDrop = useCallback((generation?: number) => {
290
+ if (generation !== undefined && generation !== dropGeneration.current) return;
291
+ setActiveTicket(null);
292
+ setDroppingId(null);
293
+ }, []);
275
294
  const overlayStyle = useAnimatedStyle(
276
295
  () => ({
277
296
  position: 'absolute',
@@ -341,6 +360,13 @@ export function ProjectBoardBase({
341
360
  measure();
342
361
  const rect = ticketRects.current[id];
343
362
  activeId.current = id;
363
+ beforePointer.current = cloneColumns(columnsRef.current);
364
+ dropGeneration.current++;
365
+ cancelAnimation(dragX);
366
+ cancelAnimation(dragY);
367
+ cancelAnimation(draggedWidth);
368
+ if (dropFrame.current) cancelAnimationFrame(dropFrame.current);
369
+ setDroppingId(null);
344
370
  fingerOffset.current = {
345
371
  x: rect ? x - rect.x : 0,
346
372
  y: rect ? y - rect.y : 0,
@@ -373,6 +399,15 @@ export function ProjectBoardBase({
373
399
  targetRef.current = next;
374
400
  setOver(next);
375
401
  }
402
+ if (next) {
403
+ const current = columnsRef.current;
404
+ const source = current.find((column) => column.tickets.some((ticket) => ticket.id === activeId.current));
405
+ if (source && source.id !== next.columnId) {
406
+ const preview = moveTicket(current, activeId.current, next.columnId, next.index);
407
+ columnsRef.current = preview;
408
+ setColumns(preview);
409
+ }
410
+ }
376
411
  const board = boardRect.current;
377
412
  if (board && (x < board.x + 32 || x > board.x + board.width - 32)) {
378
413
  scrollX.current = Math.max(
@@ -419,14 +454,45 @@ export function ProjectBoardBase({
419
454
  AccessibilityInfo.announceForAccessibility?.(
420
455
  `${activeTicket?.code ?? ''}, ${columnsRef.current.find((column) => column.id === target.columnId)?.title ?? target.columnId}`,
421
456
  );
457
+ } else if (beforePointer.current) {
458
+ columnsRef.current = beforePointer.current;
459
+ setColumns(beforePointer.current);
422
460
  }
461
+ beforePointer.current = null;
423
462
  activeId.current = null;
424
463
  targetRef.current = null;
425
464
  lastDragEnd.current = Date.now();
426
- setActiveTicket(null);
427
465
  setOver(null);
466
+ if (reducedMotion) {
467
+ finishDrop();
468
+ return;
469
+ }
470
+ setDroppingId(id);
471
+ const generation = ++dropGeneration.current;
472
+ // React first mounts the destination ticket; its window rect is the
473
+ // landing position even after a column reorder or a scaled preview.
474
+ dropFrame.current = requestAnimationFrame(() => {
475
+ const node = ticketNodes.current[id];
476
+ if (!node) {
477
+ finishDrop(generation);
478
+ return;
479
+ }
480
+ node.measureInWindow((x, y, width) => {
481
+ if (generation !== dropGeneration.current) return;
482
+ if (![x, y, width].every(Number.isFinite) || width <= 0) {
483
+ finishDrop(generation);
484
+ return;
485
+ }
486
+ const config = { duration: 320, easing: Easing.bezier(0.22, 1, 0.36, 1) };
487
+ dragX.value = withTiming(x, config);
488
+ draggedWidth.value = withTiming(width, config);
489
+ dragY.value = withTiming(y, config, (finished) => {
490
+ if (finished) runOnJS(finishDrop)(generation);
491
+ });
492
+ });
493
+ });
428
494
  },
429
- [visibleColumns, activeTicket, commit],
495
+ [visibleColumns, activeTicket, commit, reducedMotion, finishDrop, dragX, dragY, draggedWidth],
430
496
  );
431
497
  function keyboardMove(id: string, key: string) {
432
498
  const current = columnsRef.current;
@@ -792,8 +858,6 @@ export function ProjectBoardBase({
792
858
  contentContainerStyle={{
793
859
  paddingHorizontal: 6,
794
860
  paddingTop: 8,
795
- paddingBottom: 6,
796
- gap: 6,
797
861
  }}
798
862
  accessibilityLabel={column.title}
799
863
  onScroll={(event) => {
@@ -803,18 +867,23 @@ export function ProjectBoardBase({
803
867
  }}
804
868
  scrollEventThrottle={16}
805
869
  >
806
- {column.tickets.map((ticket, index) => (
870
+ <TicketPresenceList tickets={column.tickets} retainId={beforePointer.current ? activeTicket?.id : undefined}>
871
+ {({ ticket, present }, paintStyle, onHeight) => present || (activeTicket?.id === ticket.id && beforePointer.current !== null) ? (
807
872
  <DraggableTicket
808
873
  key={ticket.id}
809
874
  nativeID={`${boardId}-${ticket.id}`}
810
875
  ticket={ticket}
876
+ interactive={present}
811
877
  members={members}
812
878
  dragging={activeTicket?.id === ticket.id}
879
+ landing={droppingId === ticket.id}
880
+ paintStyle={paintStyle}
881
+ onHeight={onHeight}
813
882
  target={
814
- over?.columnId === column.id && over.index === index
883
+ over?.columnId === column.id && over.index === column.tickets.findIndex((item) => item.id === ticket.id)
815
884
  }
816
885
  register={(node) => {
817
- ticketNodes.current[ticket.id] = node;
886
+ if (present) ticketNodes.current[ticket.id] = node;
818
887
  }}
819
888
  onLayout={measure}
820
889
  onOpen={() => openDetail(ticket.id)}
@@ -851,7 +920,16 @@ export function ProjectBoardBase({
851
920
  }
852
921
  }}
853
922
  />
854
- ))}
923
+ ) : (
924
+ <SourceView onLayout={(event) => onHeight(event.nativeEvent.layout.height)}>
925
+ <AnimatedView style={paintStyle}>
926
+ <SourceView className="pointer-events-none opacity-20">
927
+ <TicketCard overlay ticket={ticket} members={members} />
928
+ </SourceView>
929
+ </AnimatedView>
930
+ </SourceView>
931
+ )}
932
+ </TicketPresenceList>
855
933
  {over?.columnId === column.id &&
856
934
  over.index >= column.tickets.length && (
857
935
  <StyledView
@@ -868,7 +946,14 @@ export function ProjectBoardBase({
868
946
  {activeTicket && (
869
947
  <Portal>
870
948
  <OverlayRoot>
871
- <AnimatedView pointerEvents="none" style={overlayStyle}>
949
+ <AnimatedView
950
+ testID="project-board-drag-overlay"
951
+ pointerEvents="none"
952
+ aria-hidden
953
+ accessibilityElementsHidden
954
+ importantForAccessibility="no-hide-descendants"
955
+ style={overlayStyle}
956
+ >
872
957
  <TicketCard overlay ticket={activeTicket} members={members} />
873
958
  </AnimatedView>
874
959
  </OverlayRoot>
@@ -915,8 +1000,12 @@ export function ProjectBoardBase({
915
1000
  function DraggableTicket({
916
1001
  nativeID,
917
1002
  ticket,
1003
+ interactive,
918
1004
  members,
919
1005
  dragging,
1006
+ landing,
1007
+ paintStyle,
1008
+ onHeight,
920
1009
  target,
921
1010
  register,
922
1011
  onLayout,
@@ -928,8 +1017,12 @@ function DraggableTicket({
928
1017
  }: {
929
1018
  nativeID: string;
930
1019
  ticket: ProjectTicket;
1020
+ interactive: boolean;
931
1021
  members: Readonly<Record<string, ProjectMember>>;
932
1022
  dragging: boolean;
1023
+ landing: boolean;
1024
+ paintStyle: AnimatedStyle<ViewStyle>;
1025
+ onHeight: (height: number) => void;
933
1026
  target: boolean;
934
1027
  register: (node: View | null) => void;
935
1028
  onLayout: () => void;
@@ -940,7 +1033,6 @@ function DraggableTicket({
940
1033
  onAccessibleMove: (direction: 'up' | 'down' | 'next' | 'previous') => void;
941
1034
  }) {
942
1035
  const { colors } = useTheme();
943
- const reducedMotion = useReducedMotion();
944
1036
  const { messages: m } = useMessages(PROJECT_BOARD_MESSAGES);
945
1037
  const callbacks = useRef({ onStart, onUpdate, onEnd });
946
1038
  callbacks.current = { onStart, onUpdate, onEnd };
@@ -976,21 +1068,21 @@ function DraggableTicket({
976
1068
  return (
977
1069
  <GestureDetector gesture={gesture}>
978
1070
  <AnimatedView
979
- entering={reducedMotion ? undefined : FadeIn.duration(320)}
980
- exiting={reducedMotion ? undefined : FadeOut.duration(320)}
981
- layout={reducedMotion ? undefined : LinearTransition.duration(260)}
982
1071
  className="shrink-0 touch-manipulation rounded-xl outline-none focus-visible:ring-[2.5px] focus-visible:ring-border-button-hover"
983
1072
  style={{
984
- opacity: dragging ? 0.2 : 1,
1073
+ opacity: landing ? 0 : dragging ? 0.2 : 1,
985
1074
  borderTopWidth: target ? 3 : 0,
986
1075
  borderTopColor: colors.primary,
987
1076
  }}
988
1077
  >
989
1078
  <StyledPressable
990
- nativeID={nativeID}
1079
+ nativeID={interactive ? nativeID : undefined}
991
1080
  ref={register}
992
- onLayout={onLayout}
993
- onPress={onOpen}
1081
+ onLayout={(event) => {
1082
+ onHeight(event.nativeEvent.layout.height);
1083
+ onLayout();
1084
+ }}
1085
+ onPress={interactive ? onOpen : undefined}
994
1086
  accessibilityRole="button"
995
1087
  accessibilityLabel={m.openTicket(ticket.code, ticket.title)}
996
1088
  aria-haspopup="dialog"
@@ -1014,7 +1106,9 @@ function DraggableTicket({
1014
1106
  );
1015
1107
  }}
1016
1108
  >
1017
- <TicketCard ticket={ticket} members={members} />
1109
+ <AnimatedView style={paintStyle}>
1110
+ <TicketCard ticket={ticket} members={members} />
1111
+ </AnimatedView>
1018
1112
  </StyledPressable>
1019
1113
  </AnimatedView>
1020
1114
  </GestureDetector>
@@ -1,9 +1,9 @@
1
- import { useEffect, useRef, type ReactNode } from 'react';
1
+ import { useEffect, useRef, useState, type ReactNode } from 'react';
2
2
  import { useReducedMotion } from 'react-native-reanimated';
3
3
  import { useDialogContext } from '../dialog/context';
4
4
  import { adoptStyleSheet } from '../styles/adopt-style-sheet';
5
+ import { TicketGenieEnteredContext } from './context';
5
6
  const styles = {
6
- 'animate-chart-reveal': 'bloom-ticket-detail-animate-chart-reveal',
7
7
  band: 'bloom-ticket-detail-band',
8
8
  bands: 'bloom-ticket-detail-bands',
9
9
  genie: 'bloom-ticket-detail-genie',
@@ -12,7 +12,7 @@ const styles = {
12
12
  surface: 'bloom-ticket-detail-surface',
13
13
  };
14
14
  const CSS =
15
- '/* React Aria keeps the panel mounted for the shared Genie timeline.\n * Its pixels bend into the bottom-right dock; the shell never slides. */\n.bloom-ticket-detail-panel[data-entering], .bloom-ticket-detail-panel[data-exiting] { animation: bloom-ticket-detail-detail-presence 560ms linear both; }\n.bloom-ticket-detail-panel[data-exiting] { pointer-events: none; }\n@keyframes bloom-ticket-detail-detail-presence {\n from { opacity: 0.9999; }\n to { opacity: 1; }\n}\n.bloom-ticket-detail-genie, .bloom-ticket-detail-surface { position: relative; height: 100%; min-height: 0; }\n.bloom-ticket-detail-genie { isolation: isolate; }\n/* Start the chart\'s own drawing animation only after the visual bands hand\n * back to the live panel, so it never flashes a completed copy first. */\n.bloom-ticket-detail-genie:not([data-entered="true"]) :global(.bloom-ticket-detail-animate-chart-reveal) {\n animation: none;\n clip-path: inset(0 100% 0 0);\n}\n/* Skip the barely visible lead-in while retaining the clean Genie handoff. */\n.bloom-ticket-detail-surface :global(.bloom-ticket-detail-animate-chart-reveal) { animation-delay: -150ms; }\n.bloom-ticket-detail-shadow {\n position: absolute;\n inset: 0;\n border-radius: 24px;\n pointer-events: none;\n transform-origin: right bottom;\n box-shadow: -10px 0 34px rgb(0 0 0 / 0.05), -10px 0 250px rgb(0 0 0 / 0.25);\n}\n.bloom-ticket-detail-bands { position: absolute; inset: 0; pointer-events: none; opacity: 0; }\n.bloom-ticket-detail-band {\n position: absolute;\n top: 0;\n left: 0;\n overflow: hidden;\n contain: strict;\n transform-origin: 0 0;\n will-change: transform;\n backface-visibility: hidden;\n}\n@media (prefers-reduced-motion: reduce) {\n .bloom-ticket-detail-panel[data-entering], .bloom-ticket-detail-panel[data-exiting] { animation: none; }\n}\n';
15
+ '/* React Aria keeps the panel mounted for the shared Genie timeline.\n * Its pixels bend into the bottom-right dock; the shell never slides. */\n.bloom-ticket-detail-panel[data-entering], .bloom-ticket-detail-panel[data-exiting] { animation: bloom-ticket-detail-detail-presence 560ms linear both; }\n.bloom-ticket-detail-panel[data-exiting] { pointer-events: none; }\n@keyframes bloom-ticket-detail-detail-presence {\n from { opacity: 0.9999; }\n to { opacity: 1; }\n}\n.bloom-ticket-detail-genie, .bloom-ticket-detail-surface { position: relative; height: 100%; min-height: 0; }\n.bloom-ticket-detail-genie { isolation: isolate; }\n/* Start the chart\'s own drawing animation only after the visual bands hand\n * back to the live panel, so it never flashes a completed copy first. */\n.bloom-ticket-detail-genie:not([data-entered="true"]) .bloom-ticket-detail-chart svg {\n visibility: hidden;\n}\n.bloom-ticket-detail-shadow {\n position: absolute;\n inset: 0;\n border-radius: 24px;\n pointer-events: none;\n transform-origin: right bottom;\n box-shadow: -10px 0 34px rgb(0 0 0 / 0.05), -10px 0 250px rgb(0 0 0 / 0.25);\n}\n.bloom-ticket-detail-bands { position: absolute; inset: 0; pointer-events: none; opacity: 0; }\n.bloom-ticket-detail-band {\n position: absolute;\n top: 0;\n left: 0;\n overflow: hidden;\n contain: strict;\n transform-origin: 0 0;\n will-change: transform;\n backface-visibility: hidden;\n}\n@media (prefers-reduced-motion: reduce) {\n .bloom-ticket-detail-panel[data-entering], .bloom-ticket-detail-panel[data-exiting] { animation: none; }\n}\n';
16
16
 
17
17
  const DURATION = 480;
18
18
  const FRAMES = 32;
@@ -93,8 +93,8 @@ function rebaseIds(copy: HTMLElement, prefix: string) {
93
93
  * gradient fills differently in each perspective-transformed clipping band.
94
94
  * A shared bitmap keeps every band on the same paint, at device resolution. */
95
95
  async function snapshotCharts(source: HTMLDivElement, copy: HTMLDivElement) {
96
- const charts = source.querySelectorAll<SVGSVGElement>('svg.recharts-surface');
97
- const copies = copy.querySelectorAll<SVGSVGElement>('svg.recharts-surface');
96
+ const charts = source.querySelectorAll<SVGSVGElement>('.bloom-ticket-detail-chart svg');
97
+ const copies = copy.querySelectorAll<SVGSVGElement>('.bloom-ticket-detail-chart svg');
98
98
  await Promise.allSettled(
99
99
  Array.from(charts, async (chart, index) => {
100
100
  const { width, height } = chart.getBoundingClientRect();
@@ -206,6 +206,7 @@ export function TicketCornerGenieSurface({
206
206
  const shadowRef = useRef<HTMLDivElement>(null);
207
207
  const progressRef = useRef(1);
208
208
  const reducedMotion = useReducedMotion();
209
+ const [entered, setEntered] = useState(reducedMotion);
209
210
 
210
211
  useEffect(() => {
211
212
  const root = rootRef.current;
@@ -215,6 +216,7 @@ export function TicketCornerGenieSurface({
215
216
  if (!root || !surface || !bands || !shadow) return;
216
217
  if (reducedMotion) {
217
218
  root.dataset.entered = 'true';
219
+ setEntered(true);
218
220
  surface.style.opacity = '1';
219
221
  shadow.style.opacity = '1';
220
222
  bands.replaceChildren();
@@ -247,7 +249,7 @@ export function TicketCornerGenieSurface({
247
249
  // invisible plot adds SVG serialization, PNG encoding, and two decodes
248
250
  // to the cold opening path without contributing any visible pixels.
249
251
  template
250
- .querySelectorAll('svg.recharts-surface')
252
+ .querySelectorAll('.bloom-ticket-detail-chart svg')
251
253
  .forEach((chart) => chart.remove());
252
254
  }
253
255
  if (cancelled) return;
@@ -346,6 +348,7 @@ export function TicketCornerGenieSurface({
346
348
  progressRef.current = to;
347
349
  if (!exiting) {
348
350
  root.dataset.entered = 'true';
351
+ setEntered(true);
349
352
  surface.style.opacity = '1';
350
353
  shadow.style.opacity = '1';
351
354
  shadow.style.transform = 'none';
@@ -381,6 +384,7 @@ export function TicketCornerGenieSurface({
381
384
  }, [exiting, reducedMotion]);
382
385
 
383
386
  return (
387
+ <TicketGenieEnteredContext.Provider value={entered}>
384
388
  <div ref={rootRef} className={styles.genie}>
385
389
  <div
386
390
  ref={shadowRef}
@@ -397,5 +401,6 @@ export function TicketCornerGenieSurface({
397
401
  </div>
398
402
  <div ref={bandsRef} aria-hidden inert className={styles.bands} />
399
403
  </div>
404
+ </TicketGenieEnteredContext.Provider>
400
405
  );
401
406
  }
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
- import { Linking, Platform, ScrollView } from 'react-native';
3
- import { TokensChartCard } from '../chart-cards/TokensChartCard';
2
+ import { Linking, Platform, ScrollView, useWindowDimensions } from 'react-native';
3
+ import { TicketTokenChart } from './TicketTokenChart';
4
4
  import { useDialogControl } from '../dialog/context';
5
5
  import { useIsRtl } from '../hooks/use-is-rtl';
6
6
  import { useMessages } from '../locale/messages';
@@ -93,6 +93,7 @@ export function TicketDetailDialog({
93
93
  } = useProjectBoardPlatform();
94
94
  const control = useDialogControl();
95
95
  const rtl = useIsRtl();
96
+ const { width: viewportWidth } = useWindowDimensions();
96
97
  const [comment, setComment] = useState('');
97
98
  const [editingDescription, setEditingDescription] = useState(false);
98
99
  const [draft, setDraft] = useState(ticket.description ?? '');
@@ -175,7 +176,7 @@ export function TicketDetailDialog({
175
176
  containerStyle={{
176
177
  alignItems: 'flex-end',
177
178
  justifyContent: 'flex-start',
178
- padding: 12,
179
+ padding: viewportWidth < 640 ? 8 : 12,
179
180
  }}
180
181
  panelStyle={{
181
182
  width: '100%',
@@ -605,7 +606,7 @@ export function TicketDetailDialog({
605
606
  </View>
606
607
  )}
607
608
  {ticket.tokenUsage && (
608
- <TokensChartCard
609
+ <TicketTokenChart
609
610
  title={m.tokens}
610
611
  {...ticket.tokenUsage}
611
612
  plotHeight={190}
@@ -9,6 +9,7 @@ import Animated, {
9
9
  } from 'react-native-reanimated';
10
10
  import { useDialogContext } from '../dialog/context';
11
11
  import { StyledView } from '../styles/styled-primitives';
12
+ import { TicketGenieEnteredContext } from './context';
12
13
  const AnimatedView = Animated.createAnimatedComponent(StyledView);
13
14
  function phase(start: number, end: number, value: number) {
14
15
  'worklet';
@@ -26,6 +27,7 @@ export function TicketGenieSurface({
26
27
  const reduced = useReducedMotion();
27
28
  const progress = useSharedValue(reduced ? 0 : 1);
28
29
  const [height, setHeight] = useState(280);
30
+ const [entered, setEntered] = useState(reduced || !corner);
29
31
  const { height: viewportHeight } = useWindowDimensions();
30
32
  useEffect(() => {
31
33
  progress.value = withTiming(isClosing ? 1 : 0, {
@@ -33,6 +35,11 @@ export function TicketGenieSurface({
33
35
  easing: Easing.bezier(0.42, 0, 0.58, 1),
34
36
  });
35
37
  }, [isClosing, progress, reduced]);
38
+ useEffect(() => {
39
+ if (isClosing || !corner) return;
40
+ const timer = setTimeout(() => setEntered(true), reduced ? 0 : 480);
41
+ return () => clearTimeout(timer);
42
+ }, [isClosing, corner, reduced]);
36
43
  const style = useAnimatedStyle(() => {
37
44
  const p = reduced ? 0 : progress.value;
38
45
  const extension = 64 * phase(0, 0.42, p);
@@ -56,12 +63,14 @@ export function TicketGenieSurface({
56
63
  };
57
64
  }, [progress, reduced, corner, height, viewportHeight]);
58
65
  return (
66
+ <TicketGenieEnteredContext.Provider value={entered}>
59
67
  <AnimatedView
60
68
  onLayout={(event) => setHeight(event.nativeEvent.layout.height || 280)}
61
69
  style={[corner ? { flex: 1, minHeight: 0 } : {}, style]}
62
70
  >
63
71
  {children}
64
72
  </AnimatedView>
73
+ </TicketGenieEnteredContext.Provider>
65
74
  );
66
75
  }
67
76
  export function TicketCornerGenieSurface({
@@ -11,7 +11,7 @@ const styles = {
11
11
  surface: 'bloom-ticket-create-surface',
12
12
  };
13
13
  const CSS =
14
- '.bloom-ticket-create-panel {\n transform-origin: center bottom;\n}\n\n.bloom-ticket-create-genie {\n position: relative;\n isolation: isolate;\n}\n\n.bloom-ticket-create-surface {\n position: relative;\n transform-origin: center bottom;\n}\n\n/* Paint once on a sibling layer, then animate only transform/opacity. The\n * broad shadows no longer sample and re-blur the changing dialog pixels. */\n.bloom-ticket-create-shadow {\n position: absolute;\n inset: 0;\n border-radius: 24px;\n pointer-events: none;\n transform-origin: center bottom;\n box-shadow:\n 0 1px 1px rgb(0 0 0 / 0.04),\n 0 4px 4px rgb(0 0 0 / 0.02),\n 0 0 120px rgb(0 0 0 / 0.1),\n 0 0 48px rgb(0 0 0 / 0.2);\n}\n\n:global(.bloom-ticket-create-dark) .bloom-ticket-create-shadow {\n box-shadow:\n 0 1px 1px rgb(0 0 0 / 0.14),\n 0 4px 4px rgb(0 0 0 / 0.1),\n 0 0 120px rgb(0 0 0 / 0.1),\n 0 0 48px rgb(0 0 0 / 0.2);\n}\n\n/* React Aria waits for this animation before unmounting. Geometry is driven\n * by one Motion timeline on the surface, so the panel itself never slides. */\n.bloom-ticket-create-panel[data-entering],\n.bloom-ticket-create-panel[data-exiting] {\n animation: bloom-ticket-create-ticket-presence 480ms linear both;\n}\n\n.bloom-ticket-create-panel[data-exiting] {\n pointer-events: none;\n}\n\n.bloom-ticket-create-panel[data-entering] .bloom-ticket-create-surface,\n.bloom-ticket-create-panel[data-exiting] .bloom-ticket-create-surface,\n.bloom-ticket-create-panel[data-entering] .bloom-ticket-create-shadow,\n.bloom-ticket-create-panel[data-exiting] .bloom-ticket-create-shadow {\n will-change: transform, opacity;\n}\n\n@keyframes bloom-ticket-create-ticket-presence {\n from { opacity: 0.9999; }\n to { opacity: 1; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .bloom-ticket-create-panel[data-entering],\n .bloom-ticket-create-panel[data-exiting] {\n animation: none;\n }\n\n .bloom-ticket-create-panel[data-entering] .bloom-ticket-create-surface,\n .bloom-ticket-create-panel[data-exiting] .bloom-ticket-create-surface,\n .bloom-ticket-create-panel[data-entering] .bloom-ticket-create-shadow,\n .bloom-ticket-create-panel[data-exiting] .bloom-ticket-create-shadow {\n will-change: auto;\n }\n}\n';
14
+ '.bloom-ticket-create-panel {\n transform-origin: center bottom;\n}\n\n.bloom-ticket-create-genie {\n position: relative;\n isolation: isolate;\n}\n\n.bloom-ticket-create-surface {\n position: relative;\n transform-origin: center bottom;\n}\n\n/* Paint once on a sibling layer, then animate only transform/opacity. The\n * broad shadows no longer sample and re-blur the changing dialog pixels. */\n.bloom-ticket-create-shadow {\n position: absolute;\n inset: 0;\n border-radius: 24px;\n pointer-events: none;\n transform-origin: center bottom;\n box-shadow:\n 0 1px 1px rgb(0 0 0 / 0.04),\n 0 4px 4px rgb(0 0 0 / 0.02),\n 0 0 120px rgb(0 0 0 / 0.1),\n 0 0 48px rgb(0 0 0 / 0.2);\n}\n\n.dark .bloom-ticket-create-shadow {\n box-shadow:\n 0 1px 1px rgb(0 0 0 / 0.14),\n 0 4px 4px rgb(0 0 0 / 0.1),\n 0 0 120px rgb(0 0 0 / 0.1),\n 0 0 48px rgb(0 0 0 / 0.2);\n}\n\n/* React Aria waits for this animation before unmounting. Geometry is driven\n * by one Motion timeline on the surface, so the panel itself never slides. */\n.bloom-ticket-create-panel[data-entering],\n.bloom-ticket-create-panel[data-exiting] {\n animation: bloom-ticket-create-ticket-presence 480ms linear both;\n}\n\n.bloom-ticket-create-panel[data-exiting] {\n pointer-events: none;\n}\n\n.bloom-ticket-create-panel[data-entering] .bloom-ticket-create-surface,\n.bloom-ticket-create-panel[data-exiting] .bloom-ticket-create-surface,\n.bloom-ticket-create-panel[data-entering] .bloom-ticket-create-shadow,\n.bloom-ticket-create-panel[data-exiting] .bloom-ticket-create-shadow {\n will-change: transform, opacity;\n}\n\n@keyframes bloom-ticket-create-ticket-presence {\n from { opacity: 0.9999; }\n to { opacity: 1; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .bloom-ticket-create-panel[data-entering],\n .bloom-ticket-create-panel[data-exiting] {\n animation: none;\n }\n\n .bloom-ticket-create-panel[data-entering] .bloom-ticket-create-surface,\n .bloom-ticket-create-panel[data-exiting] .bloom-ticket-create-surface,\n .bloom-ticket-create-panel[data-entering] .bloom-ticket-create-shadow,\n .bloom-ticket-create-panel[data-exiting] .bloom-ticket-create-shadow {\n will-change: auto;\n }\n}\n';
15
15
 
16
16
  // A smooth, steep center gradient creates a narrow neck with a displacement
17
17
  // no larger than the dialog width. Its cubic mask leaves the top broad while
@@ -0,0 +1,137 @@
1
+ import { useEffect, useRef, useState, type ReactNode } from 'react';
2
+ import { Platform, type ViewStyle } from 'react-native';
3
+ import Animated, {
4
+ cancelAnimation,
5
+ Easing,
6
+ LinearTransition,
7
+ useAnimatedStyle,
8
+ useReducedMotion,
9
+ useSharedValue,
10
+ withTiming,
11
+ type AnimatedStyle,
12
+ } from 'react-native-reanimated';
13
+ import { StyledView } from '../styles/styled-primitives';
14
+ import type { ProjectTicket } from './types';
15
+
16
+ const AnimatedView = Animated.createAnimatedComponent(StyledView);
17
+ const DURATION = 320;
18
+ const WEB = Platform.OS === 'web';
19
+
20
+ export interface PresentTicket {
21
+ ticket: ProjectTicket;
22
+ present: boolean;
23
+ initial: boolean;
24
+ }
25
+
26
+ /** Removed tickets retain only their pixels; interactive nodes leave immediately. */
27
+ export function reconcileTicketPresence(
28
+ previous: PresentTicket[],
29
+ tickets: ProjectTicket[],
30
+ ): PresentTicket[] {
31
+ const ids = new Set(tickets.map((ticket) => ticket.id));
32
+ const next = tickets.map((ticket) => ({
33
+ ticket,
34
+ present: true,
35
+ initial: previous.find((entry) => entry.ticket.id === ticket.id)?.initial ?? false,
36
+ }));
37
+ previous.forEach((entry, index) => {
38
+ if (!ids.has(entry.ticket.id))
39
+ next.splice(Math.min(index, next.length), 0, { ...entry, present: false });
40
+ });
41
+ return next;
42
+ }
43
+
44
+ export function TicketPresenceList({
45
+ tickets,
46
+ retainId,
47
+ children,
48
+ }: {
49
+ tickets: ProjectTicket[];
50
+ retainId?: string | null;
51
+ children: (entry: PresentTicket, paintStyle: AnimatedStyle<ViewStyle>, onHeight: (height: number) => void) => ReactNode;
52
+ }) {
53
+ const [state, setState] = useState(() => ({
54
+ tickets,
55
+ entries: tickets.map((ticket) => ({ ticket, present: true, initial: true })),
56
+ }));
57
+ if (state.tickets !== tickets) {
58
+ setState({ tickets, entries: reconcileTicketPresence(state.entries, tickets) });
59
+ }
60
+ return state.entries.map((entry) => (
61
+ <TicketPresence
62
+ key={entry.ticket.id}
63
+ present={entry.present}
64
+ initial={entry.initial}
65
+ retain={entry.ticket.id === retainId}
66
+ onExit={() => setState((current) => ({
67
+ ...current,
68
+ entries: current.entries.filter((item) => item.present || item.ticket.id !== entry.ticket.id),
69
+ }))}
70
+ >
71
+ {(paintStyle, onHeight) => children(entry, paintStyle, onHeight)}
72
+ </TicketPresence>
73
+ ));
74
+ }
75
+
76
+ /** A shared-value timeline avoids web layout-animation clones and frozen positions. */
77
+ function TicketPresence({
78
+ present,
79
+ initial,
80
+ retain,
81
+ onExit,
82
+ children,
83
+ }: {
84
+ present: boolean;
85
+ initial: boolean;
86
+ retain: boolean;
87
+ onExit: () => void;
88
+ children: (paintStyle: AnimatedStyle<ViewStyle>, onHeight: (height: number) => void) => ReactNode;
89
+ }) {
90
+ const reduced = useReducedMotion();
91
+ const progress = useSharedValue(initial || reduced ? 1 : 0);
92
+ const height = useSharedValue(0);
93
+ const exit = useRef(onExit);
94
+ exit.current = onExit;
95
+ useEffect(() => {
96
+ progress.value = reduced ? Number(present) : withTiming(Number(present), {
97
+ duration: DURATION,
98
+ easing: Easing.bezier(0.22, 1, 0.36, 1),
99
+ });
100
+ return () => cancelAnimation(progress);
101
+ }, [present, reduced, progress]);
102
+ useEffect(() => {
103
+ if (present || retain) return;
104
+ const timer = setTimeout(() => exit.current(), reduced ? 0 : DURATION);
105
+ return () => clearTimeout(timer);
106
+ }, [present, reduced, retain]);
107
+ const slotStyle = useAnimatedStyle(() => {
108
+ const p = Math.min(1, Math.max(0, progress.value));
109
+ return {
110
+ height: height.value > 0 ? (height.value + 6) * p : undefined,
111
+ paddingBottom: 6 * p,
112
+ };
113
+ }, [height, progress]);
114
+ const paintStyle = useAnimatedStyle<ViewStyle>(() => {
115
+ const p = Math.min(1, Math.max(0, progress.value));
116
+ const blur = 6 * (1 - p);
117
+ return {
118
+ opacity: p,
119
+ transform: [{ scale: 0.7 + 0.3 * p }],
120
+ filter: WEB ? `blur(${blur}px)` : [{ blur }],
121
+ } satisfies ViewStyle;
122
+ }, [progress]);
123
+ return (
124
+ <AnimatedView
125
+ className="relative shrink-0 pb-1.5"
126
+ layout={reduced ? undefined : LinearTransition.duration(260)}
127
+ style={slotStyle}
128
+ pointerEvents={present ? 'auto' : 'none'}
129
+ aria-hidden={!present}
130
+ accessibilityElementsHidden={!present}
131
+ importantForAccessibility={present ? 'auto' : 'no-hide-descendants'}
132
+ {...(WEB && !present ? { inert: true } : {})}
133
+ >
134
+ {children(paintStyle, (value) => { height.value = value; })}
135
+ </AnimatedView>
136
+ );
137
+ }
@@ -0,0 +1,14 @@
1
+ import { useContext, type ComponentProps } from 'react';
2
+ import { TokensChartCard } from '../chart-cards/TokensChartCard';
3
+ import { StyledView } from '../styles/styled-primitives';
4
+ import { TicketGenieEnteredContext } from './context';
5
+
6
+ /** Keep the card's geometry in the entrance copy, then restart its live plot clock. */
7
+ export function TicketTokenChart(props: ComponentProps<typeof TokensChartCard>) {
8
+ const entered = useContext(TicketGenieEnteredContext);
9
+ return (
10
+ <StyledView className="bloom-ticket-detail-chart">
11
+ <TokensChartCard {...props} animate={entered} />
12
+ </StyledView>
13
+ );
14
+ }
@@ -36,6 +36,8 @@ export interface ProjectBoardPlatform {
36
36
  }
37
37
  export const ProjectBoardPlatformContext =
38
38
  createContext<ProjectBoardPlatform | null>(null);
39
+ /** The plot starts its own reveal after the panel has reached its live pixels. */
40
+ export const TicketGenieEnteredContext = createContext(true);
39
41
  export function useProjectBoardPlatform() {
40
42
  const platform = useContext(ProjectBoardPlatformContext);
41
43
  if (!platform)
@@ -2,6 +2,7 @@ import { forwardRef, useCallback, useMemo, useRef } from 'react';
2
2
  import { Platform, type TextInput, View } from 'react-native';
3
3
 
4
4
  import { mergeRefs } from '../hooks/merge-refs';
5
+ import { useDirectionProps, useIsRtl } from '../hooks/use-is-rtl';
5
6
  import { useCommonMessages } from '../locale/common-messages';
6
7
  import { useMessages } from '../locale/messages';
7
8
  import { SEARCH_MESSAGES } from './messages';
@@ -25,6 +26,8 @@ type SearchProps = Omit<TextFieldInputProps, 'label'> & {
25
26
  export const Search = forwardRef<TextInput, SearchProps>(
26
27
  function Search({ value, label: labelProp, onClearText, onFocus, onPressIn, ...rest }, ref) {
27
28
  const theme = useTheme();
29
+ const direction = useDirectionProps();
30
+ const rtl = useIsRtl();
28
31
  const common = useCommonMessages();
29
32
  const { messages } = useMessages(SEARCH_MESSAGES);
30
33
  const label = labelProp ?? common.search;
@@ -66,7 +69,7 @@ export const Search = forwardRef<TextInput, SearchProps>(
66
69
  );
67
70
 
68
71
  return (
69
- <View style={[a.w_full, a.relative]}>
72
+ <View {...direction} style={[a.w_full, a.relative]}>
70
73
  <TextField radius={borderRadius.full}>
71
74
  <TextFieldIcon icon={MagnifyingGlassIcon} />
72
75
  <TextFieldInput
@@ -81,7 +84,7 @@ export const Search = forwardRef<TextInput, SearchProps>(
81
84
  autoCorrect={false}
82
85
  autoComplete="off"
83
86
  autoCapitalize="none"
84
- style={showClear ? { paddingRight: 24 } : undefined}
87
+ style={showClear ? (rtl ? { paddingLeft: 24 } : { paddingRight: 24 }) : undefined}
85
88
  {...rest}
86
89
  onPressIn={handlePressIn}
87
90
  onFocus={handleFocus}
@@ -94,10 +97,8 @@ export const Search = forwardRef<TextInput, SearchProps>(
94
97
  a.absolute,
95
98
  a.z_20,
96
99
  a.my_auto,
97
- a.inset_0,
98
100
  a.justify_center,
99
- a.pr_sm,
100
- { left: 'auto' },
101
+ { top: 0, bottom: 0, insetInlineEnd: 0, paddingInlineEnd: 8 },
101
102
  ]}>
102
103
  {/* Neutral and transparent. `variant="ghost"` painted an accent
103
104
  wash inside the field, which read as a state the field was not