@dxos/plugin-automation 0.7.4 → 0.7.5-labs.401163d

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 (302) hide show
  1. package/dist/lib/browser/AutomationPanel-VW2XIUPU.mjs +8 -0
  2. package/dist/lib/browser/ChatContainer-OLRZNGWK.mjs +12 -0
  3. package/dist/lib/browser/ai-client-5CNY6JBF.mjs +22 -0
  4. package/dist/lib/browser/ai-client-5CNY6JBF.mjs.map +7 -0
  5. package/dist/lib/browser/app-graph-builder-6H7MDCXE.mjs +197 -0
  6. package/dist/lib/browser/app-graph-builder-6H7MDCXE.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-2H2EUYXL.mjs +15 -0
  8. package/dist/lib/browser/chunk-2H2EUYXL.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-ITG6CBKL.mjs +1602 -0
  10. package/dist/lib/browser/chunk-ITG6CBKL.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-MRBC5J4T.mjs +518 -0
  12. package/dist/lib/browser/chunk-MRBC5J4T.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-NQFZ6XRX.mjs +243 -0
  14. package/dist/lib/browser/chunk-NQFZ6XRX.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-Q4IMHYGH.mjs +150 -0
  16. package/dist/lib/browser/chunk-Q4IMHYGH.mjs.map +7 -0
  17. package/dist/lib/browser/{chunk-X5KMOH3I.mjs → chunk-R4JH4TLE.mjs} +6 -4
  18. package/dist/lib/browser/chunk-R4JH4TLE.mjs.map +7 -0
  19. package/dist/lib/browser/index.mjs +157 -252
  20. package/dist/lib/browser/index.mjs.map +4 -4
  21. package/dist/lib/browser/intent-resolver-BWAXKT27.mjs +29 -0
  22. package/dist/lib/browser/intent-resolver-BWAXKT27.mjs.map +7 -0
  23. package/dist/lib/browser/meta.json +1 -1
  24. package/dist/lib/browser/react-surface-O6SHIBCA.mjs +58 -0
  25. package/dist/lib/browser/react-surface-O6SHIBCA.mjs.map +7 -0
  26. package/dist/lib/browser/types/index.mjs +12 -3
  27. package/dist/lib/node/{meta.cjs → AutomationPanel-G6EDDYWW.cjs} +7 -11
  28. package/dist/lib/node/AutomationPanel-G6EDDYWW.cjs.map +7 -0
  29. package/dist/lib/node/ChatContainer-GRZDJIG5.cjs +33 -0
  30. package/dist/lib/node/ChatContainer-GRZDJIG5.cjs.map +7 -0
  31. package/dist/lib/node/ai-client-FKLPDELV.cjs +38 -0
  32. package/dist/lib/node/ai-client-FKLPDELV.cjs.map +7 -0
  33. package/dist/lib/node/app-graph-builder-7JMJLZIE.cjs +212 -0
  34. package/dist/lib/node/app-graph-builder-7JMJLZIE.cjs.map +7 -0
  35. package/dist/lib/node/chunk-CFBERLTN.cjs +1586 -0
  36. package/dist/lib/node/chunk-CFBERLTN.cjs.map +7 -0
  37. package/dist/lib/node/{chunk-DTJ7XVO2.cjs → chunk-EQYHOTGG.cjs} +11 -8
  38. package/dist/lib/node/chunk-EQYHOTGG.cjs.map +7 -0
  39. package/dist/lib/node/chunk-FQUY77HT.cjs +527 -0
  40. package/dist/lib/node/chunk-FQUY77HT.cjs.map +7 -0
  41. package/dist/lib/node/chunk-GB7245FH.cjs +173 -0
  42. package/dist/lib/node/chunk-GB7245FH.cjs.map +7 -0
  43. package/dist/lib/node/chunk-QXIHYOMF.cjs +267 -0
  44. package/dist/lib/node/chunk-QXIHYOMF.cjs.map +7 -0
  45. package/dist/lib/node/chunk-U5Z7LFWB.cjs +34 -0
  46. package/dist/lib/node/chunk-U5Z7LFWB.cjs.map +7 -0
  47. package/dist/lib/node/index.cjs +174 -282
  48. package/dist/lib/node/index.cjs.map +4 -4
  49. package/dist/lib/node/intent-resolver-C6OKFVEW.cjs +44 -0
  50. package/dist/lib/node/intent-resolver-C6OKFVEW.cjs.map +7 -0
  51. package/dist/lib/node/meta.json +1 -1
  52. package/dist/lib/node/react-surface-JT6SVCPK.cjs +76 -0
  53. package/dist/lib/node/react-surface-JT6SVCPK.cjs.map +7 -0
  54. package/dist/lib/node/types/index.cjs +16 -7
  55. package/dist/lib/node/types/index.cjs.map +2 -2
  56. package/dist/lib/node-esm/AutomationPanel-V3IWQAMO.mjs +9 -0
  57. package/dist/lib/node-esm/AutomationPanel-V3IWQAMO.mjs.map +7 -0
  58. package/dist/lib/node-esm/ChatContainer-ORVWQPJO.mjs +13 -0
  59. package/dist/lib/node-esm/ChatContainer-ORVWQPJO.mjs.map +7 -0
  60. package/dist/lib/node-esm/ai-client-XGNA6SJ5.mjs +23 -0
  61. package/dist/lib/node-esm/ai-client-XGNA6SJ5.mjs.map +7 -0
  62. package/dist/lib/node-esm/app-graph-builder-C6NUQGCU.mjs +198 -0
  63. package/dist/lib/node-esm/app-graph-builder-C6NUQGCU.mjs.map +7 -0
  64. package/dist/lib/node-esm/chunk-2OKJZ4ZW.mjs +519 -0
  65. package/dist/lib/node-esm/chunk-2OKJZ4ZW.mjs.map +7 -0
  66. package/dist/lib/node-esm/chunk-6HLBYDUI.mjs +244 -0
  67. package/dist/lib/node-esm/chunk-6HLBYDUI.mjs.map +7 -0
  68. package/dist/lib/node-esm/chunk-7VPT3OO3.mjs +1603 -0
  69. package/dist/lib/node-esm/chunk-7VPT3OO3.mjs.map +7 -0
  70. package/dist/lib/node-esm/chunk-DNCXRGAF.mjs +151 -0
  71. package/dist/lib/node-esm/chunk-DNCXRGAF.mjs.map +7 -0
  72. package/dist/lib/node-esm/{chunk-HNOBZHWK.mjs → chunk-EMVA6QUT.mjs} +6 -4
  73. package/dist/lib/node-esm/chunk-EMVA6QUT.mjs.map +7 -0
  74. package/dist/lib/node-esm/chunk-IJRTDSKN.mjs +16 -0
  75. package/dist/lib/node-esm/chunk-IJRTDSKN.mjs.map +7 -0
  76. package/dist/lib/node-esm/index.mjs +157 -252
  77. package/dist/lib/node-esm/index.mjs.map +4 -4
  78. package/dist/lib/node-esm/intent-resolver-DCP4ZDBA.mjs +30 -0
  79. package/dist/lib/node-esm/intent-resolver-DCP4ZDBA.mjs.map +7 -0
  80. package/dist/lib/node-esm/meta.json +1 -1
  81. package/dist/lib/node-esm/react-surface-DSVM33SA.mjs +59 -0
  82. package/dist/lib/node-esm/react-surface-DSVM33SA.mjs.map +7 -0
  83. package/dist/lib/node-esm/types/index.mjs +12 -3
  84. package/dist/types/src/AutomationPlugin.d.ts +1 -3
  85. package/dist/types/src/AutomationPlugin.d.ts.map +1 -1
  86. package/dist/types/src/artifacts.stories.d.ts +16 -0
  87. package/dist/types/src/artifacts.stories.d.ts.map +1 -0
  88. package/dist/types/src/capabilities/ai-client.d.ts +5 -0
  89. package/dist/types/src/capabilities/ai-client.d.ts.map +1 -0
  90. package/dist/types/src/capabilities/app-graph-builder.d.ts +181 -0
  91. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  92. package/dist/types/src/capabilities/capabilities.d.ts +5 -0
  93. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
  94. package/dist/types/src/capabilities/index.d.ts +182 -0
  95. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  96. package/dist/types/src/capabilities/intent-resolver.d.ts +4 -0
  97. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
  98. package/dist/types/src/capabilities/react-surface.d.ts +4 -0
  99. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  100. package/dist/types/src/components/AmbientChatDialog/AmbientChatDialog.d.ts +3 -0
  101. package/dist/types/src/components/AmbientChatDialog/AmbientChatDialog.d.ts.map +1 -0
  102. package/dist/types/src/components/AmbientChatDialog/AmbientChatDialog.stories.d.ts +8 -0
  103. package/dist/types/src/components/AmbientChatDialog/AmbientChatDialog.stories.d.ts.map +1 -0
  104. package/dist/types/src/components/AmbientChatDialog/index.d.ts +2 -0
  105. package/dist/types/src/components/AmbientChatDialog/index.d.ts.map +1 -0
  106. package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts +1 -1
  107. package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts.map +1 -1
  108. package/dist/types/src/components/AutomationPanel/AutomationPanel.stories.d.ts.map +1 -1
  109. package/dist/types/src/components/Box/StatusLine.d.ts +11 -0
  110. package/dist/types/src/components/Box/StatusLine.d.ts.map +1 -0
  111. package/dist/types/src/components/Box/StatusLine.stories.d.ts +9 -0
  112. package/dist/types/src/components/Box/StatusLine.stories.d.ts.map +1 -0
  113. package/dist/types/src/components/Box/Tabbed.d.ts +15 -0
  114. package/dist/types/src/components/Box/Tabbed.d.ts.map +1 -0
  115. package/dist/types/src/components/Box/Tabbed.stories.d.ts +8 -0
  116. package/dist/types/src/components/Box/Tabbed.stories.d.ts.map +1 -0
  117. package/dist/types/src/components/Box/ToggleContainer.d.ts +13 -0
  118. package/dist/types/src/components/Box/ToggleContainer.d.ts.map +1 -0
  119. package/dist/types/src/components/Box/ToggleContainer.stories.d.ts +9 -0
  120. package/dist/types/src/components/Box/ToggleContainer.stories.d.ts.map +1 -0
  121. package/dist/types/src/components/Box/index.d.ts +4 -0
  122. package/dist/types/src/components/Box/index.d.ts.map +1 -0
  123. package/dist/types/src/components/ChatContainer/ChatContainer.d.ts +7 -0
  124. package/dist/types/src/components/ChatContainer/ChatContainer.d.ts.map +1 -0
  125. package/dist/types/src/components/ChatContainer/index.d.ts +4 -0
  126. package/dist/types/src/components/ChatContainer/index.d.ts.map +1 -0
  127. package/dist/types/src/components/MarkdownViewer/MarkdownViewer.d.ts +14 -0
  128. package/dist/types/src/components/MarkdownViewer/MarkdownViewer.d.ts.map +1 -0
  129. package/dist/types/src/components/MarkdownViewer/MarkdownViewer.stories.d.ts +8 -0
  130. package/dist/types/src/components/MarkdownViewer/MarkdownViewer.stories.d.ts.map +1 -0
  131. package/dist/types/src/components/MarkdownViewer/index.d.ts +2 -0
  132. package/dist/types/src/components/MarkdownViewer/index.d.ts.map +1 -0
  133. package/dist/types/src/components/Prompt/Prompt.d.ts +7 -0
  134. package/dist/types/src/components/Prompt/Prompt.d.ts.map +1 -0
  135. package/dist/types/src/components/Prompt/Prompt.stories.d.ts +8 -0
  136. package/dist/types/src/components/Prompt/Prompt.stories.d.ts.map +1 -0
  137. package/dist/types/src/components/Prompt/index.d.ts +2 -0
  138. package/dist/types/src/components/Prompt/index.d.ts.map +1 -0
  139. package/dist/types/src/components/Prompt/prompt-autocomplete.d.ts +20 -0
  140. package/dist/types/src/components/Prompt/prompt-autocomplete.d.ts.map +1 -0
  141. package/dist/types/src/components/ServiceRegistry/ServiceRegistry.d.ts +6 -0
  142. package/dist/types/src/components/ServiceRegistry/ServiceRegistry.d.ts.map +1 -0
  143. package/dist/types/src/components/ServiceRegistry/ServiceRegistry.stories.d.ts +8 -0
  144. package/dist/types/src/components/ServiceRegistry/ServiceRegistry.stories.d.ts.map +1 -0
  145. package/dist/types/src/components/ServiceRegistry/index.d.ts +2 -0
  146. package/dist/types/src/components/ServiceRegistry/index.d.ts.map +1 -0
  147. package/dist/types/src/components/Thread/Thread.d.ts +11 -0
  148. package/dist/types/src/components/Thread/Thread.d.ts.map +1 -0
  149. package/dist/types/src/components/Thread/Thread.stories.d.ts +11 -0
  150. package/dist/types/src/components/Thread/Thread.stories.d.ts.map +1 -0
  151. package/dist/types/src/components/Thread/ThreadMessage.d.ts +12 -0
  152. package/dist/types/src/components/Thread/ThreadMessage.d.ts.map +1 -0
  153. package/dist/types/src/components/Thread/index.d.ts +2 -0
  154. package/dist/types/src/components/Thread/index.d.ts.map +1 -0
  155. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts +1 -2
  156. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts.map +1 -1
  157. package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts.map +1 -1
  158. package/dist/types/src/components/index.d.ts +13 -2
  159. package/dist/types/src/components/index.d.ts.map +1 -1
  160. package/dist/types/src/hooks/email.d.ts.map +1 -1
  161. package/dist/types/src/hooks/index.d.ts +4 -0
  162. package/dist/types/src/hooks/index.d.ts.map +1 -1
  163. package/dist/types/src/hooks/invocation-handler.d.ts.map +1 -1
  164. package/dist/types/src/hooks/processor.d.ts +70 -0
  165. package/dist/types/src/hooks/processor.d.ts.map +1 -0
  166. package/dist/types/src/hooks/processor.test.d.ts +2 -0
  167. package/dist/types/src/hooks/processor.test.d.ts.map +1 -0
  168. package/dist/types/src/hooks/useChatProcessor.d.ts +7 -0
  169. package/dist/types/src/hooks/useChatProcessor.d.ts.map +1 -0
  170. package/dist/types/src/hooks/useMessageQueue.d.ts +41 -0
  171. package/dist/types/src/hooks/useMessageQueue.d.ts.map +1 -0
  172. package/dist/types/src/hooks/useServices.d.ts +7 -0
  173. package/dist/types/src/hooks/useServices.d.ts.map +1 -0
  174. package/dist/types/src/index.d.ts +1 -2
  175. package/dist/types/src/index.d.ts.map +1 -1
  176. package/dist/types/src/meta.d.ts +2 -2
  177. package/dist/types/src/meta.d.ts.map +1 -1
  178. package/dist/types/src/testing/index.d.ts +2 -1
  179. package/dist/types/src/testing/index.d.ts.map +1 -1
  180. package/dist/types/src/testing/{testing.d.ts → test-functions.d.ts} +3 -1
  181. package/dist/types/src/testing/test-functions.d.ts.map +1 -0
  182. package/dist/types/src/testing/test-services.d.ts +5 -0
  183. package/dist/types/src/testing/test-services.d.ts.map +1 -0
  184. package/dist/types/src/tools/function.d.ts +5 -0
  185. package/dist/types/src/tools/function.d.ts.map +1 -0
  186. package/dist/types/src/tools/index.d.ts +3 -0
  187. package/dist/types/src/tools/index.d.ts.map +1 -0
  188. package/dist/types/src/tools/openapi.d.ts +10 -0
  189. package/dist/types/src/tools/openapi.d.ts.map +1 -0
  190. package/dist/types/src/tools/openapi.test.d.ts +2 -0
  191. package/dist/types/src/tools/openapi.test.d.ts.map +1 -0
  192. package/dist/types/src/translations.d.ts +84 -2
  193. package/dist/types/src/translations.d.ts.map +1 -1
  194. package/dist/types/src/types/index.d.ts +1 -0
  195. package/dist/types/src/types/index.d.ts.map +1 -1
  196. package/dist/types/src/types/registry.d.ts +10 -0
  197. package/dist/types/src/types/registry.d.ts.map +1 -0
  198. package/dist/types/src/types/schema.d.ts +189 -39
  199. package/dist/types/src/types/schema.d.ts.map +1 -1
  200. package/dist/types/src/types/types.d.ts +16 -5
  201. package/dist/types/src/types/types.d.ts.map +1 -1
  202. package/dist/types/tsconfig.tsbuildinfo +1 -0
  203. package/package.json +57 -48
  204. package/src/AutomationPlugin.tsx +77 -194
  205. package/src/artifacts.stories.tsx +241 -0
  206. package/src/capabilities/ai-client.ts +19 -0
  207. package/src/capabilities/app-graph-builder.ts +187 -0
  208. package/src/capabilities/capabilities.ts +12 -0
  209. package/src/capabilities/index.ts +12 -0
  210. package/src/capabilities/intent-resolver.ts +27 -0
  211. package/src/capabilities/react-surface.tsx +42 -0
  212. package/src/components/AmbientChatDialog/AmbientChatDialog.stories.tsx +44 -0
  213. package/src/components/AmbientChatDialog/AmbientChatDialog.tsx +63 -0
  214. package/src/components/AmbientChatDialog/index.ts +5 -0
  215. package/src/components/AutomationPanel/AutomationPanel.stories.tsx +1 -2
  216. package/src/components/AutomationPanel/AutomationPanel.tsx +61 -39
  217. package/src/components/Box/StatusLine.stories.tsx +52 -0
  218. package/src/components/Box/StatusLine.tsx +76 -0
  219. package/src/components/Box/Tabbed.stories.tsx +51 -0
  220. package/src/components/Box/Tabbed.tsx +89 -0
  221. package/src/components/Box/ToggleContainer.stories.tsx +110 -0
  222. package/src/components/Box/ToggleContainer.tsx +108 -0
  223. package/src/components/Box/index.ts +7 -0
  224. package/src/components/ChatContainer/ChatContainer.tsx +52 -0
  225. package/src/components/ChatContainer/index.ts +8 -0
  226. package/src/components/MarkdownViewer/MarkdownViewer.stories.tsx +56 -0
  227. package/src/components/MarkdownViewer/MarkdownViewer.tsx +79 -0
  228. package/src/components/MarkdownViewer/index.ts +5 -0
  229. package/src/components/Prompt/Prompt.stories.tsx +50 -0
  230. package/src/components/Prompt/Prompt.tsx +39 -0
  231. package/src/components/Prompt/index.ts +5 -0
  232. package/src/components/Prompt/prompt-autocomplete.ts +200 -0
  233. package/src/components/PromptEditor/PromptEditor.stories.tsx +6 -6
  234. package/src/components/PromptEditor/PromptEditor.tsx +3 -3
  235. package/src/components/ServiceRegistry/ServiceRegistry.stories.tsx +49 -0
  236. package/src/components/ServiceRegistry/ServiceRegistry.tsx +76 -0
  237. package/src/components/ServiceRegistry/index.ts +5 -0
  238. package/src/components/Thread/Thread.stories.tsx +197 -0
  239. package/src/components/Thread/Thread.tsx +156 -0
  240. package/src/components/Thread/ThreadMessage.tsx +225 -0
  241. package/src/components/Thread/index.ts +5 -0
  242. package/src/components/TriggerEditor/TriggerEditor.stories.tsx +1 -2
  243. package/src/components/TriggerEditor/TriggerEditor.tsx +86 -16
  244. package/src/components/index.ts +10 -1
  245. package/src/hooks/email.ts +2 -2
  246. package/src/hooks/index.ts +5 -0
  247. package/src/hooks/invocation-handler.ts +2 -2
  248. package/src/hooks/processor.test.ts +15 -0
  249. package/src/hooks/processor.ts +210 -0
  250. package/src/hooks/useChatProcessor.tsx +86 -0
  251. package/src/hooks/useMessageQueue.ts +23 -0
  252. package/src/hooks/useServices.ts +28 -0
  253. package/src/index.ts +1 -4
  254. package/src/meta.ts +5 -2
  255. package/src/testing/index.ts +2 -1
  256. package/src/testing/{testing.ts → test-functions.ts} +9 -2
  257. package/src/testing/test-services.ts +131 -0
  258. package/src/tools/function.ts +47 -0
  259. package/src/tools/index.ts +6 -0
  260. package/src/tools/openapi.test.ts +224 -0
  261. package/src/tools/openapi.ts +331 -0
  262. package/src/translations.ts +18 -2
  263. package/src/types/index.ts +1 -0
  264. package/src/types/registry.ts +26 -0
  265. package/src/types/schema.ts +96 -2
  266. package/src/types/types.ts +15 -21
  267. package/dist/lib/browser/AssistantPanel-N3QSALKY.mjs +0 -341
  268. package/dist/lib/browser/AssistantPanel-N3QSALKY.mjs.map +0 -7
  269. package/dist/lib/browser/AutomationPanel-AQMN2CQR.mjs +0 -153
  270. package/dist/lib/browser/AutomationPanel-AQMN2CQR.mjs.map +0 -7
  271. package/dist/lib/browser/chunk-7KB4UMXO.mjs +0 -49
  272. package/dist/lib/browser/chunk-7KB4UMXO.mjs.map +0 -7
  273. package/dist/lib/browser/chunk-X5KMOH3I.mjs.map +0 -7
  274. package/dist/lib/browser/meta.mjs +0 -9
  275. package/dist/lib/node/AssistantPanel-RIA4TI3B.cjs +0 -361
  276. package/dist/lib/node/AssistantPanel-RIA4TI3B.cjs.map +0 -7
  277. package/dist/lib/node/AutomationPanel-HZS5WKI5.cjs +0 -173
  278. package/dist/lib/node/AutomationPanel-HZS5WKI5.cjs.map +0 -7
  279. package/dist/lib/node/chunk-CUCUWUAF.cjs +0 -73
  280. package/dist/lib/node/chunk-CUCUWUAF.cjs.map +0 -7
  281. package/dist/lib/node/chunk-DTJ7XVO2.cjs.map +0 -7
  282. package/dist/lib/node/meta.cjs.map +0 -7
  283. package/dist/lib/node-esm/AssistantPanel-72YH43CH.mjs +0 -342
  284. package/dist/lib/node-esm/AssistantPanel-72YH43CH.mjs.map +0 -7
  285. package/dist/lib/node-esm/AutomationPanel-JUHOWQWW.mjs +0 -154
  286. package/dist/lib/node-esm/AutomationPanel-JUHOWQWW.mjs.map +0 -7
  287. package/dist/lib/node-esm/chunk-23LY7DYS.mjs +0 -51
  288. package/dist/lib/node-esm/chunk-23LY7DYS.mjs.map +0 -7
  289. package/dist/lib/node-esm/chunk-HNOBZHWK.mjs.map +0 -7
  290. package/dist/lib/node-esm/meta.mjs +0 -10
  291. package/dist/types/src/components/AssistantPanel/AssistantPanel.d.ts +0 -8
  292. package/dist/types/src/components/AssistantPanel/AssistantPanel.d.ts.map +0 -1
  293. package/dist/types/src/components/AssistantPanel/index.d.ts +0 -3
  294. package/dist/types/src/components/AssistantPanel/index.d.ts.map +0 -1
  295. package/dist/types/src/components/AssistantPanel/system-instructions.d.ts +0 -6
  296. package/dist/types/src/components/AssistantPanel/system-instructions.d.ts.map +0 -1
  297. package/dist/types/src/testing/testing.d.ts.map +0 -1
  298. package/src/components/AssistantPanel/AssistantPanel.tsx +0 -230
  299. package/src/components/AssistantPanel/index.ts +0 -7
  300. package/src/components/AssistantPanel/system-instructions.ts +0 -166
  301. /package/dist/lib/browser/{meta.mjs.map → AutomationPanel-VW2XIUPU.mjs.map} +0 -0
  302. /package/dist/lib/{node-esm/meta.mjs.map → browser/ChatContainer-OLRZNGWK.mjs.map} +0 -0
