@dxos/app-framework 0.8.4-main.74a063c4e0 → 0.8.4-main.765dc60934

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 (307) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/{capability-OP63CD5N.mjs → capability-HRFPP52W.mjs} +11 -11
  4. package/dist/lib/browser/capability-HRFPP52W.mjs.map +7 -0
  5. package/dist/lib/browser/{capability-BBBBAPDI.mjs → capability-VQ236TY5.mjs} +10 -10
  6. package/dist/lib/browser/{chunk-T3Y4AEKX.mjs → chunk-23D4SJUE.mjs} +3 -3
  7. package/dist/lib/browser/{chunk-T3Y4AEKX.mjs.map → chunk-23D4SJUE.mjs.map} +1 -1
  8. package/dist/lib/browser/{chunk-2CKCJ6PN.mjs → chunk-3JWJXGLK.mjs} +1 -1
  9. package/dist/lib/browser/{chunk-2CKCJ6PN.mjs.map → chunk-3JWJXGLK.mjs.map} +1 -1
  10. package/dist/lib/browser/{chunk-I34GF4NG.mjs → chunk-45CHLTBV.mjs} +2 -2
  11. package/dist/lib/browser/{chunk-QSXYHXCE.mjs → chunk-66IXTIVK.mjs} +1 -1
  12. package/dist/lib/browser/{chunk-QSXYHXCE.mjs.map → chunk-66IXTIVK.mjs.map} +2 -2
  13. package/dist/lib/browser/chunk-6GZYCP73.mjs +422 -0
  14. package/dist/lib/browser/chunk-6GZYCP73.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-7VZJR2OA.mjs +581 -0
  16. package/dist/lib/browser/chunk-7VZJR2OA.mjs.map +7 -0
  17. package/dist/lib/browser/{chunk-TGX63LTL.mjs → chunk-FJ4765WW.mjs} +1 -1
  18. package/dist/lib/browser/{chunk-TGX63LTL.mjs.map → chunk-FJ4765WW.mjs.map} +2 -2
  19. package/dist/lib/browser/chunk-FO3IYSLV.mjs +68 -0
  20. package/dist/lib/browser/chunk-FO3IYSLV.mjs.map +7 -0
  21. package/dist/lib/browser/{chunk-FU4GAFUQ.mjs → chunk-G6Q2M2M5.mjs} +85 -15
  22. package/dist/lib/browser/chunk-G6Q2M2M5.mjs.map +7 -0
  23. package/dist/lib/browser/chunk-J43JDWAV.mjs +1445 -0
  24. package/dist/lib/browser/chunk-J43JDWAV.mjs.map +7 -0
  25. package/dist/lib/browser/{chunk-JKWMHZP6.mjs → chunk-WBHCSOBW.mjs} +2 -2
  26. package/dist/lib/browser/chunk-WBHCSOBW.mjs.map +7 -0
  27. package/dist/lib/browser/chunk-XLYULXAO.mjs +469 -0
  28. package/dist/lib/browser/chunk-XLYULXAO.mjs.map +7 -0
  29. package/dist/lib/browser/{chunk-F7FW2RK2.mjs → chunk-ZGJAZSNE.mjs} +7 -32
  30. package/dist/lib/browser/chunk-ZGJAZSNE.mjs.map +7 -0
  31. package/dist/lib/browser/cli/index.mjs +14 -28
  32. package/dist/lib/browser/cli/index.mjs.map +3 -3
  33. package/dist/lib/browser/common/activation-events.mjs +7 -7
  34. package/dist/lib/browser/common/capabilities.mjs +7 -7
  35. package/dist/lib/browser/core/activation-event.mjs +1 -1
  36. package/dist/lib/browser/core/capability.mjs +1 -1
  37. package/dist/lib/browser/core/plugin-manager.mjs +8 -4
  38. package/dist/lib/browser/core/plugin.mjs +12 -2
  39. package/dist/lib/browser/core/url-loader.mjs +17 -5
  40. package/dist/lib/browser/index.mjs +38 -21
  41. package/dist/lib/browser/index.mjs.map +3 -3
  42. package/dist/lib/browser/{invoker-capability-H5PPENOC.mjs → invoker-capability-CEL2L6GX.mjs} +12 -11
  43. package/dist/lib/browser/invoker-capability-CEL2L6GX.mjs.map +7 -0
  44. package/dist/lib/browser/meta.json +1 -1
  45. package/dist/lib/browser/testing/index.mjs +149 -28
  46. package/dist/lib/browser/testing/index.mjs.map +4 -4
  47. package/dist/lib/browser/testing/react.mjs +78 -0
  48. package/dist/lib/browser/testing/react.mjs.map +7 -0
  49. package/dist/lib/browser/ui/index.mjs +18 -14
  50. package/dist/lib/node-esm/{capability-AWBEMRYR.mjs → capability-GS3XJH74.mjs} +10 -10
  51. package/dist/lib/node-esm/{capability-WFEG6CIZ.mjs → capability-I5P7ACFL.mjs} +11 -11
  52. package/dist/lib/node-esm/capability-I5P7ACFL.mjs.map +7 -0
  53. package/dist/lib/node-esm/{chunk-FKE4Z3D6.mjs → chunk-37Z53PXZ.mjs} +1 -1
  54. package/dist/lib/node-esm/{chunk-FKE4Z3D6.mjs.map → chunk-37Z53PXZ.mjs.map} +2 -2
  55. package/dist/lib/node-esm/chunk-4RJL37GN.mjs +423 -0
  56. package/dist/lib/node-esm/chunk-4RJL37GN.mjs.map +7 -0
  57. package/dist/lib/node-esm/{chunk-WZCSOX5Q.mjs → chunk-6XW6LET6.mjs} +2 -2
  58. package/dist/lib/node-esm/{chunk-URWHJQT2.mjs → chunk-D347W3KO.mjs} +7 -32
  59. package/dist/lib/node-esm/chunk-D347W3KO.mjs.map +7 -0
  60. package/dist/lib/node-esm/chunk-DW45NFLW.mjs +1446 -0
  61. package/dist/lib/node-esm/chunk-DW45NFLW.mjs.map +7 -0
  62. package/dist/lib/node-esm/chunk-EI4LYXFY.mjs +470 -0
  63. package/dist/lib/node-esm/chunk-EI4LYXFY.mjs.map +7 -0
  64. package/dist/lib/node-esm/{chunk-ULUEXB7Q.mjs → chunk-KIFOQXD4.mjs} +85 -15
  65. package/dist/lib/node-esm/chunk-KIFOQXD4.mjs.map +7 -0
  66. package/dist/lib/node-esm/{chunk-VKHGNEDB.mjs → chunk-SBS2YMPT.mjs} +3 -3
  67. package/dist/lib/node-esm/{chunk-VKHGNEDB.mjs.map → chunk-SBS2YMPT.mjs.map} +1 -1
  68. package/dist/lib/node-esm/{chunk-42KBWDE4.mjs → chunk-SDJ4B2LU.mjs} +1 -1
  69. package/dist/lib/node-esm/{chunk-42KBWDE4.mjs.map → chunk-SDJ4B2LU.mjs.map} +1 -1
  70. package/dist/lib/node-esm/chunk-V24UWT36.mjs +582 -0
  71. package/dist/lib/node-esm/chunk-V24UWT36.mjs.map +7 -0
  72. package/dist/lib/node-esm/{chunk-G3RTFSNG.mjs → chunk-WFSRZKBP.mjs} +2 -2
  73. package/dist/lib/node-esm/chunk-WFSRZKBP.mjs.map +7 -0
  74. package/dist/lib/node-esm/chunk-WK7OIQKI.mjs +70 -0
  75. package/dist/lib/node-esm/chunk-WK7OIQKI.mjs.map +7 -0
  76. package/dist/lib/node-esm/{chunk-ZZ7CKK6W.mjs → chunk-XOCUANHO.mjs} +1 -1
  77. package/dist/lib/node-esm/{chunk-ZZ7CKK6W.mjs.map → chunk-XOCUANHO.mjs.map} +2 -2
  78. package/dist/lib/node-esm/cli/index.mjs +14 -28
  79. package/dist/lib/node-esm/cli/index.mjs.map +3 -3
  80. package/dist/lib/node-esm/common/activation-events.mjs +7 -7
  81. package/dist/lib/node-esm/common/capabilities.mjs +7 -7
  82. package/dist/lib/node-esm/core/activation-event.mjs +1 -1
  83. package/dist/lib/node-esm/core/capability.mjs +1 -1
  84. package/dist/lib/node-esm/core/plugin-manager.mjs +8 -4
  85. package/dist/lib/node-esm/core/plugin.mjs +12 -2
  86. package/dist/lib/node-esm/core/url-loader.mjs +17 -5
  87. package/dist/lib/node-esm/index.mjs +38 -21
  88. package/dist/lib/node-esm/index.mjs.map +3 -3
  89. package/dist/lib/node-esm/{invoker-capability-S3ZA527J.mjs → invoker-capability-QKUKN2MF.mjs} +12 -11
  90. package/dist/lib/node-esm/invoker-capability-QKUKN2MF.mjs.map +7 -0
  91. package/dist/lib/node-esm/meta.json +1 -1
  92. package/dist/lib/node-esm/testing/index.mjs +149 -28
  93. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  94. package/dist/lib/node-esm/testing/react.mjs +79 -0
  95. package/dist/lib/node-esm/testing/react.mjs.map +7 -0
  96. package/dist/lib/node-esm/ui/index.mjs +18 -14
  97. package/dist/plugin/node-esm/index.mjs +542 -33
  98. package/dist/plugin/node-esm/index.mjs.map +4 -4
  99. package/dist/plugin/node-esm/meta.json +1 -1
  100. package/dist/types/src/common/annotations.d.ts +1 -0
  101. package/dist/types/src/common/annotations.d.ts.map +1 -0
  102. package/dist/types/src/common/capabilities.d.ts +2 -1
  103. package/dist/types/src/common/capabilities.d.ts.map +1 -1
  104. package/dist/types/src/common/operations.d.ts +1 -1
  105. package/dist/types/src/common/operations.d.ts.map +1 -1
  106. package/dist/types/src/core/activation-event.d.ts +4 -4
  107. package/dist/types/src/core/activation-event.d.ts.map +1 -1
  108. package/dist/types/src/core/capability-manager.d.ts.map +1 -1
  109. package/dist/types/src/core/capability.d.ts +2 -2
  110. package/dist/types/src/core/capability.d.ts.map +1 -1
  111. package/dist/types/src/core/edge-registry-plugin-provider.d.ts +30 -0
  112. package/dist/types/src/core/edge-registry-plugin-provider.d.ts.map +1 -0
  113. package/dist/types/src/core/index.d.ts +5 -0
  114. package/dist/types/src/core/index.d.ts.map +1 -1
  115. package/dist/types/src/core/plugin-asset-cache.d.ts +71 -0
  116. package/dist/types/src/core/plugin-asset-cache.d.ts.map +1 -0
  117. package/dist/types/src/core/plugin-manager.d.ts +230 -5
  118. package/dist/types/src/core/plugin-manager.d.ts.map +1 -1
  119. package/dist/types/src/core/plugin-manifest.d.ts +101 -0
  120. package/dist/types/src/core/plugin-manifest.d.ts.map +1 -0
  121. package/dist/types/src/core/plugin-manifest.test.d.ts +2 -0
  122. package/dist/types/src/core/plugin-manifest.test.d.ts.map +1 -0
  123. package/dist/types/src/core/plugin.d.ts +164 -6
  124. package/dist/types/src/core/plugin.d.ts.map +1 -1
  125. package/dist/types/src/core/registry.d.ts +107 -0
  126. package/dist/types/src/core/registry.d.ts.map +1 -0
  127. package/dist/types/src/core/url-loader.d.ts +105 -3
  128. package/dist/types/src/core/url-loader.d.ts.map +1 -1
  129. package/dist/types/src/helpers.d.ts.map +1 -1
  130. package/dist/types/src/plugin-operation/history/capability.d.ts.map +1 -1
  131. package/dist/types/src/plugin-operation/history/errors.d.ts +6 -6
  132. package/dist/types/src/plugin-operation/history/errors.d.ts.map +1 -1
  133. package/dist/types/src/plugin-operation/history/history-tracker.d.ts +1 -1
  134. package/dist/types/src/plugin-operation/history/history-tracker.d.ts.map +1 -1
  135. package/dist/types/src/plugin-operation/history/types.d.ts +1 -1
  136. package/dist/types/src/plugin-operation/history/types.d.ts.map +1 -1
  137. package/dist/types/src/plugin-operation/history/undo-mapping.d.ts +1 -1
  138. package/dist/types/src/plugin-operation/history/undo-mapping.d.ts.map +1 -1
  139. package/dist/types/src/plugin-operation/history/undo-registry.d.ts +1 -1
  140. package/dist/types/src/plugin-operation/history/undo-registry.d.ts.map +1 -1
  141. package/dist/types/src/plugin-operation/invoker-capability.d.ts +1 -1
  142. package/dist/types/src/plugin-operation/invoker-capability.d.ts.map +1 -1
  143. package/dist/types/src/plugin-operation/meta.d.ts.map +1 -1
  144. package/dist/types/src/plugin-operation/testing.d.ts +2 -1
  145. package/dist/types/src/plugin-operation/testing.d.ts.map +1 -1
  146. package/dist/types/src/plugin-runtime/capability.d.ts +1 -1
  147. package/dist/types/src/plugin-runtime/capability.d.ts.map +1 -1
  148. package/dist/types/src/plugin-runtime/meta.d.ts.map +1 -1
  149. package/dist/types/src/testing/harness.d.ts +67 -0
  150. package/dist/types/src/testing/harness.d.ts.map +1 -0
  151. package/dist/types/src/testing/index.d.ts +1 -0
  152. package/dist/types/src/testing/index.d.ts.map +1 -1
  153. package/dist/types/src/testing/react.d.ts +27 -0
  154. package/dist/types/src/testing/react.d.ts.map +1 -0
  155. package/dist/types/src/testing/react.test.d.ts +2 -0
  156. package/dist/types/src/testing/react.test.d.ts.map +1 -0
  157. package/dist/types/src/testing/service.d.ts.map +1 -1
  158. package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
  159. package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -1
  160. package/dist/types/src/ui/components/App/App.d.ts.map +1 -1
  161. package/dist/types/src/ui/components/App/App.stories.d.ts.map +1 -1
  162. package/dist/types/src/ui/components/Placeholder/Placeholder.d.ts +64 -0
  163. package/dist/types/src/ui/components/Placeholder/Placeholder.d.ts.map +1 -0
  164. package/dist/types/src/ui/components/Placeholder/Placeholder.stories.d.ts +19 -0
  165. package/dist/types/src/ui/components/Placeholder/Placeholder.stories.d.ts.map +1 -0
  166. package/dist/types/src/ui/components/Placeholder/index.d.ts +2 -0
  167. package/dist/types/src/ui/components/Placeholder/index.d.ts.map +1 -0
  168. package/dist/types/src/ui/components/PluginManager/PluginManagerContext.stories.d.ts.map +1 -1
  169. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts +16 -4
  170. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts.map +1 -1
  171. package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
  172. package/dist/types/src/ui/components/Surface/SurfaceInfo.d.ts.map +1 -1
  173. package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts.map +1 -1
  174. package/dist/types/src/ui/components/Surface/index.d.ts +16 -6
  175. package/dist/types/src/ui/components/Surface/index.d.ts.map +1 -1
  176. package/dist/types/src/ui/components/Surface/types.d.ts +110 -9
  177. package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -1
  178. package/dist/types/src/ui/components/Surface/types.test.d.ts +2 -0
  179. package/dist/types/src/ui/components/Surface/types.test.d.ts.map +1 -0
  180. package/dist/types/src/ui/components/index.d.ts +1 -0
  181. package/dist/types/src/ui/components/index.d.ts.map +1 -1
  182. package/dist/types/src/ui/hooks/useApp.d.ts +30 -3
  183. package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -1
  184. package/dist/types/src/ui/hooks/useCapabilities.d.ts.map +1 -1
  185. package/dist/types/src/ui/hooks/useLoading.d.ts.map +1 -1
  186. package/dist/types/src/ui/hooks/useSettingsState.d.ts.map +1 -1
  187. package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts +34 -0
  188. package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts.map +1 -0
  189. package/dist/types/src/vite-plugin/boot-loader/index.d.ts +2 -0
  190. package/dist/types/src/vite-plugin/boot-loader/index.d.ts.map +1 -0
  191. package/dist/types/src/vite-plugin/boot-loader/loader.d.ts +51 -0
  192. package/dist/types/src/vite-plugin/boot-loader/loader.d.ts.map +1 -0
  193. package/dist/types/src/vite-plugin/composer/index.d.ts +34 -0
  194. package/dist/types/src/vite-plugin/composer/index.d.ts.map +1 -0
  195. package/dist/types/src/vite-plugin/import-map/index.d.ts +28 -0
  196. package/dist/types/src/vite-plugin/import-map/index.d.ts.map +1 -0
  197. package/dist/types/src/vite-plugin/index.d.ts +4 -2
  198. package/dist/types/src/vite-plugin/index.d.ts.map +1 -1
  199. package/dist/types/src/vite-plugin/manifest.d.ts +41 -0
  200. package/dist/types/src/vite-plugin/manifest.d.ts.map +1 -0
  201. package/dist/types/src/vite-plugin/manifest.test.d.ts +2 -0
  202. package/dist/types/src/vite-plugin/manifest.test.d.ts.map +1 -0
  203. package/dist/types/src/vite-plugin/packages.d.ts +10 -4
  204. package/dist/types/src/vite-plugin/packages.d.ts.map +1 -1
  205. package/dist/types/tsconfig.tsbuildinfo +1 -1
  206. package/moon.yml +1 -0
  207. package/package.json +36 -60
  208. package/src/cli/cli.ts +1 -1
  209. package/src/common/annotations.ts +3 -0
  210. package/src/common/capabilities.ts +2 -1
  211. package/src/common/operations.ts +1 -1
  212. package/src/core/capability.ts +1 -1
  213. package/src/core/edge-registry-plugin-provider.ts +92 -0
  214. package/src/core/index.ts +5 -0
  215. package/src/core/plugin-asset-cache.ts +60 -0
  216. package/src/core/plugin-manager.test.ts +793 -7
  217. package/src/core/plugin-manager.ts +846 -44
  218. package/src/core/plugin-manifest.test.ts +75 -0
  219. package/src/core/plugin-manifest.ts +134 -0
  220. package/src/core/plugin.ts +170 -10
  221. package/src/core/registry.ts +163 -0
  222. package/src/core/url-loader.test.ts +148 -6
  223. package/src/core/url-loader.ts +284 -44
  224. package/src/plugin-operation/OperationPlugin.ts +2 -2
  225. package/src/plugin-operation/history/capability.ts +1 -1
  226. package/src/plugin-operation/history/history-tracker.test.ts +2 -1
  227. package/src/plugin-operation/history/history-tracker.ts +1 -1
  228. package/src/plugin-operation/history/types.ts +1 -1
  229. package/src/plugin-operation/history/undo-mapping.ts +1 -1
  230. package/src/plugin-operation/history/undo-registry.ts +1 -1
  231. package/src/plugin-operation/invoker-capability.ts +2 -1
  232. package/src/plugin-operation/meta.ts +1 -0
  233. package/src/plugin-operation/testing.ts +2 -1
  234. package/src/plugin-runtime/RuntimePlugin.ts +2 -2
  235. package/src/plugin-runtime/meta.ts +1 -0
  236. package/src/testing/harness.ts +229 -0
  237. package/src/testing/index.ts +1 -0
  238. package/src/testing/react.test.tsx +48 -0
  239. package/src/testing/react.tsx +113 -0
  240. package/src/testing/service.ts +1 -1
  241. package/src/testing/withPluginManager.stories.tsx +1 -1
  242. package/src/testing/withPluginManager.tsx +0 -2
  243. package/src/ui/components/App/App.stories.tsx +2 -6
  244. package/src/ui/components/App/App.tsx +25 -2
  245. package/src/ui/components/Placeholder/Placeholder.stories.tsx +77 -0
  246. package/src/ui/components/Placeholder/Placeholder.tsx +155 -0
  247. package/src/ui/components/Placeholder/index.ts +5 -0
  248. package/src/ui/components/PluginManager/PluginManagerContext.stories.tsx +4 -2
  249. package/src/ui/components/Surface/SurfaceComponent.stories.tsx +1 -1
  250. package/src/ui/components/Surface/SurfaceComponent.tsx +85 -48
  251. package/src/ui/components/Surface/SurfaceInfo.tsx +0 -1
  252. package/src/ui/components/Surface/index.ts +20 -1
  253. package/src/ui/components/Surface/types.test.ts +126 -0
  254. package/src/ui/components/Surface/types.ts +164 -12
  255. package/src/ui/components/index.ts +1 -0
  256. package/src/ui/hooks/useApp.test.tsx +1 -1
  257. package/src/ui/hooks/useApp.tsx +177 -42
  258. package/src/ui/hooks/useLoading.tsx +14 -6
  259. package/src/vite-plugin/boot-loader/BootLoader.stories.tsx +270 -0
  260. package/src/vite-plugin/boot-loader/boot-loader.css +321 -0
  261. package/src/vite-plugin/boot-loader/boot-loader.js +325 -0
  262. package/src/vite-plugin/boot-loader/index.ts +5 -0
  263. package/src/vite-plugin/boot-loader/loader.ts +123 -0
  264. package/src/vite-plugin/composer/index.ts +306 -0
  265. package/src/vite-plugin/import-map/index.ts +527 -0
  266. package/src/vite-plugin/index.ts +6 -2
  267. package/src/vite-plugin/manifest.test.ts +46 -0
  268. package/src/vite-plugin/manifest.ts +57 -0
  269. package/src/vite-plugin/packages.ts +168 -10
  270. package/tsconfig.json +16 -1
  271. package/.swc/plugins/linux_x86_64_19.0.0/727453fb3a62f7f1d952a41e051ca8a6f88cadc45cee43c6a4d1aa45f9b75665.wasmer-v7 +0 -0
  272. package/dist/lib/browser/capability-OP63CD5N.mjs.map +0 -7
  273. package/dist/lib/browser/chunk-F7FW2RK2.mjs.map +0 -7
  274. package/dist/lib/browser/chunk-FU4GAFUQ.mjs.map +0 -7
  275. package/dist/lib/browser/chunk-GX4TUNM6.mjs +0 -963
  276. package/dist/lib/browser/chunk-GX4TUNM6.mjs.map +0 -7
  277. package/dist/lib/browser/chunk-JKWMHZP6.mjs.map +0 -7
  278. package/dist/lib/browser/chunk-LVJW5EFU.mjs +0 -157
  279. package/dist/lib/browser/chunk-LVJW5EFU.mjs.map +0 -7
  280. package/dist/lib/browser/chunk-RFSO3JRG.mjs +0 -1
  281. package/dist/lib/browser/chunk-RFSO3JRG.mjs.map +0 -7
  282. package/dist/lib/browser/chunk-WPE6AL7I.mjs +0 -905
  283. package/dist/lib/browser/chunk-WPE6AL7I.mjs.map +0 -7
  284. package/dist/lib/browser/invoker-capability-H5PPENOC.mjs.map +0 -7
  285. package/dist/lib/node-esm/capability-WFEG6CIZ.mjs.map +0 -7
  286. package/dist/lib/node-esm/chunk-4A3ZCMI3.mjs +0 -158
  287. package/dist/lib/node-esm/chunk-4A3ZCMI3.mjs.map +0 -7
  288. package/dist/lib/node-esm/chunk-BCEOLX47.mjs +0 -964
  289. package/dist/lib/node-esm/chunk-BCEOLX47.mjs.map +0 -7
  290. package/dist/lib/node-esm/chunk-EL3R25OQ.mjs +0 -2
  291. package/dist/lib/node-esm/chunk-EL3R25OQ.mjs.map +0 -7
  292. package/dist/lib/node-esm/chunk-G3RTFSNG.mjs.map +0 -7
  293. package/dist/lib/node-esm/chunk-LQKOTNJW.mjs +0 -906
  294. package/dist/lib/node-esm/chunk-LQKOTNJW.mjs.map +0 -7
  295. package/dist/lib/node-esm/chunk-ULUEXB7Q.mjs.map +0 -7
  296. package/dist/lib/node-esm/chunk-URWHJQT2.mjs.map +0 -7
  297. package/dist/lib/node-esm/invoker-capability-S3ZA527J.mjs.map +0 -7
  298. package/dist/types/src/vite-plugin/composer-plugin.d.ts +0 -18
  299. package/dist/types/src/vite-plugin/composer-plugin.d.ts.map +0 -1
  300. package/dist/types/src/vite-plugin/import-map-plugin.d.ts +0 -16
  301. package/dist/types/src/vite-plugin/import-map-plugin.d.ts.map +0 -1
  302. package/src/vite-plugin/composer-plugin.ts +0 -128
  303. package/src/vite-plugin/import-map-plugin.ts +0 -314
  304. /package/dist/lib/browser/{capability-BBBBAPDI.mjs.map → capability-VQ236TY5.mjs.map} +0 -0
  305. /package/dist/lib/browser/{chunk-I34GF4NG.mjs.map → chunk-45CHLTBV.mjs.map} +0 -0
  306. /package/dist/lib/node-esm/{capability-AWBEMRYR.mjs.map → capability-GS3XJH74.mjs.map} +0 -0
  307. /package/dist/lib/node-esm/{chunk-WZCSOX5Q.mjs.map → chunk-6XW6LET6.mjs.map} +0 -0
