@elizaos/app-core 2.0.0-alpha.10

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 (399) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +90 -0
  3. package/src/App.tsx +472 -0
  4. package/src/actions/character.test.ts +139 -0
  5. package/src/actions/character.ts +152 -0
  6. package/src/actions/chat-helpers.ts +100 -0
  7. package/src/actions/cloud.ts +59 -0
  8. package/src/actions/index.ts +12 -0
  9. package/src/actions/lifecycle.ts +175 -0
  10. package/src/actions/onboarding.ts +46 -0
  11. package/src/actions/triggers.ts +190 -0
  12. package/src/ambient.d.ts +16 -0
  13. package/src/api/client.ts +5516 -0
  14. package/src/api/index.ts +1 -0
  15. package/src/autonomy/index.ts +477 -0
  16. package/src/bridge/capacitor-bridge.ts +295 -0
  17. package/src/bridge/electrobun-rpc.ts +58 -0
  18. package/src/bridge/electrobun-runtime.ts +28 -0
  19. package/src/bridge/index.ts +5 -0
  20. package/src/bridge/native-plugins.ts +134 -0
  21. package/src/bridge/plugin-bridge.ts +352 -0
  22. package/src/bridge/storage-bridge.ts +162 -0
  23. package/src/chat/index.ts +250 -0
  24. package/src/coding/index.ts +43 -0
  25. package/src/components/AdvancedPageView.tsx +362 -0
  26. package/src/components/AgentActivityBox.tsx +49 -0
  27. package/src/components/ApiKeyConfig.tsx +224 -0
  28. package/src/components/AppsPageView.tsx +52 -0
  29. package/src/components/AppsView.tsx +293 -0
  30. package/src/components/AvatarLoader.tsx +86 -0
  31. package/src/components/AvatarSelector.tsx +223 -0
  32. package/src/components/BscTradePanel.tsx +549 -0
  33. package/src/components/BugReportModal.tsx +499 -0
  34. package/src/components/CharacterView.tsx +1645 -0
  35. package/src/components/ChatAvatar.test.ts +96 -0
  36. package/src/components/ChatAvatar.tsx +147 -0
  37. package/src/components/ChatComposer.tsx +330 -0
  38. package/src/components/ChatMessage.tsx +448 -0
  39. package/src/components/ChatModalView.test.tsx +118 -0
  40. package/src/components/ChatModalView.tsx +125 -0
  41. package/src/components/ChatView.tsx +992 -0
  42. package/src/components/CloudSourceControls.tsx +80 -0
  43. package/src/components/CodingAgentSettingsSection.tsx +536 -0
  44. package/src/components/CommandPalette.tsx +284 -0
  45. package/src/components/CompanionSceneHost.tsx +497 -0
  46. package/src/components/CompanionShell.tsx +31 -0
  47. package/src/components/CompanionView.tsx +109 -0
  48. package/src/components/ConfigPageView.tsx +758 -0
  49. package/src/components/ConfigSaveFooter.tsx +41 -0
  50. package/src/components/ConfirmModal.tsx +379 -0
  51. package/src/components/ConnectionFailedBanner.tsx +91 -0
  52. package/src/components/ConnectorsPageView.tsx +13 -0
  53. package/src/components/ConversationsSidebar.tsx +279 -0
  54. package/src/components/CustomActionEditor.tsx +1125 -0
  55. package/src/components/CustomActionsPanel.tsx +288 -0
  56. package/src/components/CustomActionsView.tsx +322 -0
  57. package/src/components/DatabasePageView.tsx +55 -0
  58. package/src/components/DatabaseView.tsx +814 -0
  59. package/src/components/ElizaCloudDashboard.tsx +1696 -0
  60. package/src/components/EmotePicker.tsx +529 -0
  61. package/src/components/ErrorBoundary.tsx +76 -0
  62. package/src/components/FineTuningView.tsx +1077 -0
  63. package/src/components/GameView.tsx +552 -0
  64. package/src/components/GameViewOverlay.tsx +133 -0
  65. package/src/components/GlobalEmoteOverlay.tsx +155 -0
  66. package/src/components/Header.test.tsx +413 -0
  67. package/src/components/Header.tsx +403 -0
  68. package/src/components/HeartbeatsView.tsx +1003 -0
  69. package/src/components/InventoryView.tsx +385 -0
  70. package/src/components/KnowledgeView.tsx +1128 -0
  71. package/src/components/LanguageDropdown.tsx +188 -0
  72. package/src/components/LifoMonitorPanel.tsx +196 -0
  73. package/src/components/LifoSandboxView.tsx +499 -0
  74. package/src/components/LoadingScreen.tsx +77 -0
  75. package/src/components/LogsPageView.tsx +17 -0
  76. package/src/components/LogsView.tsx +239 -0
  77. package/src/components/MediaGalleryView.tsx +433 -0
  78. package/src/components/MediaSettingsSection.tsx +893 -0
  79. package/src/components/MessageContent.tsx +815 -0
  80. package/src/components/OnboardingWizard.test.tsx +107 -0
  81. package/src/components/OnboardingWizard.tsx +189 -0
  82. package/src/components/PairingView.tsx +110 -0
  83. package/src/components/PermissionsSection.tsx +1186 -0
  84. package/src/components/PluginsPageView.tsx +9 -0
  85. package/src/components/PluginsView.tsx +3157 -0
  86. package/src/components/ProviderSwitcher.tsx +908 -0
  87. package/src/components/RestartBanner.tsx +76 -0
  88. package/src/components/RuntimeView.tsx +460 -0
  89. package/src/components/SaveCommandModal.tsx +211 -0
  90. package/src/components/SecretsView.tsx +569 -0
  91. package/src/components/SettingsView.tsx +825 -0
  92. package/src/components/ShellOverlays.tsx +41 -0
  93. package/src/components/ShortcutsOverlay.tsx +155 -0
  94. package/src/components/SkillsView.tsx +1435 -0
  95. package/src/components/StartupFailureView.tsx +63 -0
  96. package/src/components/StreamView.tsx +483 -0
  97. package/src/components/StripeEmbeddedCheckout.tsx +155 -0
  98. package/src/components/SubscriptionStatus.tsx +640 -0
  99. package/src/components/SystemWarningBanner.tsx +71 -0
  100. package/src/components/ThemeToggle.tsx +100 -0
  101. package/src/components/TrajectoriesView.tsx +526 -0
  102. package/src/components/TrajectoryDetailView.tsx +426 -0
  103. package/src/components/TriggersView.tsx +1 -0
  104. package/src/components/VectorBrowserView.tsx +1633 -0
  105. package/src/components/VoiceConfigView.tsx +675 -0
  106. package/src/components/VrmStage.test.ts +219 -0
  107. package/src/components/VrmStage.tsx +432 -0
  108. package/src/components/WhatsAppQrOverlay.tsx +230 -0
  109. package/src/components/__tests__/chainConfig.test.ts +220 -0
  110. package/src/components/apps/AppDetailPane.tsx +242 -0
  111. package/src/components/apps/AppsCatalogGrid.tsx +137 -0
  112. package/src/components/apps/extensions/HyperscapeAppDetailPanel.tsx +577 -0
  113. package/src/components/apps/extensions/registry.ts +16 -0
  114. package/src/components/apps/extensions/types.ts +9 -0
  115. package/src/components/apps/helpers.ts +44 -0
  116. package/src/components/avatar/VrmAnimationLoader.test.ts +164 -0
  117. package/src/components/avatar/VrmAnimationLoader.ts +151 -0
  118. package/src/components/avatar/VrmBlinkController.ts +118 -0
  119. package/src/components/avatar/VrmCameraManager.ts +407 -0
  120. package/src/components/avatar/VrmEngine.ts +2678 -0
  121. package/src/components/avatar/VrmFootShadow.ts +96 -0
  122. package/src/components/avatar/VrmViewer.tsx +421 -0
  123. package/src/components/avatar/__tests__/VrmCameraManager.test.ts +168 -0
  124. package/src/components/avatar/__tests__/VrmEngine.test.ts +1574 -0
  125. package/src/components/avatar/mixamoVRMRigMap.ts +62 -0
  126. package/src/components/avatar/retargetMixamoFbxToVrm.ts +144 -0
  127. package/src/components/avatar/retargetMixamoGltfToVrm.ts +119 -0
  128. package/src/components/chainConfig.ts +380 -0
  129. package/src/components/companion/CompanionHeader.tsx +47 -0
  130. package/src/components/companion/CompanionSceneHost.tsx +1 -0
  131. package/src/components/companion/VrmStage.tsx +2 -0
  132. package/src/components/companion/__tests__/walletUtils.test.ts +742 -0
  133. package/src/components/companion/walletUtils.ts +290 -0
  134. package/src/components/companion-shell-styles.test.ts +142 -0
  135. package/src/components/companion-shell-styles.ts +270 -0
  136. package/src/components/confirm-delete-control.tsx +69 -0
  137. package/src/components/conversations/ConversationListItem.tsx +185 -0
  138. package/src/components/conversations/conversation-utils.ts +151 -0
  139. package/src/components/format.ts +131 -0
  140. package/src/components/index.ts +94 -0
  141. package/src/components/inventory/CopyableAddress.tsx +41 -0
  142. package/src/components/inventory/InventoryToolbar.tsx +142 -0
  143. package/src/components/inventory/NftGrid.tsx +99 -0
  144. package/src/components/inventory/TokenLogo.tsx +71 -0
  145. package/src/components/inventory/TokensTable.tsx +216 -0
  146. package/src/components/inventory/constants.ts +170 -0
  147. package/src/components/inventory/index.ts +29 -0
  148. package/src/components/inventory/media-url.test.ts +38 -0
  149. package/src/components/inventory/media-url.ts +36 -0
  150. package/src/components/inventory/useInventoryData.ts +460 -0
  151. package/src/components/knowledge-upload-image.ts +215 -0
  152. package/src/components/labels.ts +46 -0
  153. package/src/components/onboarding/ActivateStep.tsx +30 -0
  154. package/src/components/onboarding/ConnectionStep.tsx +1530 -0
  155. package/src/components/onboarding/IdentityStep.tsx +147 -0
  156. package/src/components/onboarding/OnboardingPanel.tsx +39 -0
  157. package/src/components/onboarding/OnboardingStepNav.tsx +31 -0
  158. package/src/components/onboarding/PermissionsStep.tsx +20 -0
  159. package/src/components/onboarding/RpcStep.tsx +402 -0
  160. package/src/components/onboarding/WakeUpStep.tsx +184 -0
  161. package/src/components/permissions/PermissionIcon.tsx +25 -0
  162. package/src/components/permissions/StreamingPermissions.tsx +413 -0
  163. package/src/components/plugins/showcase-data.ts +481 -0
  164. package/src/components/shared/ShellHeaderControls.tsx +193 -0
  165. package/src/components/shared-companion-scene-context.ts +15 -0
  166. package/src/components/skeletons.tsx +88 -0
  167. package/src/components/stream/ActivityFeed.tsx +113 -0
  168. package/src/components/stream/AvatarPip.tsx +10 -0
  169. package/src/components/stream/ChatContent.tsx +126 -0
  170. package/src/components/stream/ChatTicker.tsx +55 -0
  171. package/src/components/stream/IdleContent.tsx +73 -0
  172. package/src/components/stream/StatusBar.tsx +469 -0
  173. package/src/components/stream/StreamSettings.tsx +506 -0
  174. package/src/components/stream/StreamTerminal.tsx +94 -0
  175. package/src/components/stream/StreamVoiceConfig.tsx +160 -0
  176. package/src/components/stream/helpers.ts +134 -0
  177. package/src/components/stream/overlays/OverlayLayer.tsx +75 -0
  178. package/src/components/stream/overlays/built-in/ActionTickerWidget.tsx +64 -0
  179. package/src/components/stream/overlays/built-in/AlertPopupWidget.tsx +87 -0
  180. package/src/components/stream/overlays/built-in/BrandingWidget.tsx +51 -0
  181. package/src/components/stream/overlays/built-in/CustomHtmlWidget.tsx +105 -0
  182. package/src/components/stream/overlays/built-in/PeonGlassWidget.tsx +265 -0
  183. package/src/components/stream/overlays/built-in/PeonHudWidget.tsx +247 -0
  184. package/src/components/stream/overlays/built-in/PeonSakuraWidget.tsx +278 -0
  185. package/src/components/stream/overlays/built-in/ThoughtBubbleWidget.tsx +77 -0
  186. package/src/components/stream/overlays/built-in/ViewerCountWidget.tsx +46 -0
  187. package/src/components/stream/overlays/built-in/index.ts +13 -0
  188. package/src/components/stream/overlays/registry.ts +22 -0
  189. package/src/components/stream/overlays/types.ts +90 -0
  190. package/src/components/stream/overlays/useOverlayLayout.ts +218 -0
  191. package/src/components/trajectory-format.ts +50 -0
  192. package/src/components/ui-badges.tsx +109 -0
  193. package/src/components/ui-switch.tsx +57 -0
  194. package/src/components/vector-browser-three.ts +27 -0
  195. package/src/config/config-catalog.ts +1092 -0
  196. package/src/config/config-field.tsx +1901 -0
  197. package/src/config/config-renderer.tsx +730 -0
  198. package/src/config/index.ts +11 -0
  199. package/src/config/ui-renderer.tsx +1751 -0
  200. package/src/config/ui-spec.ts +256 -0
  201. package/src/events/index.ts +89 -0
  202. package/src/hooks/index.ts +13 -0
  203. package/src/hooks/useBugReport.tsx +43 -0
  204. package/src/hooks/useCanvasWindow.ts +372 -0
  205. package/src/hooks/useChatAvatarVoice.ts +111 -0
  206. package/src/hooks/useContextMenu.ts +127 -0
  207. package/src/hooks/useKeyboardShortcuts.ts +86 -0
  208. package/src/hooks/useLifoSync.ts +143 -0
  209. package/src/hooks/useMemoryMonitor.ts +334 -0
  210. package/src/hooks/useRenderGuard.ts +43 -0
  211. package/src/hooks/useRetakeCapture.ts +67 -0
  212. package/src/hooks/useStreamPopoutNavigation.ts +27 -0
  213. package/src/hooks/useTimeout.ts +37 -0
  214. package/src/hooks/useVoiceChat.ts +1441 -0
  215. package/src/hooks/useWhatsAppPairing.ts +123 -0
  216. package/src/i18n/index.ts +76 -0
  217. package/src/i18n/locales/en.json +1194 -0
  218. package/src/i18n/locales/es.json +1194 -0
  219. package/src/i18n/locales/ko.json +1194 -0
  220. package/src/i18n/locales/pt.json +1194 -0
  221. package/src/i18n/locales/zh-CN.json +1194 -0
  222. package/src/i18n/messages.ts +21 -0
  223. package/src/index.ts +6 -0
  224. package/src/navigation/index.ts +282 -0
  225. package/src/navigation.test.ts +189 -0
  226. package/src/onboarding-config.test.ts +104 -0
  227. package/src/onboarding-config.ts +114 -0
  228. package/src/platform/browser-launch.test.ts +94 -0
  229. package/src/platform/browser-launch.ts +149 -0
  230. package/src/platform/index.ts +58 -0
  231. package/src/platform/init.ts +236 -0
  232. package/src/platform/lifo.ts +215 -0
  233. package/src/providers/index.ts +99 -0
  234. package/src/state/AppContext.tsx +5846 -0
  235. package/src/state/index.ts +6 -0
  236. package/src/state/internal.ts +86 -0
  237. package/src/state/onboarding-resume.test.ts +135 -0
  238. package/src/state/onboarding-resume.ts +263 -0
  239. package/src/state/parsers.test.ts +124 -0
  240. package/src/state/parsers.ts +308 -0
  241. package/src/state/persistence.ts +321 -0
  242. package/src/state/shell-routing.ts +32 -0
  243. package/src/state/types.ts +701 -0
  244. package/src/state/ui-preferences.ts +3 -0
  245. package/src/state/useApp.ts +23 -0
  246. package/src/state/vrm.ts +76 -0
  247. package/src/stories/AppMockProvider.tsx +32 -0
  248. package/src/stories/ChatEmptyState.stories.tsx +27 -0
  249. package/src/stories/ChatMessage.stories.tsx +115 -0
  250. package/src/stories/CompanionHeader.stories.tsx +74 -0
  251. package/src/stories/CompanionView.stories.tsx +33 -0
  252. package/src/stories/ConversationListItem.stories.tsx +102 -0
  253. package/src/stories/TypingIndicator.stories.tsx +28 -0
  254. package/src/styles/anime.css +6324 -0
  255. package/src/styles/base.css +196 -0
  256. package/src/styles/onboarding-game.css +738 -0
  257. package/src/styles/styles.css +2087 -0
  258. package/src/styles/xterm.css +241 -0
  259. package/src/types/index.ts +715 -0
  260. package/src/types/react-test-renderer.d.ts +45 -0
  261. package/src/utils/asset-url.ts +110 -0
  262. package/src/utils/assistant-text.ts +172 -0
  263. package/src/utils/clipboard.ts +41 -0
  264. package/src/utils/desktop-dialogs.ts +80 -0
  265. package/src/utils/index.ts +6 -0
  266. package/src/utils/number-parsing.ts +125 -0
  267. package/src/utils/openExternalUrl.ts +20 -0
  268. package/src/utils/spoken-text.ts +65 -0
  269. package/src/utils/streaming-text.ts +120 -0
  270. package/src/voice/index.ts +1 -0
  271. package/src/voice/types.ts +197 -0
  272. package/src/wallet-rpc.ts +176 -0
  273. package/test/app/AppContext.pty-sessions.test.tsx +143 -0
  274. package/test/app/MessageContent.test.tsx +326 -0
  275. package/test/app/PermissionsOnboarding.test.tsx +356 -0
  276. package/test/app/PermissionsSection.test.tsx +573 -0
  277. package/test/app/advanced-trajectory-fine-tuning.e2e.test.ts +393 -0
  278. package/test/app/agent-activity-box.test.tsx +132 -0
  279. package/test/app/agent-transfer-lock.test.ts +274 -0
  280. package/test/app/api-client-electron-fallback.test.ts +139 -0
  281. package/test/app/api-client-timeout.test.ts +75 -0
  282. package/test/app/api-client-ws.test.ts +98 -0
  283. package/test/app/api-client.ws-max-reconnect.test.ts +139 -0
  284. package/test/app/api-client.ws-reconnect.test.ts +157 -0
  285. package/test/app/app-context-autonomy-events.test.ts +478 -0
  286. package/test/app/apps-page-view.test.ts +114 -0
  287. package/test/app/apps-view.test.ts +769 -0
  288. package/test/app/autonomous-workflows.e2e.test.ts +765 -0
  289. package/test/app/autonomy-events.test.ts +150 -0
  290. package/test/app/avatar-selector.test.tsx +52 -0
  291. package/test/app/bsc-trade-panel.test.tsx +134 -0
  292. package/test/app/bug-report-modal.test.tsx +353 -0
  293. package/test/app/character-customization.e2e.test.ts +1199 -0
  294. package/test/app/chat-advanced-features.e2e.test.ts +706 -0
  295. package/test/app/chat-composer.test.tsx +181 -0
  296. package/test/app/chat-language-header.test.ts +64 -0
  297. package/test/app/chat-message.test.tsx +222 -0
  298. package/test/app/chat-modal-view.test.tsx +191 -0
  299. package/test/app/chat-routine-filter.test.ts +96 -0
  300. package/test/app/chat-send-lock.test.ts +1465 -0
  301. package/test/app/chat-stream-api-client.test.tsx +390 -0
  302. package/test/app/chat-view-game-modal.test.tsx +661 -0
  303. package/test/app/chat-view.test.tsx +877 -0
  304. package/test/app/cloud-api.e2e.test.ts +258 -0
  305. package/test/app/cloud-login-flow.e2e.test.ts +494 -0
  306. package/test/app/cloud-login-lock.test.ts +411 -0
  307. package/test/app/command-palette.test.tsx +184 -0
  308. package/test/app/command-registry.test.ts +75 -0
  309. package/test/app/companion-greeting-wave.test.tsx +425 -0
  310. package/test/app/companion-stale-conversation.test.tsx +447 -0
  311. package/test/app/companion-view.test.tsx +686 -0
  312. package/test/app/confirm-delete-control.test.ts +79 -0
  313. package/test/app/confirm-modal.test.tsx +219 -0
  314. package/test/app/connectors-ui.e2e.test.ts +508 -0
  315. package/test/app/conversations-sidebar-game-modal.test.tsx +260 -0
  316. package/test/app/conversations-sidebar.test.tsx +160 -0
  317. package/test/app/custom-actions-smoke.test.ts +387 -0
  318. package/test/app/custom-avatar-api-client.test.ts +207 -0
  319. package/test/app/desktop-utils.test.ts +145 -0
  320. package/test/app/electrobun-rpc-bridge.test.ts +83 -0
  321. package/test/app/events.test.ts +88 -0
  322. package/test/app/export-import-flows.e2e.test.ts +700 -0
  323. package/test/app/fine-tuning-view.test.ts +471 -0
  324. package/test/app/game-view-auth-session.test.tsx +186 -0
  325. package/test/app/game-view.test.ts +444 -0
  326. package/test/app/global-emote-overlay.test.tsx +106 -0
  327. package/test/app/header-status.test.tsx +149 -0
  328. package/test/app/i18n.test.ts +152 -0
  329. package/test/app/inventory-bsc-view.test.ts +940 -0
  330. package/test/app/knowledge-ui.e2e.test.ts +762 -0
  331. package/test/app/knowledge-upload-helpers.test.ts +124 -0
  332. package/test/app/lifecycle-lock.test.ts +267 -0
  333. package/test/app/lifo-popout-utils.test.ts +208 -0
  334. package/test/app/lifo-safe-endpoint.test.ts +34 -0
  335. package/test/app/loading-screen.test.tsx +45 -0
  336. package/test/app/memory-monitor.test.ts +332 -0
  337. package/test/app/navigation.test.tsx +22 -0
  338. package/test/app/onboarding-finish-lock.test.ts +663 -0
  339. package/test/app/onboarding-language.test.tsx +160 -0
  340. package/test/app/onboarding-steps.test.tsx +375 -0
  341. package/test/app/open-external-url.test.ts +65 -0
  342. package/test/app/pages-navigation-smoke.e2e.test.ts +633 -0
  343. package/test/app/pairing-lock.test.ts +260 -0
  344. package/test/app/pairing-view.test.tsx +74 -0
  345. package/test/app/permissions-section.test.ts +432 -0
  346. package/test/app/plugin-bridge.test.ts +109 -0
  347. package/test/app/plugins-ui.e2e.test.ts +605 -0
  348. package/test/app/plugins-view-game-modal.test.tsx +650 -0
  349. package/test/app/plugins-view-toggle-restart.test.ts +129 -0
  350. package/test/app/provider-dropdown-default.test.tsx +302 -0
  351. package/test/app/restart-banner.test.tsx +197 -0
  352. package/test/app/retake-capture.test.ts +84 -0
  353. package/test/app/sandbox-api-client.test.ts +108 -0
  354. package/test/app/save-command-modal.test.tsx +109 -0
  355. package/test/app/secrets-view.test.tsx +92 -0
  356. package/test/app/settings-control-styles.test.tsx +142 -0
  357. package/test/app/settings-reset.e2e.test.ts +726 -0
  358. package/test/app/settings-sections.e2e.test.ts +614 -0
  359. package/test/app/shared-format.test.ts +44 -0
  360. package/test/app/shared-switch.test.ts +69 -0
  361. package/test/app/shell-mode-switching.e2e.test.ts +829 -0
  362. package/test/app/shell-mode-tab-memory.test.tsx +58 -0
  363. package/test/app/shell-overlays.test.tsx +50 -0
  364. package/test/app/shortcuts-overlay.test.tsx +111 -0
  365. package/test/app/sse-interruption.test.ts +122 -0
  366. package/test/app/startup-asset-missing.e2e.test.ts +126 -0
  367. package/test/app/startup-backend-missing.e2e.test.ts +118 -0
  368. package/test/app/startup-chat.e2e.test.ts +305 -0
  369. package/test/app/startup-conversation-restore.test.tsx +344 -0
  370. package/test/app/startup-failure-view.test.tsx +103 -0
  371. package/test/app/startup-onboarding.e2e.test.ts +618 -0
  372. package/test/app/startup-timeout.test.tsx +80 -0
  373. package/test/app/startup-token-401.e2e.test.ts +103 -0
  374. package/test/app/stream-helpers.test.ts +46 -0
  375. package/test/app/stream-popout-navigation.test.tsx +41 -0
  376. package/test/app/stream-status-bar.test.tsx +89 -0
  377. package/test/app/theme-toggle.test.tsx +33 -0
  378. package/test/app/training-api-client.test.ts +128 -0
  379. package/test/app/trajectories-view.test.tsx +220 -0
  380. package/test/app/triggers-api-client.test.ts +77 -0
  381. package/test/app/triggers-navigation.test.ts +113 -0
  382. package/test/app/triggers-view.e2e.test.ts +674 -0
  383. package/test/app/update-channel-lock.test.ts +259 -0
  384. package/test/app/vector-browser.async-cleanup.test.tsx +367 -0
  385. package/test/app/vector-browser.e2e.test.ts +653 -0
  386. package/test/app/vrm-stage.test.tsx +351 -0
  387. package/test/app/vrm-viewer.test.tsx +298 -0
  388. package/test/app/wallet-api-save-lock.test.ts +298 -0
  389. package/test/app/wallet-hooks.test.ts +405 -0
  390. package/test/app/wallet-ui-flows.e2e.test.ts +556 -0
  391. package/test/avatar/asset-url.test.ts +90 -0
  392. package/test/avatar/avatar-selector.test.ts +173 -0
  393. package/test/avatar/mixamo-vrm-rig-map.test.ts +111 -0
  394. package/test/avatar/voice-chat-streaming-text.test.ts +96 -0
  395. package/test/avatar/voice-chat.test.ts +391 -0
  396. package/test/ui/command-palette-commands.test.ts +57 -0
  397. package/test/ui/ui-renderer.test.ts +39 -0
  398. package/tsconfig.build.json +19 -0
  399. package/tsconfig.json +20 -0