@@ -0,0 +1,187 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Capabilities, contributes, createIntent, LayoutAction, type PluginsContext } from '@dxos/app-framework';
6
+ import { ClientCapabilities } from '@dxos/plugin-client';
7
+ import { createExtension, type Node, ROOT_ID, toSignal } from '@dxos/plugin-graph';
8
+ import { memoizeQuery } from '@dxos/plugin-space';
9
+ import { getTypename, parseId, SpaceState } from '@dxos/react-client/echo';
10
+
11
+ import { AMBIENT_CHAT_DIALOG, AUTOMATION_PLUGIN } from '../meta';
12
+
13
+ export default (context: PluginsContext) => {
14
+ const resolve = (typename: string) =>
15
+ context.requestCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ?? {};
16
+
17
+ return contributes(Capabilities.AppGraphBuilder, [
18
+ createExtension({
19
+ id: `${AUTOMATION_PLUGIN}/ambient-chat`,
20
+ filter: (node): node is Node<null> => node.id === ROOT_ID,
21
+ actions: () => [
22
+ {
23
+ id: `${LayoutAction.UpdateDialog._tag}/ambient-chat/open`,
24
+ data: async () => {
25
+ const { dispatchPromise: dispatch } = context.requestCapability(Capabilities.IntentDispatcher);
26
+ await dispatch(
27
+ createIntent(LayoutAction.UpdateDialog, {
28
+ part: 'dialog',
29
+ subject: AMBIENT_CHAT_DIALOG,
30
+ options: {
31
+ state: true,
32
+ blockAlign: 'end',
33
+ },
34
+ }),
35
+ );
36
+ },
37
+ properties: {
38
+ label: ['open ambient chat label', { ns: AUTOMATION_PLUGIN }],
39
+ icon: 'ph--chat-centered-text--regular',
40
+ disposition: 'pin-end',
41
+ keyBinding: {
42
+ macos: 'shift+meta+k',
43
+ windows: 'shift+ctrl+k',
44
+ },
45
+ },
46
+ },
47
+ ],
48
+ }),
49
+ createExtension({
50
+ id: `${AUTOMATION_PLUGIN}/service-registry`,
51
+ resolver: ({ id }) => {
52
+ if (!id.endsWith('~service-registry')) {
53
+ return;
54
+ }
55
+
56
+ const type = 'orphan-settings-for-subject';
57
+ const icon = 'ph--plugs--regular';
58
+
59
+ const client = context.requestCapability(ClientCapabilities.Client);
60
+ const [subjectId] = id.split('~');
61
+ const { spaceId } = parseId(subjectId);
62
+ const spaces = toSignal(
63
+ (onChange) => client.spaces.subscribe(() => onChange()).unsubscribe,
64
+ () => client.spaces.get(),
65
+ );
66
+ const space = spaces?.find((space) => space.id === spaceId && space.state.get() === SpaceState.SPACE_READY);
67
+ return {
68
+ id,
69
+ type,
70
+ data: null,
71
+ properties: {
72
+ icon,
73
+ label: ['service registry label', { ns: AUTOMATION_PLUGIN }],
74
+ object: null,
75
+ space,
76
+ },
77
+ };
78
+ },
79
+ }),
80
+ createExtension({
81
+ id: `${AUTOMATION_PLUGIN}/automation-for-subject`,
82
+ resolver: ({ id }) => {
83
+ if (!id.endsWith('~automation')) {
84
+ return;
85
+ }
86
+
87
+ const type = 'orphan-settings-for-subject';
88
+ const icon = 'ph--magic-wand--regular';
89
+
90
+ const client = context.requestCapability(ClientCapabilities.Client);
91
+ const [subjectId] = id.split('~');
92
+ const { spaceId, objectId } = parseId(subjectId);
93
+ const spaces = toSignal(
94
+ (onChange) => client.spaces.subscribe(() => onChange()).unsubscribe,
95
+ () => client.spaces.get(),
96
+ );
97
+ const space = spaces?.find((space) => space.id === spaceId && space.state.get() === SpaceState.SPACE_READY);
98
+ if (!objectId) {
99
+ // TODO(burdon): Ref SPACE_PLUGIN ns.
100
+ const label = space
101
+ ? space.properties.name || ['unnamed space label', { ns: AUTOMATION_PLUGIN }]
102
+ : ['unnamed object settings label', { ns: AUTOMATION_PLUGIN }];
103
+
104
+ // TODO(wittjosiah): Support comments for arbitrary subjects.
105
+ // This is to ensure that the comments panel is not stuck on an old object.
106
+ return {
107
+ id,
108
+ type,
109
+ data: null,
110
+ properties: {
111
+ icon,
112
+ label,
113
+ showResolvedThreads: false,
114
+ object: null,
115
+ space,
116
+ },
117
+ };
118
+ }
119
+
120
+ const [object] = memoizeQuery(space, { id: objectId });
121
+ if (!object || !subjectId) {
122
+ return;
123
+ }
124
+
125
+ const meta = resolve(getTypename(object) ?? '');
126
+ const label = meta.label?.(object) ||
127
+ object.name ||
128
+ meta.placeholder || ['unnamed object settings label', { ns: AUTOMATION_PLUGIN }];
129
+
130
+ return {
131
+ id,
132
+ type,
133
+ data: null,
134
+ properties: {
135
+ icon,
136
+ label,
137
+ object,
138
+ },
139
+ };
140
+ },
141
+ }),
142
+ createExtension({
143
+ id: `${AUTOMATION_PLUGIN}/assistant-for-subject`,
144
+ resolver: ({ id }) => {
145
+ if (!id.endsWith('~assistant')) {
146
+ return;
147
+ }
148
+
149
+ const client = context.requestCapability(ClientCapabilities.Client);
150
+ const [subjectId] = id.split('~');
151
+ const { spaceId, objectId } = parseId(subjectId);
152
+ const spaces = toSignal(
153
+ (onChange) => client.spaces.subscribe(() => onChange()).unsubscribe,
154
+ () => client.spaces.get(),
155
+ );
156
+ const space = spaces?.find((space) => space.id === spaceId && space.state.get() === SpaceState.SPACE_READY);
157
+ if (!objectId) {
158
+ // TODO(wittjosiah): Support assistant for arbitrary subjects.
159
+ // This is to ensure that the assistant panel is not stuck on an old object.
160
+ return {
161
+ id,
162
+ type: 'orphan-automation-for-subject',
163
+ data: null,
164
+ properties: {
165
+ icon: 'ph--atom--regular',
166
+ label: ['assistant panel label', { ns: AUTOMATION_PLUGIN }],
167
+ object: null,
168
+ space,
169
+ },
170
+ };
171
+ }
172
+
173
+ const [object] = memoizeQuery(space, { id: objectId });
174
+ return {
175
+ id,
176
+ type: 'orphan-automation-for-subject',
177
+ data: null,
178
+ properties: {
179
+ icon: 'ph--atom--regular',
180
+ label: ['assistant panel label', { ns: AUTOMATION_PLUGIN }],
181
+ object,
182
+ },
183
+ };
184
+ },
185
+ }),
186
+ ]);
187
+ };
@@ -0,0 +1,12 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { defineCapability } from '@dxos/app-framework';
6
+ import { type AIServiceClientImpl } from '@dxos/assistant';
7
+
8
+ import { AUTOMATION_PLUGIN } from '../meta';
9
+
10
+ export namespace AutomationCapabilities {
11
+ export const AiClient = defineCapability<AIServiceClientImpl>(`${AUTOMATION_PLUGIN}/capability/ai-client`);
12
+ }
@@ -0,0 +1,12 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { lazy } from '@dxos/app-framework';
6
+
7
+ export const AiClient = lazy(() => import('./ai-client'));
8
+ export const AppGraphBuilder = lazy(() => import('./app-graph-builder'));
9
+ export const IntentResolver = lazy(() => import('./intent-resolver'));
10
+ export const ReactSurface = lazy(() => import('./react-surface'));
11
+
12
+ export * from './capabilities';
@@ -0,0 +1,27 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Capabilities, contributes, createResolver } from '@dxos/app-framework';
6
+ import { ObjectId } from '@dxos/echo-schema';
7
+ import { create, makeRef } from '@dxos/live-object';
8
+
9
+ import { AutomationAction, AIChatType } from '../types';
10
+
11
+ export default () =>
12
+ contributes(
13
+ Capabilities.IntentResolver,
14
+ createResolver({
15
+ intent: AutomationAction.Create,
16
+ resolve: ({ name }) => ({
17
+ data: {
18
+ object: create(AIChatType, {
19
+ name,
20
+ // TODO(burdon): ???
21
+ // new DXN(DXN.kind.QUEUE, [QueueSubspaceTags.DATA, SpaceId.random(), ObjectId.random()]).toString(),
22
+ queue: makeRef({ id: ObjectId.random() }),
23
+ }),
24
+ },
25
+ }),
26
+ }),
27
+ );
@@ -0,0 +1,42 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { Capabilities, contributes, createSurface } from '@dxos/app-framework';
8
+ import { getSpace, isEchoObject, isSpace, type ReactiveEchoObject } from '@dxos/react-client/echo';
9
+
10
+ import { AmbientChatDialog, AutomationPanel, ChatContainer, ServiceRegistry } from '../components';
11
+ import { AUTOMATION_PLUGIN, AMBIENT_CHAT_DIALOG } from '../meta';
12
+ import { AIChatType } from '../types';
13
+
14
+ export default () =>
15
+ contributes(Capabilities.ReactSurface, [
16
+ createSurface({
17
+ id: `${AUTOMATION_PLUGIN}/ai-chat`,
18
+ role: 'article',
19
+ filter: (data): data is { subject: AIChatType } => data.subject instanceof AIChatType,
20
+ component: ({ data, role }) => <ChatContainer role={role} chat={data.subject} />,
21
+ }),
22
+ createSurface({
23
+ id: `${AUTOMATION_PLUGIN}/service-registry`,
24
+ role: 'complementary--service-registry',
25
+ component: ({ data }) => (
26
+ <ServiceRegistry space={isSpace(data.subject) ? data.subject : getSpace(data.subject)!} />
27
+ ),
28
+ }),
29
+ createSurface({
30
+ id: AMBIENT_CHAT_DIALOG,
31
+ role: 'dialog',
32
+ filter: (data): data is any => data.component === AMBIENT_CHAT_DIALOG,
33
+ component: () => <AmbientChatDialog />,
34
+ }),
35
+ createSurface({
36
+ id: `${AUTOMATION_PLUGIN}/automation`,
37
+ role: 'complementary--automation',
38
+ filter: (data): data is { subject: ReactiveEchoObject<any> } =>
39
+ isEchoObject(data.subject) && !!getSpace(data.subject),
40
+ component: ({ data }) => <AutomationPanel space={getSpace(data.subject)!} object={data.subject} />,
41
+ }),
42
+ ]);
@@ -0,0 +1,44 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import '@dxos-theme';
6
+
7
+ import { type StoryObj, type Meta } from '@storybook/react';
8
+ import React, { useState } from 'react';
9
+
10
+ import { Dialog, Toolbar } from '@dxos/react-ui';
11
+ import { withTheme, withLayout } from '@dxos/storybook-utils';
12
+
13
+ import { AmbientChatDialog } from './AmbientChatDialog';
14
+ import translations from '../../translations';
15
+
16
+ const meta: Meta<typeof AmbientChatDialog> = {
17
+ title: 'plugins/plugin-automation/AmbientChatDialog',
18
+ component: AmbientChatDialog,
19
+ render: () => {
20
+ const [open, setOpen] = useState(true);
21
+ return (
22
+ <>
23
+ <div>
24
+ <Toolbar.Root>
25
+ <Toolbar.Button onClick={() => setOpen(true)}>Open</Toolbar.Button>
26
+ </Toolbar.Root>
27
+ </div>
28
+ <Dialog.Root open={open} onOpenChange={setOpen}>
29
+ <AmbientChatDialog />
30
+ </Dialog.Root>
31
+ </>
32
+ );
33
+ },
34
+ decorators: [withTheme, withLayout({ fullscreen: true, tooltips: true })],
35
+ parameters: {
36
+ translations,
37
+ },
38
+ };
39
+
40
+ export default meta;
41
+
42
+ type Story = StoryObj<typeof AmbientChatDialog>;
43
+
44
+ export const Default: Story = {};
@@ -0,0 +1,63 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { type PropsWithChildren, useState } from 'react';
6
+
7
+ import { Dialog, Icon, IconButton, useTranslation } from '@dxos/react-ui';
8
+ import { resizeAttributes, ResizeHandle, type Size, sizeStyle } from '@dxos/react-ui-dnd';
9
+
10
+ import { AUTOMATION_PLUGIN } from '../../meta';
11
+ import { Prompt } from '../Prompt';
12
+
13
+ const preventDefault = (event: Event) => event.preventDefault();
14
+
15
+ export const AmbientChatDialog = ({ children }: PropsWithChildren) => {
16
+ const { t } = useTranslation(AUTOMATION_PLUGIN);
17
+ const [size, setSize] = useState<Size>('min-content');
18
+ const [iter, setIter] = useState(0);
19
+ return (
20
+ <div role='none' className='dx-dialog__overlay bg-transparent pointer-events-none' data-block-align='end'>
21
+ <Dialog.Content
22
+ onInteractOutside={preventDefault}
23
+ classNames='pointer-events-auto relative overflow-hidden is-[500px] max-is-none'
24
+ inOverlayLayout
25
+ {...resizeAttributes}
26
+ style={{
27
+ ...sizeStyle(size, 'vertical'),
28
+ maxBlockSize: 'calc(100dvh - env(safe-area-inset-bottom) - env(safe-area-inset-top) - 8rem)',
29
+ }}
30
+ >
31
+ <ResizeHandle
32
+ key={iter}
33
+ side='block-start'
34
+ defaultSize='min-content'
35
+ minSize={5}
36
+ fallbackSize={5}
37
+ iconPosition='center'
38
+ onSizeChange={setSize}
39
+ />
40
+
41
+ <div className='flex w-full items-center'>
42
+ <Dialog.Title classNames='sr-only'>{t('ambient chat dialog title')}</Dialog.Title>
43
+ <Dialog.Close>
44
+ <Icon icon='ph--x--regular' size={4} />
45
+ </Dialog.Close>
46
+ <div className='grow' />
47
+ <IconButton
48
+ variant='ghost'
49
+ icon='ph--caret-down--regular'
50
+ iconOnly
51
+ label='Shrink'
52
+ onClick={() => {
53
+ setIter((iter) => iter + 1);
54
+ setSize('min-content');
55
+ }}
56
+ />
57
+ </div>
58
+
59
+ <Prompt autoFocus lineWrapping />
60
+ </Dialog.Content>
61
+ </div>
62
+ );
63
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './AmbientChatDialog';
@@ -7,8 +7,7 @@ import '@dxos-theme';
7
7
  import { type Meta } from '@storybook/react';
8
8
  import React from 'react';
9
9
 
10
- import { FunctionTrigger } from '@dxos/functions';
11
- import { FunctionType } from '@dxos/plugin-script/types';
10
+ import { FunctionType, FunctionTrigger } from '@dxos/functions';
12
11
  import { create, useSpaces } from '@dxos/react-client/echo';
13
12
  import { withClientProvider } from '@dxos/react-client/testing';
14
13
  import { withLayout, withTheme } from '@dxos/storybook-utils';
@@ -5,8 +5,14 @@
5
5
  import React, { useState } from 'react';
6
6
 
7
7
  import { S } from '@dxos/echo-schema';
8
- import { FunctionTriggerSchema, FunctionTrigger, type FunctionTriggerType } from '@dxos/functions';
9
- import { FunctionType, ScriptType } from '@dxos/plugin-script';
8
+ import {
9
+ FunctionType,
10
+ FunctionTrigger,
11
+ FunctionTriggerSchema,
12
+ TriggerKind,
13
+ type FunctionTriggerType,
14
+ ScriptType,
15
+ } from '@dxos/functions';
10
16
  import { type Client, useClient } from '@dxos/react-client';
11
17
  import { create, Filter, useQuery, type Space, type ReactiveObject, getSpace } from '@dxos/react-client/echo';
12
18
  import { IconButton, Input, useTranslation, Button } from '@dxos/react-ui';
@@ -24,7 +30,7 @@ export type AutomationPanelProps = {
24
30
  };
25
31
 
26
32
  // TODO(burdon): Factor out common layout with ViewEditor.
27
- export const AutomationPanel = ({ space }: AutomationPanelProps) => {
33
+ export const AutomationPanel = ({ space, object }: AutomationPanelProps) => {
28
34
  const { t } = useTranslation(AUTOMATION_PLUGIN);
29
35
  const client = useClient();
30
36
  const triggers = useQuery(space, Filter.schema(FunctionTrigger));
@@ -41,7 +47,7 @@ export const AutomationPanel = ({ space }: AutomationPanelProps) => {
41
47
  };
42
48
 
43
49
  const handleAdd = () => {
44
- setTrigger(create(FunctionTriggerSchema, {}));
50
+ setTrigger(create(FunctionTriggerSchema, { meta: {} }));
45
51
  setSelected(undefined);
46
52
  };
47
53
 
@@ -71,43 +77,46 @@ export const AutomationPanel = ({ space }: AutomationPanelProps) => {
71
77
  <List.Root<FunctionTrigger> items={triggers} isItem={S.is(FunctionTrigger)} getId={(field) => field.id}>
72
78
  {({ items: triggers }) => (
73
79
  <div role='list' className='flex flex-col w-full'>
74
- {triggers?.map((trigger) => (
75
- <List.Item<FunctionTrigger>
76
- key={trigger.id}
77
- item={trigger}
78
- classNames={mx(grid, ghostHover, 'items-center')}
79
- >
80
- <Input.Root>
81
- <Input.Switch checked={trigger.enabled} onCheckedChange={(checked) => (trigger.enabled = checked)} />
82
- </Input.Root>
83
-
84
- <div className={'flex'}>
85
- <List.ItemTitle classNames='px-2 cursor-pointer w-0 shrink' onClick={() => handleSelect(trigger)}>
86
- {getFunctionName(scripts, functions, trigger)}
87
- </List.ItemTitle>
88
-
89
- {/* TODO: a better way to expose URL copy action */}
90
- <Button onClick={() => navigator.clipboard.writeText(getWebhookUrl(client, trigger))}>
91
- Copy URL
92
- </Button>
93
- </div>
94
-
95
- <List.ItemDeleteButton onClick={() => handleDelete(trigger)} />
96
- </List.Item>
97
- ))}
80
+ {triggers?.map((trigger) => {
81
+ const copyAction = getCopyAction(client, trigger);
82
+ return (
83
+ <List.Item<FunctionTrigger>
84
+ key={trigger.id}
85
+ item={trigger}
86
+ classNames={mx(grid, ghostHover, 'items-center', 'px-2')}
87
+ >
88
+ <Input.Root>
89
+ <Input.Switch
90
+ checked={trigger.enabled}
91
+ onCheckedChange={(checked) => (trigger.enabled = checked)}
92
+ />
93
+ </Input.Root>
94
+
95
+ <div className={'flex'}>
96
+ <List.ItemTitle
97
+ classNames='px-1 cursor-pointer w-0 shrink truncate'
98
+ onClick={() => handleSelect(trigger)}
99
+ >
100
+ {getFunctionName(scripts, functions, trigger) ?? '∅'}
101
+ </List.ItemTitle>
102
+
103
+ {/* TODO: a better way to expose copy action */}
104
+ {copyAction && (
105
+ <Button onClick={() => navigator.clipboard.writeText(copyAction.contentProvider())}>
106
+ {t(copyAction.translationKey)}
107
+ </Button>
108
+ )}
109
+ </div>
110
+
111
+ <List.ItemDeleteButton onClick={() => handleDelete(trigger)} />
112
+ </List.Item>
113
+ );
114
+ })}
98
115
  </div>
99
116
  )}
100
117
  </List.Root>
101
118
 
102
- {trigger && (
103
- <TriggerEditor
104
- space={space}
105
- storedTrigger={selected}
106
- trigger={trigger}
107
- onSave={handleSave}
108
- onCancel={handleCancel}
109
- />
110
- )}
119
+ {trigger && <TriggerEditor space={space} trigger={trigger} onSave={handleSave} onCancel={handleCancel} />}
111
120
 
112
121
  {!trigger && (
113
122
  <div className='flex p-2 justify-center'>
@@ -118,6 +127,18 @@ export const AutomationPanel = ({ space }: AutomationPanelProps) => {
118
127
  );
119
128
  };
120
129
 
130
+ const getCopyAction = (client: Client, trigger: FunctionTrigger | undefined) => {
131
+ if (trigger?.spec?.type === TriggerKind.Email) {
132
+ return { translationKey: 'trigger copy email', contentProvider: () => `${getSpace(trigger)!.id}@dxos.network` };
133
+ }
134
+
135
+ if (trigger?.spec?.type === TriggerKind.Webhook) {
136
+ return { translationKey: 'trigger copy url', contentProvider: () => getWebhookUrl(client, trigger) };
137
+ }
138
+
139
+ return undefined;
140
+ };
141
+
121
142
  const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
122
143
  const spaceId = getSpace(trigger)!.id;
123
144
  const edgeUrl = new URL(client.config.values.runtime!.services!.edge!.url!);
@@ -127,9 +148,10 @@ const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
127
148
  };
128
149
 
129
150
  const getFunctionName = (scripts: ScriptType[], functions: FunctionType[], trigger: FunctionTriggerType) => {
151
+ const shortId = trigger.function && `${trigger.function?.slice(0, 16)}…`;
130
152
  const functionObject = functions.find((fn) => fn.name === trigger.function);
131
153
  if (!functionObject) {
132
- return trigger.function;
154
+ return shortId;
133
155
  }
134
- return scripts.find((s) => functionObject.source?.id === s.id)?.name ?? functionObject.name;
156
+ return scripts.find((s) => functionObject.source?.target?.id === s.id)?.name ?? shortId;
135
157
  };
@@ -0,0 +1,52 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import '@dxos-theme';
6
+
7
+ import { type StoryObj, type Meta } from '@storybook/react';
8
+ import React, { useState } from 'react';
9
+
10
+ import { faker } from '@dxos/random';
11
+ import { Button } from '@dxos/react-ui';
12
+ import { withLayout, withTheme, withSignals } from '@dxos/storybook-utils';
13
+
14
+ import { StatusLine } from './StatusLine';
15
+
16
+ const meta: Meta<typeof StatusLine> = {
17
+ title: 'plugins/plugin-automation/StatusLine',
18
+ component: StatusLine,
19
+ decorators: [withSignals, withTheme, withLayout()],
20
+ parameters: {
21
+ layout: 'centered',
22
+ },
23
+ };
24
+
25
+ export default meta;
26
+
27
+ type Story = StoryObj<typeof StatusLine>;
28
+
29
+ export const Default: Story = {
30
+ args: {
31
+ classNames: 'w-96 px-2',
32
+ lines: Array.from({ length: 5 }, (_, i) => `${i}. ${faker.lorem.paragraph()}`),
33
+ autoAdvance: true,
34
+ },
35
+ };
36
+
37
+ export const Demo: Story = {
38
+ render: () => {
39
+ const [lines, setLines] = useState<string[]>([]);
40
+
41
+ return (
42
+ <div className='flex flex-col w-96 gap-4'>
43
+ <StatusLine lines={lines} autoAdvance advance={500} />
44
+ <div>
45
+ <Button onClick={() => setLines((lines) => [...lines, `${lines.length + 1}. ${faker.lorem.paragraph()}`])}>
46
+ Add
47
+ </Button>
48
+ </div>
49
+ </div>
50
+ );
51
+ },
52
+ };
@@ -0,0 +1,76 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { useEffect, useRef, useState } from 'react';
6
+
7
+ import { type ThemedClassName } from '@dxos/react-ui';
8
+ import { mx } from '@dxos/react-ui-theme';
9
+
10
+ const emptyLines: string[] = [];
11
+
12
+ export type StatusLineProps = ThemedClassName<{
13
+ line?: number;
14
+ lines?: string[];
15
+ transition?: number;
16
+ advance?: number;
17
+ autoAdvance?: boolean;
18
+ }>;
19
+
20
+ export const StatusLine = ({
21
+ classNames,
22
+ line = -1,
23
+ lines = emptyLines,
24
+ transition = 300,
25
+ advance = 1_000,
26
+ autoAdvance,
27
+ }: StatusLineProps) => {
28
+ const containerRef = useRef<HTMLDivElement>(null);
29
+ const [currentLine, setCurrentLine] = useState(line);
30
+ useEffect(() => {
31
+ setCurrentLine(line);
32
+ }, [line]);
33
+
34
+ useEffect(() => {
35
+ if (!autoAdvance) {
36
+ return;
37
+ }
38
+
39
+ const next = () => {
40
+ setCurrentLine((prev) => {
41
+ if (prev >= lines.length - 1) {
42
+ clearInterval(interval);
43
+ return prev;
44
+ }
45
+
46
+ return prev + 1;
47
+ });
48
+ };
49
+
50
+ next();
51
+ const interval = setInterval(next, advance);
52
+
53
+ return () => clearInterval(interval);
54
+ }, [lines.length, autoAdvance, advance]);
55
+
56
+ useEffect(() => {
57
+ if (containerRef.current) {
58
+ containerRef.current.style.transition = `transform ${transition}ms ease-in-out`;
59
+ containerRef.current.style.transform = `translateY(-${currentLine * 24}px)`;
60
+ }
61
+ }, [currentLine]);
62
+
63
+ return (
64
+ <div className={mx('relative h-[24px] overflow-hidden', classNames)}>
65
+ <div ref={containerRef} className='h-[24px]'>
66
+ <div className='flex flex-col'>
67
+ {lines.map((line, i) => (
68
+ <div key={i} className={mx('flex h-[24px] items-center')}>
69
+ <span className='truncate'>{line}</span>
70
+ </div>
71
+ ))}
72
+ </div>
73
+ </div>
74
+ </div>
75
+ );
76
+ };