@assistant-ui/vue 0.0.0-stage → 0.0.1

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 (231) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +90 -2
  3. package/dist/AuiIf.d.ts +27 -0
  4. package/dist/AuiIf.d.ts.map +1 -0
  5. package/dist/AuiIf.js +30 -0
  6. package/dist/AuiIf.js.map +1 -0
  7. package/dist/AuiProvider.d.ts +41 -0
  8. package/dist/AuiProvider.d.ts.map +1 -0
  9. package/dist/AuiProvider.js +61 -0
  10. package/dist/AuiProvider.js.map +1 -0
  11. package/dist/context.d.ts +10 -0
  12. package/dist/context.d.ts.map +1 -0
  13. package/dist/context.js +17 -0
  14. package/dist/context.js.map +1 -0
  15. package/dist/index.d.ts +34 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +35 -0
  18. package/dist/primitives/AttachmentByIndexProvider.d.ts +30 -0
  19. package/dist/primitives/AttachmentByIndexProvider.d.ts.map +1 -0
  20. package/dist/primitives/AttachmentByIndexProvider.js +63 -0
  21. package/dist/primitives/AttachmentByIndexProvider.js.map +1 -0
  22. package/dist/primitives/ChainOfThoughtPrimitiveAccordionTrigger.d.ts +11 -0
  23. package/dist/primitives/ChainOfThoughtPrimitiveAccordionTrigger.d.ts.map +1 -0
  24. package/dist/primitives/ChainOfThoughtPrimitiveAccordionTrigger.js +31 -0
  25. package/dist/primitives/ChainOfThoughtPrimitiveAccordionTrigger.js.map +1 -0
  26. package/dist/primitives/ChainOfThoughtPrimitiveParts.d.ts +19 -0
  27. package/dist/primitives/ChainOfThoughtPrimitiveParts.d.ts.map +1 -0
  28. package/dist/primitives/ChainOfThoughtPrimitiveParts.js +85 -0
  29. package/dist/primitives/ChainOfThoughtPrimitiveParts.js.map +1 -0
  30. package/dist/primitives/ComposerPrimitiveCancel.d.ts +12 -0
  31. package/dist/primitives/ComposerPrimitiveCancel.d.ts.map +1 -0
  32. package/dist/primitives/ComposerPrimitiveCancel.js +33 -0
  33. package/dist/primitives/ComposerPrimitiveCancel.js.map +1 -0
  34. package/dist/primitives/ComposerPrimitiveInput.d.ts +26 -0
  35. package/dist/primitives/ComposerPrimitiveInput.d.ts.map +1 -0
  36. package/dist/primitives/ComposerPrimitiveInput.js +57 -0
  37. package/dist/primitives/ComposerPrimitiveInput.js.map +1 -0
  38. package/dist/primitives/ComposerPrimitiveSend.d.ts +13 -0
  39. package/dist/primitives/ComposerPrimitiveSend.d.ts.map +1 -0
  40. package/dist/primitives/ComposerPrimitiveSend.js +31 -0
  41. package/dist/primitives/ComposerPrimitiveSend.js.map +1 -0
  42. package/dist/primitives/MessageByIdProvider.d.ts +23 -0
  43. package/dist/primitives/MessageByIdProvider.d.ts.map +1 -0
  44. package/dist/primitives/MessageByIdProvider.js +70 -0
  45. package/dist/primitives/MessageByIdProvider.js.map +1 -0
  46. package/dist/primitives/MessagePrimitiveParts.d.ts +50 -0
  47. package/dist/primitives/MessagePrimitiveParts.d.ts.map +1 -0
  48. package/dist/primitives/MessagePrimitiveParts.js +93 -0
  49. package/dist/primitives/MessagePrimitiveParts.js.map +1 -0
  50. package/dist/primitives/PartByIndexProvider.d.ts +21 -0
  51. package/dist/primitives/PartByIndexProvider.d.ts.map +1 -0
  52. package/dist/primitives/PartByIndexProvider.js +50 -0
  53. package/dist/primitives/PartByIndexProvider.js.map +1 -0
  54. package/dist/primitives/ThreadListItemPrimitiveRoot.d.ts +7 -0
  55. package/dist/primitives/ThreadListItemPrimitiveRoot.d.ts.map +1 -0
  56. package/dist/primitives/ThreadListItemPrimitiveRoot.js +36 -0
  57. package/dist/primitives/ThreadListItemPrimitiveRoot.js.map +1 -0
  58. package/dist/primitives/ThreadListItemPrimitiveTrigger.d.ts +14 -0
  59. package/dist/primitives/ThreadListItemPrimitiveTrigger.d.ts.map +1 -0
  60. package/dist/primitives/ThreadListItemPrimitiveTrigger.js +58 -0
  61. package/dist/primitives/ThreadListItemPrimitiveTrigger.js.map +1 -0
  62. package/dist/primitives/ThreadListPrimitiveRoot.d.ts +7 -0
  63. package/dist/primitives/ThreadListPrimitiveRoot.d.ts.map +1 -0
  64. package/dist/primitives/ThreadListPrimitiveRoot.js +16 -0
  65. package/dist/primitives/ThreadListPrimitiveRoot.js.map +1 -0
  66. package/dist/primitives/ThreadPrimitiveMessages.d.ts +23 -0
  67. package/dist/primitives/ThreadPrimitiveMessages.d.ts.map +1 -0
  68. package/dist/primitives/ThreadPrimitiveMessages.js +37 -0
  69. package/dist/primitives/ThreadPrimitiveMessages.js.map +1 -0
  70. package/dist/primitives/ThreadPrimitiveScrollToBottom.d.ts +25 -0
  71. package/dist/primitives/ThreadPrimitiveScrollToBottom.d.ts.map +1 -0
  72. package/dist/primitives/ThreadPrimitiveScrollToBottom.js +43 -0
  73. package/dist/primitives/ThreadPrimitiveScrollToBottom.js.map +1 -0
  74. package/dist/primitives/ThreadPrimitiveViewport.d.ts +57 -0
  75. package/dist/primitives/ThreadPrimitiveViewport.d.ts.map +1 -0
  76. package/dist/primitives/ThreadPrimitiveViewport.js +174 -0
  77. package/dist/primitives/ThreadPrimitiveViewport.js.map +1 -0
  78. package/dist/primitives/ThreadPrimitiveViewportFooter.d.ts +13 -0
  79. package/dist/primitives/ThreadPrimitiveViewportFooter.d.ts.map +1 -0
  80. package/dist/primitives/ThreadPrimitiveViewportFooter.js +46 -0
  81. package/dist/primitives/ThreadPrimitiveViewportFooter.js.map +1 -0
  82. package/dist/primitives/actionBar.d.ts +41 -0
  83. package/dist/primitives/actionBar.d.ts.map +1 -0
  84. package/dist/primitives/actionBar.js +110 -0
  85. package/dist/primitives/actionBar.js.map +1 -0
  86. package/dist/primitives/attachment.d.ts +27 -0
  87. package/dist/primitives/attachment.d.ts.map +1 -0
  88. package/dist/primitives/attachment.js +63 -0
  89. package/dist/primitives/attachment.js.map +1 -0
  90. package/dist/primitives/attrDisabled.d.ts +2 -0
  91. package/dist/primitives/attrDisabled.d.ts.map +1 -0
  92. package/dist/primitives/attrDisabled.js +6 -0
  93. package/dist/primitives/attrDisabled.js.map +1 -0
  94. package/dist/primitives/branchPicker.d.ts +18 -0
  95. package/dist/primitives/branchPicker.d.ts.map +1 -0
  96. package/dist/primitives/branchPicker.js +48 -0
  97. package/dist/primitives/branchPicker.js.map +1 -0
  98. package/dist/primitives/composerAttachments.d.ts +57 -0
  99. package/dist/primitives/composerAttachments.d.ts.map +1 -0
  100. package/dist/primitives/composerAttachments.js +132 -0
  101. package/dist/primitives/composerAttachments.js.map +1 -0
  102. package/dist/primitives/error.d.ts +15 -0
  103. package/dist/primitives/error.d.ts.map +1 -0
  104. package/dist/primitives/error.js +29 -0
  105. package/dist/primitives/error.js.map +1 -0
  106. package/dist/primitives/lastValidCache.d.ts +6 -0
  107. package/dist/primitives/lastValidCache.d.ts.map +1 -0
  108. package/dist/primitives/lastValidCache.js +9 -0
  109. package/dist/primitives/lastValidCache.js.map +1 -0
  110. package/dist/primitives/message.d.ts +12 -0
  111. package/dist/primitives/message.d.ts.map +1 -0
  112. package/dist/primitives/message.js +44 -0
  113. package/dist/primitives/message.js.map +1 -0
  114. package/dist/primitives/messageAttachments.d.ts +12 -0
  115. package/dist/primitives/messageAttachments.d.ts.map +1 -0
  116. package/dist/primitives/messageAttachments.js +27 -0
  117. package/dist/primitives/messageAttachments.js.map +1 -0
  118. package/dist/primitives/stableKeys.d.ts +5 -0
  119. package/dist/primitives/stableKeys.d.ts.map +1 -0
  120. package/dist/primitives/stableKeys.js +14 -0
  121. package/dist/primitives/stableKeys.js.map +1 -0
  122. package/dist/primitives/suggestions.d.ts +66 -0
  123. package/dist/primitives/suggestions.d.ts.map +1 -0
  124. package/dist/primitives/suggestions.js +137 -0
  125. package/dist/primitives/suggestions.js.map +1 -0
  126. package/dist/primitives/thread.d.ts +11 -0
  127. package/dist/primitives/thread.d.ts.map +1 -0
  128. package/dist/primitives/thread.js +37 -0
  129. package/dist/primitives/thread.js.map +1 -0
  130. package/dist/primitives/threadList.d.ts +82 -0
  131. package/dist/primitives/threadList.d.ts.map +1 -0
  132. package/dist/primitives/threadList.js +128 -0
  133. package/dist/primitives/threadList.js.map +1 -0
  134. package/dist/primitives/threadListFocusGroup.d.ts +14 -0
  135. package/dist/primitives/threadListFocusGroup.d.ts.map +1 -0
  136. package/dist/primitives/threadListFocusGroup.js +32 -0
  137. package/dist/primitives/threadListFocusGroup.js.map +1 -0
  138. package/dist/primitives/threadListStructural.d.ts +29 -0
  139. package/dist/primitives/threadListStructural.d.ts.map +1 -0
  140. package/dist/primitives/threadListStructural.js +55 -0
  141. package/dist/primitives/threadListStructural.js.map +1 -0
  142. package/dist/primitives/useComposerSendState.d.ts +6 -0
  143. package/dist/primitives/useComposerSendState.d.ts.map +1 -0
  144. package/dist/primitives/useComposerSendState.js +15 -0
  145. package/dist/primitives/useComposerSendState.js.map +1 -0
  146. package/dist/primitives/viewportContext.d.ts +12 -0
  147. package/dist/primitives/viewportContext.d.ts.map +1 -0
  148. package/dist/primitives/viewportContext.js +6 -0
  149. package/dist/primitives/viewportContext.js.map +1 -0
  150. package/dist/useAui.d.ts +15 -0
  151. package/dist/useAui.d.ts.map +1 -0
  152. package/dist/useAui.js +21 -0
  153. package/dist/useAui.js.map +1 -0
  154. package/dist/useAuiEvent.d.ts +17 -0
  155. package/dist/useAuiEvent.d.ts.map +1 -0
  156. package/dist/useAuiEvent.js +52 -0
  157. package/dist/useAuiEvent.js.map +1 -0
  158. package/dist/useAuiState.d.ts +23 -0
  159. package/dist/useAuiState.d.ts.map +1 -0
  160. package/dist/useAuiState.js +42 -0
  161. package/dist/useAuiState.js.map +1 -0
  162. package/dist/useScrollLock.d.ts +3 -0
  163. package/dist/useScrollLock.d.ts.map +1 -0
  164. package/dist/useScrollLock.js +53 -0
  165. package/dist/useScrollLock.js.map +1 -0
  166. package/package.json +64 -3
  167. package/src/AuiIf.ts +34 -0
  168. package/src/AuiProvider.ts +90 -0
  169. package/src/__tests__/AuiIf.test.ts +54 -0
  170. package/src/__tests__/AuiProvider.test.ts +266 -0
  171. package/src/__tests__/byindex-shrink-race.test.ts +411 -0
  172. package/src/__tests__/dist-graph.test.ts +49 -0
  173. package/src/__tests__/fixtures.ts +51 -0
  174. package/src/__tests__/primitives-actionbar.test.ts +469 -0
  175. package/src/__tests__/primitives-attachment.test.ts +493 -0
  176. package/src/__tests__/primitives-content.test.ts +749 -0
  177. package/src/__tests__/primitives-keyed-items.test.ts +270 -0
  178. package/src/__tests__/primitives-keyed-messages.test.ts +264 -0
  179. package/src/__tests__/primitives-structural.test.ts +573 -0
  180. package/src/__tests__/primitives-suggestions.test.ts +374 -0
  181. package/src/__tests__/primitives-threadlist.test.ts +443 -0
  182. package/src/__tests__/primitives-tool-ui.test.ts +579 -0
  183. package/src/__tests__/primitives.test.ts +361 -0
  184. package/src/__tests__/useAuiEvent.test.ts +159 -0
  185. package/src/__tests__/useAuiState.test.ts +124 -0
  186. package/src/context.ts +31 -0
  187. package/src/index.ts +87 -0
  188. package/src/primitives/AttachmentByIndexProvider.ts +88 -0
  189. package/src/primitives/ChainOfThoughtPrimitiveAccordionTrigger.test.ts +105 -0
  190. package/src/primitives/ChainOfThoughtPrimitiveAccordionTrigger.ts +38 -0
  191. package/src/primitives/ChainOfThoughtPrimitiveParts.test.ts +254 -0
  192. package/src/primitives/ChainOfThoughtPrimitiveParts.ts +112 -0
  193. package/src/primitives/ComposerPrimitiveCancel.ts +41 -0
  194. package/src/primitives/ComposerPrimitiveInput.ts +64 -0
  195. package/src/primitives/ComposerPrimitiveSend.ts +39 -0
  196. package/src/primitives/MessageByIdProvider.ts +89 -0
  197. package/src/primitives/MessagePrimitiveParts.tool-interaction.test.ts +88 -0
  198. package/src/primitives/MessagePrimitiveParts.ts +166 -0
  199. package/src/primitives/PartByIndexProvider.ts +62 -0
  200. package/src/primitives/ThreadListItemPrimitiveRoot.ts +57 -0
  201. package/src/primitives/ThreadListItemPrimitiveTrigger.ts +74 -0
  202. package/src/primitives/ThreadListKeyboardNav.test.ts +298 -0
  203. package/src/primitives/ThreadListPrimitiveRoot.ts +18 -0
  204. package/src/primitives/ThreadPrimitiveMessages.ts +40 -0
  205. package/src/primitives/ThreadPrimitiveScrollToBottom.ts +65 -0
  206. package/src/primitives/ThreadPrimitiveViewport.ts +233 -0
  207. package/src/primitives/ThreadPrimitiveViewportFooter.test.ts +305 -0
  208. package/src/primitives/ThreadPrimitiveViewportFooter.ts +60 -0
  209. package/src/primitives/actionBar.ts +159 -0
  210. package/src/primitives/attachment.ts +76 -0
  211. package/src/primitives/attrDisabled.ts +6 -0
  212. package/src/primitives/branchPicker.ts +77 -0
  213. package/src/primitives/composerAttachments.ts +181 -0
  214. package/src/primitives/error.ts +36 -0
  215. package/src/primitives/lastValidCache.ts +12 -0
  216. package/src/primitives/message.ts +54 -0
  217. package/src/primitives/messageAttachments.ts +32 -0
  218. package/src/primitives/stableKeys.test.ts +19 -0
  219. package/src/primitives/stableKeys.ts +21 -0
  220. package/src/primitives/suggestions.ts +179 -0
  221. package/src/primitives/thread.ts +46 -0
  222. package/src/primitives/threadList.ts +163 -0
  223. package/src/primitives/threadListFocusGroup.ts +56 -0
  224. package/src/primitives/threadListStructural.ts +86 -0
  225. package/src/primitives/useComposerSendState.ts +13 -0
  226. package/src/primitives/viewportContext.ts +16 -0
  227. package/src/useAui.ts +18 -0
  228. package/src/useAuiEvent.ts +64 -0
  229. package/src/useAuiState.ts +55 -0
  230. package/src/useScrollLock.test.ts +207 -0
  231. package/src/useScrollLock.ts +79 -0