@@ -0,0 +1,164 @@
1
+ import type { VRM } from "@pixiv/three-vrm";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+
4
+ const hoisted = vi.hoisted(() => ({
5
+ fetchMock: vi.fn(),
6
+ gltfParseAsyncMock: vi.fn(),
7
+ fbxParseMock: vi.fn(),
8
+ retargetGltfMock: vi.fn(),
9
+ retargetFbxMock: vi.fn(),
10
+ }));
11
+
12
+ vi.mock("three/examples/jsm/loaders/GLTFLoader.js", () => ({
13
+ GLTFLoader: class MockGLTFLoader {
14
+ parseAsync = hoisted.gltfParseAsyncMock;
15
+ },
16
+ }));
17
+
18
+ vi.mock("three/examples/jsm/loaders/FBXLoader.js", () => ({
19
+ FBXLoader: class MockFBXLoader {
20
+ parse = hoisted.fbxParseMock;
21
+ },
22
+ }));
23
+
24
+ vi.mock("./retargetMixamoGltfToVrm", () => ({
25
+ retargetMixamoGltfToVrm: hoisted.retargetGltfMock,
26
+ }));
27
+
28
+ vi.mock("./retargetMixamoFbxToVrm", () => ({
29
+ retargetMixamoFbxToVrm: hoisted.retargetFbxMock,
30
+ }));
31
+
32
+ import { loadEmoteClip, loadIdleClip } from "./VrmAnimationLoader";
33
+
34
+ describe("VrmAnimationLoader", () => {
35
+ const originalFetch = globalThis.fetch;
36
+ const originalDecompressionStream = globalThis.DecompressionStream;
37
+
38
+ beforeEach(() => {
39
+ vi.restoreAllMocks();
40
+ hoisted.fetchMock.mockReset();
41
+ hoisted.gltfParseAsyncMock.mockReset();
42
+ hoisted.fbxParseMock.mockReset();
43
+ hoisted.retargetGltfMock.mockReset();
44
+ hoisted.retargetFbxMock.mockReset();
45
+ Object.assign(globalThis, {
46
+ fetch: hoisted.fetchMock,
47
+ });
48
+ });
49
+
50
+ afterEach(() => {
51
+ Object.assign(globalThis, {
52
+ fetch: originalFetch,
53
+ DecompressionStream: originalDecompressionStream,
54
+ });
55
+ });
56
+
57
+ it("loads a gzipped idle GLB animation", async () => {
58
+ const compressed = new Uint8Array([0x1f, 0x8b, 0x08, 0x00]).buffer;
59
+ const decompressed = new ArrayBuffer(32);
60
+ hoisted.fetchMock.mockResolvedValueOnce({
61
+ ok: true,
62
+ arrayBuffer: vi.fn().mockResolvedValue(compressed),
63
+ });
64
+
65
+ class MockDecompressionStream {
66
+ readable = new ReadableStream<Uint8Array>();
67
+ writable = new WritableStream<Uint8Array>();
68
+ }
69
+
70
+ Object.assign(globalThis, {
71
+ DecompressionStream:
72
+ MockDecompressionStream as unknown as typeof DecompressionStream,
73
+ });
74
+
75
+ const pipeThroughSpy = vi.spyOn(Blob.prototype, "stream").mockReturnValue({
76
+ pipeThrough: vi.fn(() => new ReadableStream<Uint8Array>()),
77
+ } as unknown as ReturnType<Blob["stream"]>);
78
+ const responseArrayBufferSpy = vi
79
+ .spyOn(Response.prototype, "arrayBuffer")
80
+ .mockResolvedValueOnce(decompressed);
81
+
82
+ const vrm = {
83
+ scene: { updateMatrixWorld: vi.fn() },
84
+ } as unknown as VRM;
85
+ const gltf = {
86
+ scene: { updateMatrixWorld: vi.fn() },
87
+ animations: [],
88
+ };
89
+ const clip = { name: "idle" };
90
+ hoisted.gltfParseAsyncMock.mockResolvedValueOnce(gltf);
91
+ hoisted.retargetGltfMock.mockReturnValueOnce(clip);
92
+
93
+ const result = await loadIdleClip(vrm, "/animations/idle.glb.gz", {
94
+ isAborted: () => false,
95
+ isCurrentVrm: () => true,
96
+ });
97
+
98
+ expect(result).toBe(clip);
99
+ expect(hoisted.fetchMock).toHaveBeenCalledWith("/animations/idle.glb.gz");
100
+ expect(hoisted.gltfParseAsyncMock).toHaveBeenCalledWith(
101
+ decompressed,
102
+ "/animations/",
103
+ );
104
+ expect(hoisted.retargetGltfMock).toHaveBeenCalledWith(gltf, vrm);
105
+
106
+ responseArrayBufferSpy.mockRestore();
107
+ pipeThroughSpy.mockRestore();
108
+ });
109
+
110
+ it("loads a gzipped Mixamo FBX emote", async () => {
111
+ const compressed = new Uint8Array([0x1f, 0x8b, 0x08, 0x00]).buffer;
112
+ const decompressed = new ArrayBuffer(64);
113
+ hoisted.fetchMock.mockResolvedValueOnce({
114
+ ok: true,
115
+ arrayBuffer: vi.fn().mockResolvedValue(compressed),
116
+ });
117
+
118
+ class MockDecompressionStream {
119
+ readable = new ReadableStream<Uint8Array>();
120
+ writable = new WritableStream<Uint8Array>();
121
+ }
122
+
123
+ Object.assign(globalThis, {
124
+ DecompressionStream:
125
+ MockDecompressionStream as unknown as typeof DecompressionStream,
126
+ });
127
+
128
+ const pipeThroughSpy = vi.spyOn(Blob.prototype, "stream").mockReturnValue({
129
+ pipeThrough: vi.fn(() => new ReadableStream<Uint8Array>()),
130
+ } as unknown as ReturnType<Blob["stream"]>);
131
+ const responseArrayBufferSpy = vi
132
+ .spyOn(Response.prototype, "arrayBuffer")
133
+ .mockResolvedValueOnce(decompressed);
134
+
135
+ const vrm = {
136
+ scene: { updateMatrixWorld: vi.fn() },
137
+ } as unknown as VRM;
138
+ const sourceClip = { name: "happy" };
139
+ const fbx = {
140
+ animations: [sourceClip],
141
+ };
142
+ const clip = { name: "happy" };
143
+ hoisted.fbxParseMock.mockReturnValueOnce(fbx);
144
+ hoisted.retargetFbxMock.mockReturnValueOnce(clip);
145
+
146
+ const result = await loadEmoteClip("/animations/mixamo/Happy.fbx.gz", vrm, {
147
+ isAborted: () => false,
148
+ isCurrentVrm: () => true,
149
+ });
150
+
151
+ expect(result).toBe(clip);
152
+ expect(hoisted.fetchMock).toHaveBeenCalledWith(
153
+ "/animations/mixamo/Happy.fbx.gz",
154
+ );
155
+ expect(hoisted.fbxParseMock).toHaveBeenCalledWith(
156
+ decompressed,
157
+ "/animations/mixamo/",
158
+ );
159
+ expect(hoisted.retargetFbxMock).toHaveBeenCalledWith(fbx, sourceClip, vrm);
160
+
161
+ responseArrayBufferSpy.mockRestore();
162
+ pipeThroughSpy.mockRestore();
163
+ });
164
+ });
@@ -0,0 +1,151 @@
1
+ import type { VRM } from "@pixiv/three-vrm";
2
+ import type * as THREE from "three";
3
+ import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader.js";
4
+ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
5
+
6
+ /**
7
+ * Context needed by the animation loader to check whether the owning engine
8
+ * is still alive / relevant for the load request.
9
+ */
10
+ export type AnimationLoaderContext = {
11
+ /** Returns `true` if the loading sequence was aborted (engine disposed). */
12
+ isAborted: () => boolean;
13
+ /** Returns `true` if `vrm` is still the active model in the engine. */
14
+ isCurrentVrm: (vrm: VRM) => boolean;
15
+ };
16
+
17
+ function isGzipBuffer(buffer: ArrayBuffer): boolean {
18
+ if (buffer.byteLength < 2) return false;
19
+ const bytes = new Uint8Array(buffer, 0, 2);
20
+ return bytes[0] === 0x1f && bytes[1] === 0x8b;
21
+ }
22
+
23
+ async function decompressGzipBuffer(buffer: ArrayBuffer): Promise<ArrayBuffer> {
24
+ if (typeof DecompressionStream !== "function") {
25
+ throw new Error(
26
+ "This runtime does not support gzip-compressed animation assets.",
27
+ );
28
+ }
29
+
30
+ const stream = new Blob([buffer])
31
+ .stream()
32
+ .pipeThrough(new DecompressionStream("gzip"));
33
+ return await new Response(stream).arrayBuffer();
34
+ }
35
+
36
+ async function fetchAnimationBuffer(path: string): Promise<ArrayBuffer> {
37
+ const response = await fetch(path);
38
+ if (!response.ok) {
39
+ throw new Error(`Failed to fetch animation asset: ${response.status}`);
40
+ }
41
+
42
+ const buffer = await response.arrayBuffer();
43
+ return isGzipBuffer(buffer) ? await decompressGzipBuffer(buffer) : buffer;
44
+ }
45
+
46
+ function getAssetResourcePath(path: string): string {
47
+ const withoutQuery = path.split(/[?#]/, 1)[0] ?? path;
48
+ const lastSlash = withoutQuery.lastIndexOf("/");
49
+ return lastSlash >= 0 ? withoutQuery.slice(0, lastSlash + 1) : "./";
50
+ }
51
+
52
+ function isFbxAsset(path: string): boolean {
53
+ const normalized = path.toLowerCase();
54
+ return normalized.endsWith(".fbx") || normalized.endsWith(".fbx.gz");
55
+ }
56
+
57
+ /**
58
+ * Load and return an idle {@link THREE.AnimationClip} for the given VRM.
59
+ *
60
+ * Loads the GLB idle animation and retargets it to VRM bones.
61
+ * Returns `null` when the load was aborted (engine disposed / VRM replaced).
62
+ * Throws when the idle clip cannot be loaded.
63
+ */
64
+ export async function loadIdleClip(
65
+ vrm: VRM,
66
+ idleGlbUrl: string,
67
+ ctx: AnimationLoaderContext,
68
+ ): Promise<THREE.AnimationClip | null> {
69
+ const { retargetMixamoGltfToVrm } = await import("./retargetMixamoGltfToVrm");
70
+ if (ctx.isAborted() || !ctx.isCurrentVrm(vrm)) return null;
71
+
72
+ const gltfLoader = new GLTFLoader();
73
+ const gltf = await gltfLoader.parseAsync(
74
+ await fetchAnimationBuffer(idleGlbUrl),
75
+ getAssetResourcePath(idleGlbUrl),
76
+ );
77
+ if (ctx.isAborted() || !ctx.isCurrentVrm(vrm)) return null;
78
+
79
+ gltf.scene.updateMatrixWorld(true);
80
+ vrm.scene.updateMatrixWorld(true);
81
+ const clip = retargetMixamoGltfToVrm(
82
+ { scene: gltf.scene, animations: gltf.animations },
83
+ vrm,
84
+ );
85
+
86
+ if (!clip) {
87
+ throw new Error("No usable idle animation (idle.glb)");
88
+ }
89
+
90
+ if (ctx.isAborted() || !ctx.isCurrentVrm(vrm)) return null;
91
+ return clip;
92
+ }
93
+
94
+ /**
95
+ * Load a single emote animation clip (GLB/FBX) and retarget it to
96
+ * the supplied VRM. Returns `null` when the load was aborted or the file
97
+ * format could not be processed.
98
+ *
99
+ * Supports both `.glb` / `.gltf` files (retargeted via retargetMixamoGltfToVrm)
100
+ * and Mixamo `.fbx` files (retargeted via retargetMixamoFbxToVrm).
101
+ */
102
+ export async function loadEmoteClip(
103
+ path: string,
104
+ vrm: VRM,
105
+ ctx: AnimationLoaderContext,
106
+ ): Promise<THREE.AnimationClip | null> {
107
+ try {
108
+ if (!ctx.isCurrentVrm(vrm)) return null;
109
+
110
+ const isFbx = isFbxAsset(path);
111
+
112
+ if (isFbx) {
113
+ const { retargetMixamoFbxToVrm } = await import(
114
+ "./retargetMixamoFbxToVrm"
115
+ );
116
+ const fbxLoader = new FBXLoader();
117
+ const fbx = fbxLoader.parse(
118
+ await fetchAnimationBuffer(path),
119
+ getAssetResourcePath(path),
120
+ );
121
+ if (!ctx.isCurrentVrm(vrm)) return null;
122
+
123
+ const sourceClip = fbx.animations[0];
124
+ if (!sourceClip) {
125
+ console.warn(`[VrmEngine] FBX has no animations: ${path}`);
126
+ return null;
127
+ }
128
+ return retargetMixamoFbxToVrm(fbx, sourceClip, vrm);
129
+ }
130
+
131
+ const { retargetMixamoGltfToVrm } = await import(
132
+ "./retargetMixamoGltfToVrm"
133
+ );
134
+ const loader = new GLTFLoader();
135
+ const gltf = await loader.parseAsync(
136
+ await fetchAnimationBuffer(path),
137
+ getAssetResourcePath(path),
138
+ );
139
+ if (!ctx.isCurrentVrm(vrm)) return null;
140
+
141
+ gltf.scene.updateMatrixWorld(true);
142
+ vrm.scene.updateMatrixWorld(true);
143
+ return retargetMixamoGltfToVrm(
144
+ { scene: gltf.scene, animations: gltf.animations },
145
+ vrm,
146
+ );
147
+ } catch (err) {
148
+ console.error(`[VrmEngine] Failed to load emote: ${path}`, err);
149
+ return null;
150
+ }
151
+ }
@@ -0,0 +1,118 @@
1
+ /** Blink animation phase */
2
+ type BlinkPhase = "idle" | "closing" | "closed" | "opening";
3
+
4
+ /**
5
+ * Self-contained blink state machine for VRM eye-blink animation.
6
+ *
7
+ * State flow: idle -> closing -> closed -> opening -> idle
8
+ * Random interval between blinks with occasional double-blinks.
9
+ */
10
+ export class VrmBlinkController {
11
+ private blinkPhase: BlinkPhase = "idle";
12
+ private blinkTimer = 0;
13
+ private blinkPhaseTimer = 0;
14
+ private _blinkValue = 0;
15
+ private nextBlinkDelay = 2 + Math.random() * 3;
16
+
17
+ /** Duration (seconds) for eyelids to close */
18
+ private static readonly BLINK_CLOSE_DURATION = 0.06;
19
+ /** Duration (seconds) eyelids stay fully closed */
20
+ private static readonly BLINK_HOLD_DURATION = 0.04;
21
+ /** Duration (seconds) for eyelids to re-open */
22
+ private static readonly BLINK_OPEN_DURATION = 0.12;
23
+ /** Minimum seconds between blinks */
24
+ private static readonly BLINK_MIN_INTERVAL = 1.8;
25
+ /** Maximum seconds between blinks */
26
+ private static readonly BLINK_MAX_INTERVAL = 5.5;
27
+ /** Probability of a quick double-blink */
28
+ private static readonly DOUBLE_BLINK_CHANCE = 0.15;
29
+
30
+ /** Current blink expression weight (0 = open, 1 = closed). */
31
+ get blinkValue(): number {
32
+ return this._blinkValue;
33
+ }
34
+
35
+ /**
36
+ * Advance the blink state machine by `delta` seconds.
37
+ * Returns the current blink expression weight (0..1).
38
+ */
39
+ update(delta: number): number {
40
+ switch (this.blinkPhase) {
41
+ case "idle":
42
+ this.blinkTimer += delta;
43
+ if (this.blinkTimer >= this.nextBlinkDelay) {
44
+ this.blinkPhase = "closing";
45
+ this.blinkPhaseTimer = 0;
46
+ }
47
+ break;
48
+
49
+ case "closing": {
50
+ this.blinkPhaseTimer += delta;
51
+ const t = Math.min(
52
+ 1,
53
+ this.blinkPhaseTimer / VrmBlinkController.BLINK_CLOSE_DURATION,
54
+ );
55
+ // Ease-in (accelerate) — eyelids speed up as they close
56
+ this._blinkValue = t * t;
57
+ if (t >= 1) {
58
+ this.blinkPhase = "closed";
59
+ this.blinkPhaseTimer = 0;
60
+ this._blinkValue = 1;
61
+ }
62
+ break;
63
+ }
64
+
65
+ case "closed":
66
+ this.blinkPhaseTimer += delta;
67
+ if (this.blinkPhaseTimer >= VrmBlinkController.BLINK_HOLD_DURATION) {
68
+ this.blinkPhase = "opening";
69
+ this.blinkPhaseTimer = 0;
70
+ }
71
+ break;
72
+
73
+ case "opening": {
74
+ this.blinkPhaseTimer += delta;
75
+ const t = Math.min(
76
+ 1,
77
+ this.blinkPhaseTimer / VrmBlinkController.BLINK_OPEN_DURATION,
78
+ );
79
+ // Ease-out (decelerate) — eyelids slow down as they finish opening
80
+ const eased = 1 - (1 - t) * (1 - t);
81
+ this._blinkValue = 1 - eased;
82
+ if (t >= 1) {
83
+ this.blinkPhase = "idle";
84
+ this.blinkPhaseTimer = 0;
85
+ this._blinkValue = 0;
86
+ this.blinkTimer = 0;
87
+ this.scheduleNextBlink();
88
+ }
89
+ break;
90
+ }
91
+ }
92
+
93
+ return this._blinkValue;
94
+ }
95
+
96
+ /** Reset blink state (called when a new VRM is loaded). */
97
+ reset(): void {
98
+ this.blinkPhase = "idle";
99
+ this.blinkTimer = 0;
100
+ this.blinkPhaseTimer = 0;
101
+ this._blinkValue = 0;
102
+ this.nextBlinkDelay = 1.5 + Math.random() * 2;
103
+ }
104
+
105
+ /** Pick the delay (seconds) until the next blink. */
106
+ private scheduleNextBlink(): void {
107
+ const range =
108
+ VrmBlinkController.BLINK_MAX_INTERVAL -
109
+ VrmBlinkController.BLINK_MIN_INTERVAL;
110
+ this.nextBlinkDelay =
111
+ VrmBlinkController.BLINK_MIN_INTERVAL + Math.random() * range;
112
+
113
+ // Occasional quick double-blink
114
+ if (Math.random() < VrmBlinkController.DOUBLE_BLINK_CHANCE) {
115
+ this.nextBlinkDelay = 0.12 + Math.random() * 0.08;
116
+ }
117
+ }
118
+ }