@@ -4,7 +4,8 @@
4
4
 
5
5
  import * as Effect from 'effect/Effect';
6
6
 
7
- import { OperationHandlerSet, OperationInvoker } from '@dxos/operation';
7
+ import { OperationHandlerSet } from '@dxos/compute';
8
+ import { OperationInvoker } from '@dxos/operation';
8
9
 
9
10
  import { ActivationEvents, Capabilities } from '../common';
10
11
  import { Capability, Plugin } from '../core';
@@ -8,4 +8,5 @@ export const meta: Plugin.Meta = {
8
8
  id: 'org.dxos.plugin.operation',
9
9
  name: 'Operation Plugin',
10
10
  description: 'Provides operation invocation, undo registry, and history tracking.',
11
+ tags: ['system'],
11
12
  };
@@ -9,7 +9,8 @@ import * as Ref from 'effect/Ref';
9
9
  import * as Schema from 'effect/Schema';
10
10
  import * as Stream from 'effect/Stream';
11
11
 
12
- import { Operation, type OperationInvoker } from '@dxos/operation';
12
+ import { Operation } from '@dxos/compute';
13
+ import { OperationInvoker } from '@dxos/operation';
13
14
 
14
15
  //
15
16
  // Test Operations
@@ -11,8 +11,8 @@ const ManagedRuntimeCapability = Capability.lazy('ManagedRuntime', () => import(
11
11
  export const RuntimePlugin = Plugin.define(meta).pipe(
12
12
  Plugin.addModule({
13
13
  activatesOn: ActivationEvents.Startup,
14
- activatesBefore: [ActivationEvents.SetupLayer],
15
- activatesAfter: [ActivationEvents.ManagedRuntimeReady],
14
+ firesBeforeActivation: [ActivationEvents.SetupLayer],
15
+ firesAfterActivation: [ActivationEvents.ManagedRuntimeReady],
16
16
  activate: ManagedRuntimeCapability,
17
17
  }),
18
18
  Plugin.make,
@@ -8,4 +8,5 @@ export const meta: Plugin.Meta = {
8
8
  id: 'org.dxos.plugin.runtime',
9
9
  name: 'Runtime Plugin',
10
10
  description: 'Provides a managed Effect runtime by composing contributed layers.',
11
+ tags: ['system'],
11
12
  };
@@ -0,0 +1,229 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { type Registry } from '@effect-atom/atom-react';
6
+ import * as Duration from 'effect/Duration';
7
+ import * as Effect from 'effect/Effect';
8
+ import * as PubSub from 'effect/PubSub';
9
+ import * as Queue from 'effect/Queue';
10
+
11
+ import { type Operation } from '@dxos/compute';
12
+ import { runAndForwardErrors } from '@dxos/effect';
13
+ import { invariant } from '@dxos/invariant';
14
+
15
+ import { ActivationEvents, Capabilities } from '../common';
16
+ import { ActivationEvent, type Capability, type CapabilityManager, type Plugin, PluginManager } from '../core';
17
+
18
+ export type TestAppOptions = {
19
+ /** Plugins to register. */
20
+ plugins: Plugin.Plugin[];
21
+ /** Plugin ids that are always enabled. Defaults to all provided plugin ids. */
22
+ core?: string[];
23
+ /** Plugin ids that are enabled by default in addition to core. */
24
+ enabled?: string[];
25
+ /** Additional activation events fired alongside SetupReactSurface. */
26
+ setupEvents?: ActivationEvent.ActivationEvent[];
27
+ /**
28
+ * Whether to automatically fire SetupReactSurface + Startup during setup.
29
+ * Defaults to true.
30
+ */
31
+ autoStart?: boolean;
32
+ /**
33
+ * Whether to register the PluginManager + AtomRegistry framework capabilities.
34
+ * Defaults to true.
35
+ */
36
+ registerFrameworkCapabilities?: boolean;
37
+ };
38
+
39
+ /**
40
+ * A running plugin manager plus helpers for driving it in tests.
41
+ */
42
+ export interface TestHarness {
43
+ readonly manager: PluginManager.PluginManager;
44
+ readonly capabilities: CapabilityManager.CapabilityManager;
45
+ readonly registry: Registry.Registry;
46
+
47
+ /** Activate the given event. Equivalent to `manager.activate(event)`. */
48
+ fire(event: ActivationEvent.ActivationEvent | string): Promise<boolean>;
49
+ /** Re-activate all modules that were activated by the given event. */
50
+ reset(event: ActivationEvent.ActivationEvent | string): Promise<boolean>;
51
+
52
+ /** Returns the first contributed capability for the given interface. Throws if none are present. */
53
+ get<T>(iface: Capability.InterfaceDef<T>): T;
54
+ /** Returns all contributed capabilities for the given interface. */
55
+ getAll<T>(iface: Capability.InterfaceDef<T>): T[];
56
+ /** Waits until at least one capability is contributed for the given interface. */
57
+ waitForCapability<T>(iface: Capability.InterfaceDef<T>, opts?: { timeout?: number }): Promise<T>;
58
+ /** Waits until the given activation event has completed. */
59
+ waitForEvent(event: ActivationEvent.ActivationEvent | string, opts?: { timeout?: number }): Promise<void>;
60
+
61
+ /** Invokes an operation through the `Capabilities.OperationInvoker` capability. */
62
+ invoke<I, O>(op: Operation.Definition<I, O>, ...args: void extends I ? [input?: I] : [input: I]): Promise<O>;
63
+
64
+ enable(id: string): Promise<boolean>;
65
+ disable(id: string): Promise<boolean>;
66
+
67
+ /** Shuts down the underlying plugin manager. */
68
+ dispose(): Promise<void>;
69
+
70
+ /** Async-disposable support so tests can use `await using harness = ...`. */
71
+ [Symbol.asyncDispose](): Promise<void>;
72
+ }
73
+
74
+ const DEFAULT_TIMEOUT_MS = 5_000;
75
+
76
+ /**
77
+ * Creates a TestHarness with the same bootstrap sequence that `useApp` performs,
78
+ * minus the React provider tree.
79
+ *
80
+ * For Node-only tests, this is enough to fire activation events, read
81
+ * capabilities, and invoke operations.
82
+ *
83
+ * For React tests, pass the returned harness to `render` or `renderSurface`
84
+ * from `@dxos/app-framework/testing-react`.
85
+ */
86
+ export const createTestApp = async (opts: TestAppOptions): Promise<TestHarness> => {
87
+ const {
88
+ plugins,
89
+ core = plugins.map((plugin) => plugin.meta.id),
90
+ enabled,
91
+ setupEvents = [],
92
+ autoStart = true,
93
+ registerFrameworkCapabilities = true,
94
+ } = opts;
95
+
96
+ const pluginLoader = (id: string) =>
97
+ Effect.sync(() => {
98
+ const plugin = plugins.find((plugin) => plugin.meta.id === id);
99
+ invariant(plugin, `Plugin not found: ${id}`);
100
+ return { plugin };
101
+ });
102
+
103
+ const manager = PluginManager.make({ pluginLoader, plugins, core, enabled });
104
+
105
+ if (registerFrameworkCapabilities) {
106
+ manager.capabilities.contribute({
107
+ interface: Capabilities.PluginManager,
108
+ implementation: manager,
109
+ module: 'org.dxos.app-framework.plugin-manager',
110
+ });
111
+ manager.capabilities.contribute({
112
+ interface: Capabilities.AtomRegistry,
113
+ implementation: manager.registry,
114
+ module: 'org.dxos.app-framework.atom-registry',
115
+ });
116
+ }
117
+
118
+ if (autoStart) {
119
+ try {
120
+ await runAndForwardErrors(
121
+ Effect.all([
122
+ ...setupEvents.map((event) => manager.activate(event)),
123
+ manager.activate(ActivationEvents.SetupReactSurface),
124
+ manager.activate(ActivationEvents.Startup),
125
+ ]),
126
+ );
127
+ } catch (err) {
128
+ await runAndForwardErrors(manager.shutdown()).catch(() => undefined);
129
+ throw err;
130
+ }
131
+ }
132
+
133
+ return new TestHarnessImpl(manager);
134
+ };
135
+
136
+ class TestHarnessImpl implements TestHarness {
137
+ constructor(readonly manager: PluginManager.PluginManager) {}
138
+
139
+ get capabilities(): CapabilityManager.CapabilityManager {
140
+ return this.manager.capabilities;
141
+ }
142
+
143
+ get registry(): Registry.Registry {
144
+ return this.manager.registry;
145
+ }
146
+
147
+ fire(event: ActivationEvent.ActivationEvent | string): Promise<boolean> {
148
+ return runAndForwardErrors(this.manager.activate(event));
149
+ }
150
+
151
+ reset(event: ActivationEvent.ActivationEvent | string): Promise<boolean> {
152
+ return runAndForwardErrors(this.manager.reset(event));
153
+ }
154
+
155
+ get<T>(iface: Capability.InterfaceDef<T>): T {
156
+ return this.manager.capabilities.get(iface);
157
+ }
158
+
159
+ getAll<T>(iface: Capability.InterfaceDef<T>): T[] {
160
+ return this.manager.capabilities.getAll(iface);
161
+ }
162
+
163
+ waitForCapability<T>(iface: Capability.InterfaceDef<T>, opts?: { timeout?: number }): Promise<T> {
164
+ const timeout = opts?.timeout ?? DEFAULT_TIMEOUT_MS;
165
+ return runAndForwardErrors(
166
+ this.manager.capabilities.waitFor(iface).pipe(
167
+ Effect.timeoutFail({
168
+ duration: Duration.millis(timeout),
169
+ onTimeout: () => timeoutError(iface.identifier),
170
+ }),
171
+ ),
172
+ );
173
+ }
174
+
175
+ waitForEvent(event: ActivationEvent.ActivationEvent | string, opts?: { timeout?: number }): Promise<void> {
176
+ const key = typeof event === 'string' ? event : ActivationEvent.eventKey(event);
177
+ const timeout = opts?.timeout ?? DEFAULT_TIMEOUT_MS;
178
+
179
+ const program = Effect.gen(this, function* () {
180
+ const queue = yield* PubSub.subscribe(this.manager.activation);
181
+ // Re-check after subscribing to avoid a race where the event fires
182
+ // between the caller invoking this and the subscription being installed.
183
+ if (this.manager.getEventsFired().includes(key)) {
184
+ return;
185
+ }
186
+ while (true) {
187
+ const message = yield* Queue.take(queue);
188
+ if (message.event === key && message.state === 'activated') {
189
+ return;
190
+ }
191
+ }
192
+ }).pipe(
193
+ Effect.scoped,
194
+ Effect.timeoutFail({
195
+ duration: Duration.millis(timeout),
196
+ onTimeout: () => timeoutError(key),
197
+ }),
198
+ );
199
+
200
+ return runAndForwardErrors(program);
201
+ }
202
+
203
+ async invoke<I, O>(op: Operation.Definition<I, O>, ...args: [input?: I]): Promise<O> {
204
+ const invoker = await this.waitForCapability(Capabilities.OperationInvoker);
205
+ const result = await invoker.invokePromise(op as any, ...(args as [any]));
206
+ if (result.error) {
207
+ throw result.error;
208
+ }
209
+ return result.data as O;
210
+ }
211
+
212
+ enable(id: string): Promise<boolean> {
213
+ return runAndForwardErrors(this.manager.enable(id));
214
+ }
215
+
216
+ disable(id: string): Promise<boolean> {
217
+ return runAndForwardErrors(this.manager.disable(id));
218
+ }
219
+
220
+ async dispose(): Promise<void> {
221
+ await runAndForwardErrors(this.manager.shutdown());
222
+ }
223
+
224
+ [Symbol.asyncDispose](): Promise<void> {
225
+ return this.dispose();
226
+ }
227
+ }
228
+
229
+ const timeoutError = (id: string) => new Error(`Timed out waiting for ${id}`);
@@ -2,5 +2,6 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ export * from './harness';
5
6
  export * from './service';
6
7
  export * from './withPluginManager';
@@ -0,0 +1,48 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Effect from 'effect/Effect';
6
+ import React from 'react';
7
+ import { describe, test } from 'vitest';
8
+
9
+ import { ActivationEvents, Capabilities } from '../common';
10
+ import { Capability, Plugin } from '../core';
11
+ import { Surface } from '../ui';
12
+ import { createTestApp } from './harness';
13
+ import { render, renderSurface } from './react';
14
+
15
+ const testMeta = { id: 'org.dxos.plugin.test.react-harness', name: 'ReactHarnessTest' };
16
+
17
+ const TestPlugin = Plugin.define(testMeta).pipe(
18
+ Plugin.addModule({
19
+ id: 'surfaces',
20
+ activatesOn: ActivationEvents.SetupReactSurface,
21
+ activate: () =>
22
+ Effect.succeed(
23
+ Capability.contributes(Capabilities.ReactSurface, [
24
+ Surface.create<{ message: string }>({
25
+ id: 'greeting',
26
+ role: 'greeting',
27
+ component: ({ data }) => <span data-testid='greeting'>hello {data.message}</span>,
28
+ }),
29
+ ]),
30
+ ),
31
+ }),
32
+ Plugin.make,
33
+ );
34
+
35
+ describe('testing/react', () => {
36
+ test('render wraps ui in the harness provider tree', async ({ expect }) => {
37
+ await using harness = await createTestApp({ plugins: [TestPlugin()] });
38
+ const view = render(harness, <span data-testid='hello'>world</span>);
39
+ expect(view.getByTestId('hello').textContent).toBe('world');
40
+ });
41
+
42
+ test('renderSurface mounts a surface registered by a plugin', async ({ expect }) => {
43
+ await using harness = await createTestApp({ plugins: [TestPlugin()] });
44
+ const view = renderSurface(harness, { role: 'greeting', data: { message: 'plugins' } });
45
+ const node = await view.findByTestId('greeting');
46
+ expect(node.textContent).toBe('hello plugins');
47
+ });
48
+ });
@@ -0,0 +1,113 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { RegistryContext } from '@effect-atom/atom-react';
6
+ import { render as rtlRender, type RenderOptions, type RenderResult } from '@testing-library/react';
7
+ import React, { type FC, Fragment, type PropsWithChildren, type ReactNode } from 'react';
8
+
9
+ import { ContextProtocolProvider } from '@dxos/web-context-react';
10
+
11
+ import { Capabilities } from '../common';
12
+ import { PluginManagerContext } from '../context';
13
+ import { topologicalSort } from '../helpers';
14
+ import { PluginManagerProvider } from '../ui/components/PluginManager/PluginManagerProvider';
15
+ import { SurfaceComponent } from '../ui/components/Surface/SurfaceComponent';
16
+ import { type TestHarness } from './harness';
17
+
18
+ export type HarnessRenderOptions = Omit<RenderOptions, 'wrapper'> & {
19
+ /** Additional providers to wrap around the harness tree (applied innermost first). */
20
+ reactContexts?: FC<PropsWithChildren>[];
21
+ };
22
+
23
+ /**
24
+ * Renders `ui` wrapped in the same provider tree as `useApp` plus every contributed
25
+ * `Capabilities.ReactContext`.
26
+ */
27
+ export const render = (harness: TestHarness, ui: ReactNode, options?: HarnessRenderOptions): RenderResult => {
28
+ const { reactContexts = [], ...rest } = options ?? {};
29
+ const Wrapper = ({ children }: PropsWithChildren) => (
30
+ <HarnessProviders harness={harness} extra={reactContexts}>
31
+ {children}
32
+ </HarnessProviders>
33
+ );
34
+ return rtlRender(<>{ui}</>, { ...rest, wrapper: Wrapper });
35
+ };
36
+
37
+ export type RenderSurfaceProps = {
38
+ role: string;
39
+ data?: unknown;
40
+ limit?: number;
41
+ fallback?: FC<{ error: Error; data?: any }>;
42
+ placeholder?: ReactNode;
43
+ };
44
+
45
+ /**
46
+ * Renders a `Surface` with the given role/data inside the harness provider tree.
47
+ */
48
+ export const renderSurface = (
49
+ harness: TestHarness,
50
+ props: RenderSurfaceProps,
51
+ options?: HarnessRenderOptions,
52
+ ): RenderResult => {
53
+ const { role, data, limit, fallback, placeholder } = props;
54
+ return render(
55
+ harness,
56
+ <SurfaceComponent role={role} data={data as any} limit={limit} fallback={fallback} placeholder={placeholder} />,
57
+ options,
58
+ );
59
+ };
60
+
61
+ type HarnessProvidersProps = PropsWithChildren<{
62
+ harness: TestHarness;
63
+ extra: FC<PropsWithChildren>[];
64
+ }>;
65
+
66
+ const HarnessProviders = ({ harness, extra, children }: HarnessProvidersProps) => {
67
+ const contributed = harness.getAll(Capabilities.ReactContext);
68
+ const ContributedContext = composeContexts(contributed);
69
+ const ExtraContext = composeExtra(extra);
70
+ return (
71
+ <PluginManagerProvider value={harness.manager}>
72
+ <ContextProtocolProvider value={harness.manager} context={PluginManagerContext}>
73
+ <RegistryContext.Provider value={harness.registry}>
74
+ <ContributedContext>
75
+ <ExtraContext>{children}</ExtraContext>
76
+ </ContributedContext>
77
+ </RegistryContext.Provider>
78
+ </ContextProtocolProvider>
79
+ </PluginManagerProvider>
80
+ );
81
+ };
82
+
83
+ const composeContexts = (contexts: Capabilities.ReactContext[]): FC<PropsWithChildren> => {
84
+ if (contexts.length === 0) {
85
+ return Passthrough;
86
+ }
87
+ return topologicalSort(contexts)
88
+ .map(({ context }) => context)
89
+ .reduce((Acc, Next) => ({ children }: PropsWithChildren) => (
90
+ <Acc>
91
+ <Next>{children}</Next>
92
+ </Acc>
93
+ ));
94
+ };
95
+
96
+ // Composes in innermost-first order: the first context in the array wraps
97
+ // `children` directly; subsequent contexts wrap the accumulator.
98
+ const composeExtra = (contexts: FC<PropsWithChildren>[]): FC<PropsWithChildren> => {
99
+ if (contexts.length === 0) {
100
+ return Passthrough;
101
+ }
102
+ return contexts.reduce<FC<PropsWithChildren>>(
103
+ (Acc, Next) =>
104
+ ({ children }: PropsWithChildren) => (
105
+ <Next>
106
+ <Acc>{children}</Acc>
107
+ </Next>
108
+ ),
109
+ Passthrough,
110
+ );
111
+ };
112
+
113
+ const Passthrough: FC<PropsWithChildren> = ({ children }) => <Fragment>{children}</Fragment>;
@@ -24,7 +24,7 @@ export const fromPlugins = (plugins: Plugin.Plugin[]) =>
24
24
  Effect.sync(() => {
25
25
  const plugin = plugins.find((plugin) => plugin.meta.id === id);
26
26
  invariant(plugin, `Plugin not found: ${id}`);
27
- return plugin;
27
+ return { plugin };
28
28
  });
29
29
 
30
30
  const manager = PluginManager.make({
@@ -23,7 +23,7 @@ const DefaultStory = () => {
23
23
  };
24
24
 
25
25
  const meta = {
26
- title: 'sdk/app-framework/withPluginManager',
26
+ title: 'sdk/app-framework/testing/withPluginManager',
27
27
  render: DefaultStory,
28
28
  decorators: [
29
29
  withTheme(),
@@ -75,7 +75,6 @@ export const withPluginManager = <Args,>(init: WithPluginManagerInitializer<Args
75
75
  // Storybook replaces the full context object often, so key manager ownership by story id.
76
76
  useEffect(() => {
77
77
  const pluginManager = setupPluginManager(options);
78
-
79
78
  const capability = Capability.contributes(Capabilities.ReactRoot, {
80
79
  id: storyId,
81
80
  root: () => <Story />,
@@ -115,7 +114,6 @@ const WithPluginManagerApp = ({ fireEvents, pluginManager, setupEvents, storyId
115
114
  }, [fireEvents, pluginManager, storyId]);
116
115
 
117
116
  const App = useApp({ pluginManager, setupEvents });
118
-
119
117
  return <App />;
120
118
  };
121
119
 
@@ -55,11 +55,7 @@ const DefaultStory = ({ plugins }: DefaultStoryProps) => {
55
55
  plugins,
56
56
  core,
57
57
  placeholder: () => {
58
- return (
59
- <div role='none' className='text-description'>
60
- Loading...
61
- </div>
62
- );
58
+ return <div className='text-description'>Loading...</div>;
63
59
  },
64
60
  });
65
61
 
@@ -67,7 +63,7 @@ const DefaultStory = ({ plugins }: DefaultStoryProps) => {
67
63
  };
68
64
 
69
65
  const meta = {
70
- title: 'sdk/app-framework/App',
66
+ title: 'sdk/app-framework/components/App',
71
67
  render: DefaultStory,
72
68
  decorators: [withTheme()],
73
69
  parameters: {
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React, { type PropsWithChildren } from 'react';
5
+ import React, { type PropsWithChildren, useEffect } from 'react';
6
6
 
7
7
  import { Capabilities } from '../../../common';
8
8
  import { topologicalSort } from '../../../helpers';
@@ -14,10 +14,33 @@ export type AppProps = Pick<UseAppOptions, 'placeholder' | 'debounce'> & {
14
14
  progress?: StartupProgress;
15
15
  };
16
16
 
17
+ const FIRST_INTERACTIVE_MARK = 'app-framework:first-interactive';
18
+
17
19
  export const App = ({ placeholder: Placeholder, ready, error, debounce, progress }: AppProps) => {
18
20
  const reactContexts = useCapabilities(Capabilities.ReactContext);
19
21
  const reactRoots = useCapabilities(Capabilities.ReactRoot);
20
22
  const stage = useLoading(ready, debounce);
23
+ const placeholderDismissed = stage >= LoadingState.Done;
24
+
25
+ // Emit a once-per-app `app-framework:first-interactive` mark the first time
26
+ // the placeholder is dismissed and the real app shell renders. Closes the
27
+ // gap between `Startup` activated and the first interactive paint.
28
+ //
29
+ // Also dismisses the native-DOM boot loader from
30
+ // `@dxos/app-framework/vite-plugin` here — the framework owns the
31
+ // handoff-complete signal, so dismissing in `App` covers both hosts that
32
+ // wire in a `Placeholder` (which may have its own dismiss path) and hosts
33
+ // that don't (the loader would otherwise sit forever as a `z-index: 10`
34
+ // overlay covering the rendered shell).
35
+ useEffect(() => {
36
+ if (!placeholderDismissed) {
37
+ return;
38
+ }
39
+ if (performance.getEntriesByName(FIRST_INTERACTIVE_MARK).length === 0) {
40
+ performance.mark(FIRST_INTERACTIVE_MARK);
41
+ }
42
+ (window as { __bootLoader?: { dismiss?: () => void } }).__bootLoader?.dismiss?.();
43
+ }, [placeholderDismissed]);
21
44
 
22
45
  if (error) {
23
46
  // This triggers the error boundary to provide UI feedback for the startup error.
@@ -25,7 +48,7 @@ export const App = ({ placeholder: Placeholder, ready, error, debounce, progress
25
48
  }
26
49
 
27
50
  // TODO(wittjosiah): Consider using Suspense instead.
28
- if (stage < LoadingState.Done) {
51
+ if (!placeholderDismissed) {
29
52
  if (!Placeholder) {
30
53
  return null;
31
54
  }
@@ -0,0 +1,77 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import React, { useEffect, useState } from 'react';
7
+ import { expect, within } from 'storybook/test';
8
+
9
+ import { Composer } from '@dxos/brand';
10
+ import { Input, Panel, Toolbar } from '@dxos/react-ui';
11
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
12
+
13
+ import { Placeholder } from './Placeholder';
14
+
15
+ /**
16
+ * The story renders the brand-agnostic `Placeholder` from `@dxos/app-framework/ui`
17
+ * with the Composer brand icon as its `logo`, so the storybook mirrors what
18
+ * composer-app actually mounts in production. The render function form lets
19
+ * the SVG accept the placeholder's animation `className` directly.
20
+ */
21
+ const meta = {
22
+ title: 'sdk/app-framework/components/Placeholder',
23
+ component: Placeholder,
24
+ render: () => {
25
+ const [stage, setStage] = useState(0);
26
+ const [auto, setAuto] = useState(false);
27
+ useEffect(() => {
28
+ if (!auto) {
29
+ return;
30
+ }
31
+
32
+ const interval = setInterval(() => {
33
+ setStage((prev) => {
34
+ return prev >= 2 ? 0 : prev + 1;
35
+ });
36
+ }, 3_000);
37
+
38
+ return () => clearInterval(interval);
39
+ }, [auto]);
40
+
41
+ return (
42
+ <Panel.Root>
43
+ <Panel.Toolbar asChild>
44
+ <Toolbar.Root classNames='gap-2'>
45
+ <Input.Root>
46
+ <Input.Checkbox checked={auto} onCheckedChange={(checked) => setAuto(checked === true)} />
47
+ <Input.Label>Auto</Input.Label>
48
+ </Input.Root>
49
+ <Toolbar.Button onClick={() => setStage((prev) => (prev < 2 ? prev + 1 : 0))}>Next</Toolbar.Button>
50
+ <Toolbar.Text>{stage}</Toolbar.Text>
51
+ </Toolbar.Root>
52
+ </Panel.Toolbar>
53
+ <Panel.Content>
54
+ <Placeholder stage={stage} logo={(logoProps) => <Composer {...logoProps} />} />
55
+ </Panel.Content>
56
+ </Panel.Root>
57
+ );
58
+ },
59
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
60
+ tags: ['test'],
61
+ } satisfies Meta<typeof Placeholder>;
62
+
63
+ export default meta;
64
+
65
+ type Story = StoryObj<typeof meta>;
66
+
67
+ export const Default: Story = {
68
+ args: {
69
+ stage: 0,
70
+ },
71
+ play: async ({ canvasElement }) => {
72
+ const canvas = within(canvasElement);
73
+ // Smoke test: the toolbar's `Next` button is rendered alongside the
74
+ // placeholder, so the story mounted without throwing.
75
+ await expect(canvas.getByRole('button', { name: 'Next' })).toBeInTheDocument();
76
+ },
77
+ };