@@ -0,0 +1,23 @@
1
+ import { type ComputedRef } from "vue";
2
+ import { type AssistantState } from "@assistant-ui/store/client";
3
+ /**
4
+ * Subscribes to a slice of `AssistantState` and returns it as a computed ref.
5
+ *
6
+ * The `selector` runs on every store update; dependents re-run only when the
7
+ * selected slice changes by `Object.is`. Returning the entire state object is
8
+ * not supported and throws — select a specific field instead, or compose
9
+ * multiple `useAuiState` calls. Returning a fresh object or array literal
10
+ * re-triggers dependents on every update; select primitives or a memoized
11
+ * reference.
12
+ *
13
+ * Scopes that may be unavailable can be read via `s.optional.<scope>`, which
14
+ * resolves to `undefined` instead of throwing.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const isRunning = useAuiState((s) => s.thread.isRunning);
19
+ * // template: <button :disabled="isRunning">…</button>
20
+ * ```
21
+ */
22
+ export declare const useAuiState: <T>(selector: (state: AssistantState) => T) => ComputedRef<T>;
23
+ //# sourceMappingURL=useAuiState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAuiState.d.ts","sourceRoot":"","sources":["../src/useAuiState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAC7E,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,4BAA4B,CAAC;AAGpC;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,EAC3B,UAAU,CAAC,KAAK,EAAE,cAAc,KAAK,CAAC,KACrC,WAAW,CAAC,CAAC,CA0Bf,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { useAuiContext } from "./context.js";
2
+ import { computed, onScopeDispose, shallowRef } from "vue";
3
+ import { getProxiedAssistantState } from "@assistant-ui/store/client";
4
+ //#region src/useAuiState.ts
5
+ /**
6
+ * Subscribes to a slice of `AssistantState` and returns it as a computed ref.
7
+ *
8
+ * The `selector` runs on every store update; dependents re-run only when the
9
+ * selected slice changes by `Object.is`. Returning the entire state object is
10
+ * not supported and throws — select a specific field instead, or compose
11
+ * multiple `useAuiState` calls. Returning a fresh object or array literal
12
+ * re-triggers dependents on every update; select primitives or a memoized
13
+ * reference.
14
+ *
15
+ * Scopes that may be unavailable can be read via `s.optional.<scope>`, which
16
+ * resolves to `undefined` instead of throwing.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const isRunning = useAuiState((s) => s.thread.isRunning);
21
+ * // template: <button :disabled="isRunning">…</button>
22
+ * ```
23
+ */
24
+ const useAuiState = (selector) => {
25
+ const { source } = useAuiContext();
26
+ const version = shallowRef(0);
27
+ const unsubscribe = source.subscribe(() => {
28
+ version.value++;
29
+ });
30
+ onScopeDispose(unsubscribe);
31
+ return computed(() => {
32
+ version.value;
33
+ const state = getProxiedAssistantState(source.getClient());
34
+ const slice = selector(state);
35
+ if (typeof slice === "object" && slice !== null && (slice === state || slice === state.optional)) throw new Error("You tried to return the entire AssistantState. This is not supported due to technical limitations.");
36
+ return slice;
37
+ });
38
+ };
39
+ //#endregion
40
+ export { useAuiState };
41
+
42
+ //# sourceMappingURL=useAuiState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAuiState.js","names":[],"sources":["../src/useAuiState.ts"],"sourcesContent":["import { computed, onScopeDispose, shallowRef, type ComputedRef } from \"vue\";\nimport {\n getProxiedAssistantState,\n type AssistantState,\n} from \"@assistant-ui/store/client\";\nimport { useAuiContext } from \"./context\";\n\n/**\n * Subscribes to a slice of `AssistantState` and returns it as a computed ref.\n *\n * The `selector` runs on every store update; dependents re-run only when the\n * selected slice changes by `Object.is`. Returning the entire state object is\n * not supported and throws — select a specific field instead, or compose\n * multiple `useAuiState` calls. Returning a fresh object or array literal\n * re-triggers dependents on every update; select primitives or a memoized\n * reference.\n *\n * Scopes that may be unavailable can be read via `s.optional.<scope>`, which\n * resolves to `undefined` instead of throwing.\n *\n * @example\n * ```ts\n * const isRunning = useAuiState((s) => s.thread.isRunning);\n * // template: <button :disabled=\"isRunning\">…</button>\n * ```\n */\nexport const useAuiState = <T>(\n selector: (state: AssistantState) => T,\n): ComputedRef<T> => {\n const { source } = useAuiContext();\n\n const version = shallowRef(0);\n const unsubscribe = source.subscribe(() => {\n version.value++;\n });\n onScopeDispose(unsubscribe);\n\n return computed(() => {\n void version.value;\n const state = getProxiedAssistantState(source.getClient());\n const slice = selector(state);\n\n if (\n typeof slice === \"object\" &&\n slice !== null &&\n ((slice as unknown) === state || (slice as unknown) === state.optional)\n ) {\n throw new Error(\n \"You tried to return the entire AssistantState. This is not supported due to technical limitations.\",\n );\n }\n\n return slice;\n });\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAa,eACX,aACmB;CACnB,MAAM,EAAE,WAAW,cAAc;CAEjC,MAAM,UAAU,WAAW,CAAC;CAC5B,MAAM,cAAc,OAAO,gBAAgB;EACzC,QAAQ;CACV,CAAC;CACD,eAAe,WAAW;CAE1B,OAAO,eAAe;EACpB,QAAa;EACb,MAAM,QAAQ,yBAAyB,OAAO,UAAU,CAAC;EACzD,MAAM,QAAQ,SAAS,KAAK;EAE5B,IACE,OAAO,UAAU,YACjB,UAAU,SACR,UAAsB,SAAU,UAAsB,MAAM,WAE9D,MAAM,IAAI,MACR,oGACF;EAGF,OAAO;CACT,CAAC;AACH"}
@@ -0,0 +1,3 @@
1
+ import { type Ref } from "vue";
2
+ export declare const useScrollLock: (target: Ref<HTMLElement | null | undefined>, animationDuration: number) => () => void;
3
+ //# sourceMappingURL=useScrollLock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScrollLock.d.ts","sourceRoot":"","sources":["../src/useScrollLock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AAY/C,eAAO,MAAM,aAAa,GACxB,QAAQ,GAAG,CAAC,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC,EAC3C,mBAAmB,MAAM,eAgE1B,CAAC"}
@@ -0,0 +1,53 @@
1
+ import { onScopeDispose } from "vue";
2
+ //#region src/useScrollLock.ts
3
+ const findScrollableAncestor = (element) => {
4
+ let current = element;
5
+ while (current) {
6
+ const { overflowY } = getComputedStyle(current);
7
+ if (overflowY === "scroll" || overflowY === "auto") return current;
8
+ current = current.parentElement;
9
+ }
10
+ return null;
11
+ };
12
+ const useScrollLock = (target, animationDuration) => {
13
+ let cleanup = null;
14
+ onScopeDispose(() => cleanup?.());
15
+ return () => {
16
+ cleanup?.();
17
+ const scrollContainer = findScrollableAncestor(target.value);
18
+ if (!scrollContainer) {
19
+ cleanup = null;
20
+ return;
21
+ }
22
+ const scrollPosition = scrollContainer.scrollTop;
23
+ const scrollbarWidth = scrollContainer.style.scrollbarWidth;
24
+ const computed = getComputedStyle(scrollContainer);
25
+ const paddingSide = computed.direction === "rtl" ? "paddingLeft" : "paddingRight";
26
+ const previousPadding = scrollContainer.style[paddingSide];
27
+ const elementScrollbarSize = scrollContainer.offsetWidth - scrollContainer.clientWidth - parseFloat(computed.borderLeftWidth) - parseFloat(computed.borderRightWidth);
28
+ const ownerDocument = scrollContainer.ownerDocument;
29
+ const scrollbarSize = (scrollContainer === ownerDocument.documentElement || scrollContainer === ownerDocument.body) && elementScrollbarSize <= 0 ? (ownerDocument.defaultView?.innerWidth ?? 0) - ownerDocument.documentElement.clientWidth : elementScrollbarSize;
30
+ scrollContainer.style.scrollbarWidth = "none";
31
+ if (scrollbarSize > 0) scrollContainer.style[paddingSide] = `${parseFloat(computed[paddingSide]) + scrollbarSize}px`;
32
+ const restoreStyles = () => {
33
+ scrollContainer.style.scrollbarWidth = scrollbarWidth;
34
+ scrollContainer.style[paddingSide] = previousPadding;
35
+ };
36
+ const resetPosition = () => scrollContainer.scrollTop = scrollPosition;
37
+ scrollContainer.addEventListener("scroll", resetPosition);
38
+ const timeoutId = setTimeout(() => {
39
+ scrollContainer.removeEventListener("scroll", resetPosition);
40
+ restoreStyles();
41
+ cleanup = null;
42
+ }, animationDuration);
43
+ cleanup = () => {
44
+ clearTimeout(timeoutId);
45
+ scrollContainer.removeEventListener("scroll", resetPosition);
46
+ restoreStyles();
47
+ };
48
+ };
49
+ };
50
+ //#endregion
51
+ export { useScrollLock };
52
+
53
+ //# sourceMappingURL=useScrollLock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScrollLock.js","names":[],"sources":["../src/useScrollLock.ts"],"sourcesContent":["import { onScopeDispose, type Ref } from \"vue\";\n\nconst findScrollableAncestor = (element: HTMLElement | null | undefined) => {\n let current = element;\n while (current) {\n const { overflowY } = getComputedStyle(current);\n if (overflowY === \"scroll\" || overflowY === \"auto\") return current;\n current = current.parentElement;\n }\n return null;\n};\n\nexport const useScrollLock = (\n target: Ref<HTMLElement | null | undefined>,\n animationDuration: number,\n) => {\n let cleanup: (() => void) | null = null;\n\n onScopeDispose(() => cleanup?.());\n\n return () => {\n cleanup?.();\n\n const scrollContainer = findScrollableAncestor(target.value);\n if (!scrollContainer) {\n cleanup = null;\n return;\n }\n\n const scrollPosition = scrollContainer.scrollTop;\n const scrollbarWidth = scrollContainer.style.scrollbarWidth;\n const computed = getComputedStyle(scrollContainer);\n const paddingSide =\n computed.direction === \"rtl\" ? \"paddingLeft\" : \"paddingRight\";\n const previousPadding = scrollContainer.style[paddingSide];\n const elementScrollbarSize =\n scrollContainer.offsetWidth -\n scrollContainer.clientWidth -\n parseFloat(computed.borderLeftWidth) -\n parseFloat(computed.borderRightWidth);\n // Root offsetWidth excludes the viewport scrollbar, so use the viewport\n // width only when the element's own gutter is absent.\n const ownerDocument = scrollContainer.ownerDocument;\n const isRootScroller =\n scrollContainer === ownerDocument.documentElement ||\n scrollContainer === ownerDocument.body;\n const scrollbarSize =\n isRootScroller && elementScrollbarSize <= 0\n ? (ownerDocument.defaultView?.innerWidth ?? 0) -\n ownerDocument.documentElement.clientWidth\n : elementScrollbarSize;\n\n scrollContainer.style.scrollbarWidth = \"none\";\n if (scrollbarSize > 0) {\n scrollContainer.style[paddingSide] = `${\n parseFloat(computed[paddingSide]) + scrollbarSize\n }px`;\n }\n\n const restoreStyles = () => {\n scrollContainer.style.scrollbarWidth = scrollbarWidth;\n scrollContainer.style[paddingSide] = previousPadding;\n };\n const resetPosition = () => (scrollContainer.scrollTop = scrollPosition);\n scrollContainer.addEventListener(\"scroll\", resetPosition);\n\n const timeoutId = setTimeout(() => {\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n restoreStyles();\n cleanup = null;\n }, animationDuration);\n\n cleanup = () => {\n clearTimeout(timeoutId);\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n restoreStyles();\n };\n };\n};\n"],"mappings":";;AAEA,MAAM,0BAA0B,YAA4C;CAC1E,IAAI,UAAU;CACd,OAAO,SAAS;EACd,MAAM,EAAE,cAAc,iBAAiB,OAAO;EAC9C,IAAI,cAAc,YAAY,cAAc,QAAQ,OAAO;EAC3D,UAAU,QAAQ;CACpB;CACA,OAAO;AACT;AAEA,MAAa,iBACX,QACA,sBACG;CACH,IAAI,UAA+B;CAEnC,qBAAqB,UAAU,CAAC;CAEhC,aAAa;EACX,UAAU;EAEV,MAAM,kBAAkB,uBAAuB,OAAO,KAAK;EAC3D,IAAI,CAAC,iBAAiB;GACpB,UAAU;GACV;EACF;EAEA,MAAM,iBAAiB,gBAAgB;EACvC,MAAM,iBAAiB,gBAAgB,MAAM;EAC7C,MAAM,WAAW,iBAAiB,eAAe;EACjD,MAAM,cACJ,SAAS,cAAc,QAAQ,gBAAgB;EACjD,MAAM,kBAAkB,gBAAgB,MAAM;EAC9C,MAAM,uBACJ,gBAAgB,cAChB,gBAAgB,cAChB,WAAW,SAAS,eAAe,IACnC,WAAW,SAAS,gBAAgB;EAGtC,MAAM,gBAAgB,gBAAgB;EAItC,MAAM,iBAFJ,oBAAoB,cAAc,mBAClC,oBAAoB,cAAc,SAEhB,wBAAwB,KACrC,cAAc,aAAa,cAAc,KAC1C,cAAc,gBAAgB,cAC9B;EAEN,gBAAgB,MAAM,iBAAiB;EACvC,IAAI,gBAAgB,GAClB,gBAAgB,MAAM,eAAe,GACnC,WAAW,SAAS,YAAY,IAAI,cACrC;EAGH,MAAM,sBAAsB;GAC1B,gBAAgB,MAAM,iBAAiB;GACvC,gBAAgB,MAAM,eAAe;EACvC;EACA,MAAM,sBAAuB,gBAAgB,YAAY;EACzD,gBAAgB,iBAAiB,UAAU,aAAa;EAExD,MAAM,YAAY,iBAAiB;GACjC,gBAAgB,oBAAoB,UAAU,aAAa;GAC3D,cAAc;GACd,UAAU;EACZ,GAAG,iBAAiB;EAEpB,gBAAgB;GACd,aAAa,SAAS;GACtB,gBAAgB,oBAAoB,UAAU,aAAa;GAC3D,cAAc;EAChB;CACF;AACF"}
package/package.json CHANGED
@@ -1,6 +1,67 @@
1
1
  {
2
2
  "name": "@assistant-ui/vue",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.1",
4
+ "description": "Vue bindings for assistant-ui",
5
+ "keywords": [
6
+ "assistant",
7
+ "ai",
8
+ "chat",
9
+ "vue",
10
+ "nuxt"
11
+ ],
12
+ "author": "AgentbaseAI Inc.",
13
+ "license": "MIT",
14
+ "type": "module",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ },
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "files": [
24
+ "dist",
25
+ "src",
26
+ "README.md"
27
+ ],
28
+ "sideEffects": false,
29
+ "dependencies": {
30
+ "@assistant-ui/core": "^0.3.24",
31
+ "@assistant-ui/store": "^0.3.17",
32
+ "@assistant-ui/tap": "^0.9.21"
33
+ },
34
+ "peerDependencies": {
35
+ "@types/react": "*",
36
+ "vue": "^3.5.0"
37
+ },
38
+ "devDependencies": {
39
+ "@assistant-ui/x-buildutils": "0.0.30",
40
+ "@types/node": "^26.6.3",
41
+ "@types/react": "^19.3.0",
42
+ "assistant-cloud": "0.2.5",
43
+ "jsdom": "^30.0.1",
44
+ "vitest": "^5.0.3",
45
+ "vue": "^3.5.43"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public",
49
+ "provenance": true
50
+ },
51
+ "homepage": "https://www.assistant-ui.com/",
52
+ "repository": {
53
+ "type": "git",
54
+ "url": "git+https://github.com/assistant-ui/assistant-ui.git",
55
+ "directory": "packages/vue"
56
+ },
57
+ "bugs": {
58
+ "url": "https://github.com/assistant-ui/assistant-ui/issues"
59
+ },
60
+ "scripts": {
61
+ "build": "aui-build",
62
+ "test": "vitest run",
63
+ "test:coverage": "vitest run --coverage --coverage.reporter=text --coverage.reporter=html --coverage.reporter=json-summary",
64
+ "typecheck": "tsc --noEmit",
65
+ "test:watch": "vitest"
66
+ }
6
67
  }
package/src/AuiIf.ts ADDED
@@ -0,0 +1,34 @@
1
+ import {
2
+ defineComponent,
3
+ type PropType,
4
+ type SlotsType,
5
+ type VNodeChild,
6
+ } from "vue";
7
+ import type { AssistantState } from "@assistant-ui/store/client";
8
+ import { useAuiState } from "./useAuiState";
9
+
10
+ /**
11
+ * Renders the default slot while `condition` selects `true` from the
12
+ * assistant state.
13
+ *
14
+ * @example
15
+ * ```html
16
+ * <AuiIf :condition="(s) => s.thread.isRunning">
17
+ * <StopButton />
18
+ * </AuiIf>
19
+ * ```
20
+ */
21
+ export const AuiIf = defineComponent({
22
+ name: "AuiIf",
23
+ props: {
24
+ condition: {
25
+ type: Function as PropType<(state: AssistantState) => boolean>,
26
+ required: true,
27
+ },
28
+ },
29
+ slots: Object as SlotsType<{ default?: () => VNodeChild[] }>,
30
+ setup(props, { slots }) {
31
+ const active = useAuiState((state) => props.condition(state));
32
+ return () => (active.value ? slots.default?.() : null);
33
+ },
34
+ });
@@ -0,0 +1,90 @@
1
+ import {
2
+ defineComponent,
3
+ inject,
4
+ onScopeDispose,
5
+ provide,
6
+ watch,
7
+ type PropType,
8
+ type SlotsType,
9
+ type VNodeChild,
10
+ } from "vue";
11
+ import {
12
+ createAssistantClient,
13
+ type AssistantClient,
14
+ type AuiConfig,
15
+ } from "@assistant-ui/store/client";
16
+ import { auiInjectionKey, createClientFacade } from "./context";
17
+ import { isDevelopment } from "@assistant-ui/core/store/internal";
18
+
19
+ /**
20
+ * Creates an `AssistantClient` from the given config and provides it to the
21
+ * subtree.
22
+ *
23
+ * At the top level, `config` alone creates this subtree's own client. Under a
24
+ * parent provider, `extends` is mandatory: pass `:extends="aui"` to extend the
25
+ * parent client or `:extends="null"` to isolate from it (enforced with a dev
26
+ * error).
27
+ *
28
+ * The `config` prop is live: passing a new config object updates element args
29
+ * in place (scopes keep their state) and mounts or unmounts added and removed
30
+ * scopes. Pass a computed config to derive it from reactive inputs. `extends`
31
+ * is captured when the provider mounts; remount (for example with a `key`) to
32
+ * change the parent.
33
+ */
34
+ export const AuiProvider = defineComponent({
35
+ name: "AuiProvider",
36
+ props: {
37
+ config: {
38
+ type: Object as PropType<AuiConfig>,
39
+ required: true,
40
+ },
41
+ extends: {
42
+ type: Object as PropType<AssistantClient | null>,
43
+ default: undefined,
44
+ },
45
+ },
46
+ slots: Object as SlotsType<{ default?: () => VNodeChild[] }>,
47
+ setup(props, { slots }) {
48
+ const injected = inject(auiInjectionKey, null);
49
+ const hasExtends = props.extends !== undefined;
50
+
51
+ if (isDevelopment) {
52
+ if (injected && !hasExtends) {
53
+ throw new Error(
54
+ 'A parent AuiProvider exists. Pass :extends="aui" to inherit it or :extends="null" to isolate.',
55
+ );
56
+ }
57
+ }
58
+
59
+ const parent = !hasExtends
60
+ ? injected?.source
61
+ : props.extends === null
62
+ ? undefined
63
+ : injected && props.extends === injected.aui
64
+ ? injected.source
65
+ : props.extends;
66
+
67
+ const handle = createAssistantClient(
68
+ {
69
+ getConfig: () => props.config,
70
+ subscribe: (listener) => watch(() => props.config, listener),
71
+ },
72
+ parent ? { parent } : undefined,
73
+ );
74
+ // The provider holds the lifetime subscription that keeps the client
75
+ // mounted while descendants come and go
76
+ const release = handle.subscribe(() => {});
77
+ onScopeDispose(release);
78
+
79
+ const source = {
80
+ getClient: handle.getClient,
81
+ subscribe: handle.subscribe,
82
+ };
83
+ provide(auiInjectionKey, {
84
+ source,
85
+ aui: createClientFacade(source),
86
+ });
87
+
88
+ return () => slots.default?.();
89
+ },
90
+ });
@@ -0,0 +1,54 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createApp, defineComponent, h, nextTick, type Component } from "vue";
3
+ import { flushTapSync } from "@assistant-ui/tap";
4
+ import { AuiConfig } from "@assistant-ui/store/client";
5
+ import { AuiProvider } from "../AuiProvider";
6
+ import { AuiIf } from "../AuiIf";
7
+ import { useAui } from "../useAui";
8
+ import { ThreadClient, type AnyClient } from "./fixtures";
9
+
10
+ const mountApp = (root: Component) => {
11
+ const app = createApp(root);
12
+ const el = document.createElement("div");
13
+ app.mount(el);
14
+ return { el, unmount: () => app.unmount() };
15
+ };
16
+
17
+ describe("AuiIf", () => {
18
+ it("renders the slot only while the condition selects true", async () => {
19
+ let aui!: AnyClient;
20
+ const Probe = defineComponent({
21
+ setup() {
22
+ aui = useAui() as AnyClient;
23
+ return () =>
24
+ h(
25
+ AuiIf,
26
+ { condition: (s) => (s as AnyClient).thread.selected === 1 },
27
+ { default: () => h("span", { id: "flag" }, "visible") },
28
+ );
29
+ },
30
+ });
31
+ const { el, unmount } = mountApp(
32
+ defineComponent({
33
+ setup: () => () =>
34
+ h(
35
+ AuiProvider,
36
+ { config: AuiConfig({ thread: ThreadClient() } as never) },
37
+ { default: () => h(Probe) },
38
+ ),
39
+ }),
40
+ );
41
+
42
+ expect(el.querySelector("#flag")).toBeNull();
43
+
44
+ flushTapSync(() => aui.thread.setSelected(1));
45
+ await nextTick();
46
+ expect(el.querySelector("#flag")?.textContent).toBe("visible");
47
+
48
+ flushTapSync(() => aui.thread.setSelected(0));
49
+ await nextTick();
50
+ expect(el.querySelector("#flag")).toBeNull();
51
+
52
+ unmount();
53
+ });
54
+ });
@@ -0,0 +1,266 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import {
3
+ createApp,
4
+ defineComponent,
5
+ h,
6
+ nextTick,
7
+ shallowRef,
8
+ type Component,
9
+ } from "vue";
10
+ import { flushTapSync } from "@assistant-ui/tap";
11
+ import { AuiConfig, type AssistantClient } from "@assistant-ui/store/client";
12
+ import { AuiProvider } from "../AuiProvider";
13
+ import { useAui } from "../useAui";
14
+ import {
15
+ MessageClient,
16
+ ThreadClient,
17
+ messageDerived,
18
+ trackers,
19
+ type AnyClient,
20
+ } from "./fixtures";
21
+
22
+ const mountApp = (root: Component) => {
23
+ const app = createApp(root);
24
+ const el = document.createElement("div");
25
+ app.mount(el);
26
+ return { app, unmount: () => app.unmount() };
27
+ };
28
+
29
+ const captureAui = () => {
30
+ let aui!: AnyClient;
31
+ const Probe = defineComponent({
32
+ setup() {
33
+ aui = useAui() as AnyClient;
34
+ return () => null;
35
+ },
36
+ });
37
+ return { Probe, getAui: () => aui };
38
+ };
39
+
40
+ describe("AuiProvider", () => {
41
+ it("provides a client built from the config", () => {
42
+ const { Probe, getAui } = captureAui();
43
+ const { unmount } = mountApp(
44
+ defineComponent({
45
+ setup: () => () =>
46
+ h(
47
+ AuiProvider,
48
+ { config: AuiConfig({ thread: ThreadClient() } as never) },
49
+ { default: () => h(Probe) },
50
+ ),
51
+ }),
52
+ );
53
+
54
+ expect(getAui().thread.getState()).toEqual({ selected: 0 });
55
+
56
+ flushTapSync(() => getAui().thread.setSelected(1));
57
+ expect(getAui().thread.getState()).toEqual({ selected: 1 });
58
+
59
+ unmount();
60
+ });
61
+
62
+ it("keeps the facade stable across structural changes", () => {
63
+ const { Probe, getAui } = captureAui();
64
+ const { unmount } = mountApp(
65
+ defineComponent({
66
+ setup: () => () =>
67
+ h(
68
+ AuiProvider,
69
+ {
70
+ config: AuiConfig({
71
+ thread: ThreadClient(),
72
+ message: messageDerived(),
73
+ } as never),
74
+ },
75
+ { default: () => h(Probe) },
76
+ ),
77
+ }),
78
+ );
79
+
80
+ const aui = getAui();
81
+ expect(aui.message.getState().id).toBe("m0");
82
+
83
+ flushTapSync(() => aui.thread.setSelected(1));
84
+
85
+ expect(getAui()).toBe(aui);
86
+ expect(aui.message.getState().id).toBe("m1");
87
+
88
+ unmount();
89
+ });
90
+
91
+ it("extends the parent provider's client with an explicit extends", () => {
92
+ const { Probe, getAui } = captureAui();
93
+ const Nested = defineComponent({
94
+ setup(_, { slots }) {
95
+ const aui = useAui();
96
+ return () =>
97
+ h(
98
+ AuiProvider,
99
+ {
100
+ config: AuiConfig({ message: messageDerived() } as never),
101
+ extends: aui,
102
+ },
103
+ { default: () => slots.default?.() },
104
+ );
105
+ },
106
+ });
107
+ const { unmount } = mountApp(
108
+ defineComponent({
109
+ setup: () => () =>
110
+ h(
111
+ AuiProvider,
112
+ { config: AuiConfig({ thread: ThreadClient() } as never) },
113
+ { default: () => h(Nested, null, { default: () => h(Probe) }) },
114
+ ),
115
+ }),
116
+ );
117
+
118
+ const aui = getAui();
119
+ expect(aui.message.getState().id).toBe("m0");
120
+ expect(aui.thread.getState()).toEqual({ selected: 0 });
121
+
122
+ flushTapSync(() => aui.thread.setSelected(1));
123
+ expect(aui.message.getState().id).toBe("m1");
124
+
125
+ unmount();
126
+ });
127
+
128
+ it("throws in dev when nested without an explicit extends", () => {
129
+ const Nested = defineComponent({
130
+ setup: () => () =>
131
+ h(
132
+ AuiProvider,
133
+ { config: AuiConfig({ message: messageDerived() } as never) },
134
+ { default: () => null },
135
+ ),
136
+ });
137
+ expect(() =>
138
+ mountApp(
139
+ defineComponent({
140
+ setup: () => () =>
141
+ h(
142
+ AuiProvider,
143
+ { config: AuiConfig({ thread: ThreadClient() } as never) },
144
+ { default: () => h(Nested) },
145
+ ),
146
+ }),
147
+ ),
148
+ ).toThrow("A parent AuiProvider exists.");
149
+ });
150
+
151
+ it("isolates from the parent with extends null", () => {
152
+ const { Probe, getAui } = captureAui();
153
+ const Isolated = defineComponent({
154
+ setup: () => () =>
155
+ h(
156
+ AuiProvider,
157
+ {
158
+ config: AuiConfig({
159
+ message: MessageClient({ id: "iso" }),
160
+ } as never),
161
+ extends: null,
162
+ },
163
+ { default: () => h(Probe) },
164
+ ),
165
+ });
166
+ const { unmount } = mountApp(
167
+ defineComponent({
168
+ setup: () => () =>
169
+ h(
170
+ AuiProvider,
171
+ { config: AuiConfig({ thread: ThreadClient() } as never) },
172
+ { default: () => h(Isolated) },
173
+ ),
174
+ }),
175
+ );
176
+
177
+ expect(getAui().message.getState()).toEqual({ id: "iso", text: "" });
178
+ expect(() => getAui().thread.getState()).toThrow(
179
+ 'The current scope does not have a "thread" property.',
180
+ );
181
+
182
+ unmount();
183
+ });
184
+
185
+ it("applies config changes in place, keeping scope state", async () => {
186
+ const { Probe, getAui } = captureAui();
187
+ const config = shallowRef(
188
+ AuiConfig({ message: MessageClient({ id: "m0" }) } as never),
189
+ );
190
+ const { unmount } = mountApp(
191
+ defineComponent({
192
+ setup: () => () =>
193
+ h(AuiProvider, { config: config.value }, { default: () => h(Probe) }),
194
+ }),
195
+ );
196
+
197
+ flushTapSync(() => getAui().message.setText("draft"));
198
+ expect(getAui().message.getState()).toEqual({ id: "m0", text: "draft" });
199
+
200
+ config.value = AuiConfig({ message: MessageClient({ id: "m1" }) } as never);
201
+ await nextTick();
202
+ await vi.waitFor(() => {
203
+ expect(getAui().message.getState()).toEqual({ id: "m1", text: "draft" });
204
+ });
205
+
206
+ config.value = AuiConfig({
207
+ message: MessageClient({ id: "m1" }),
208
+ thread: ThreadClient(),
209
+ } as never);
210
+ await nextTick();
211
+ await vi.waitFor(() => {
212
+ expect(getAui().thread.getState()).toEqual({ selected: 0 });
213
+ });
214
+ expect(getAui().message.getState()).toEqual({ id: "m1", text: "draft" });
215
+
216
+ config.value = AuiConfig({ message: MessageClient({ id: "m1" }) } as never);
217
+ await nextTick();
218
+ await vi.waitFor(() => {
219
+ expect(() => getAui().thread.getState()).toThrow(
220
+ 'The current scope does not have a "thread" property.',
221
+ );
222
+ });
223
+
224
+ unmount();
225
+ });
226
+
227
+ it("releases the client on unmount, soft unmounting its scopes", async () => {
228
+ const before = trackers.threadCleanups;
229
+ const { Probe } = captureAui();
230
+ const { unmount } = mountApp(
231
+ defineComponent({
232
+ setup: () => () =>
233
+ h(
234
+ AuiProvider,
235
+ { config: AuiConfig({ thread: ThreadClient() } as never) },
236
+ { default: () => h(Probe) },
237
+ ),
238
+ }),
239
+ );
240
+
241
+ const afterMount = trackers.threadCleanups;
242
+ unmount();
243
+ await vi.waitFor(() =>
244
+ expect(trackers.threadCleanups).toBe(afterMount + 1),
245
+ );
246
+ expect(afterMount).toBeGreaterThanOrEqual(before);
247
+ });
248
+
249
+ it("returns the throwing default client outside a provider", () => {
250
+ let aui!: AssistantClient;
251
+ const { unmount } = mountApp(
252
+ defineComponent({
253
+ setup() {
254
+ aui = useAui();
255
+ return () => null;
256
+ },
257
+ }),
258
+ );
259
+
260
+ expect(() => (aui as AnyClient).thread.getState()).toThrow(
261
+ "Wrap your component in an <AuiProvider> component.",
262
+ );
263
+
264
+ unmount();
265
+ });
266
+ });