@assistant-ui/react 0.14.23 → 0.14.26

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 (87) hide show
  1. package/dist/index.d.ts +4 -3
  2. package/dist/index.js +4 -3
  3. package/dist/legacy-runtime/hooks/AttachmentContext.d.ts +12 -12
  4. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts +3 -1
  5. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts.map +1 -1
  6. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +2 -2
  7. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js.map +1 -1
  8. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
  9. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +6 -2
  10. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  11. package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
  12. package/dist/primitives/composer/ComposerInput.js +5 -14
  13. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  14. package/dist/primitives/composer/useComposerInputState.d.ts +13 -0
  15. package/dist/primitives/composer/useComposerInputState.d.ts.map +1 -0
  16. package/dist/primitives/composer/useComposerInputState.js +47 -0
  17. package/dist/primitives/composer/useComposerInputState.js.map +1 -0
  18. package/dist/primitives/messagePart/MessagePartText.d.ts +2 -0
  19. package/dist/primitives/messagePart/MessagePartText.d.ts.map +1 -1
  20. package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
  21. package/dist/primitives/thread/ThreadMessages.d.ts +2 -2
  22. package/dist/primitives/thread/ThreadMessages.js +2 -2
  23. package/dist/primitives/thread.d.ts +3 -3
  24. package/dist/primitives/thread.js +3 -2
  25. package/dist/primitives/thread.js.map +1 -1
  26. package/dist/primitives/threadList/ThreadListRoot.d.ts.map +1 -1
  27. package/dist/primitives/threadList/ThreadListRoot.js +10 -3
  28. package/dist/primitives/threadList/ThreadListRoot.js.map +1 -1
  29. package/dist/primitives/threadListFocusGroup.d.ts +26 -0
  30. package/dist/primitives/threadListFocusGroup.d.ts.map +1 -0
  31. package/dist/primitives/threadListFocusGroup.js +15 -0
  32. package/dist/primitives/threadListFocusGroup.js.map +1 -0
  33. package/dist/primitives/threadListItem/ThreadListItemRoot.d.ts.map +1 -1
  34. package/dist/primitives/threadListItem/ThreadListItemRoot.js +45 -27
  35. package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
  36. package/dist/primitives/threadListItem/ThreadListItemTrigger.d.ts.map +1 -1
  37. package/dist/primitives/threadListItem/ThreadListItemTrigger.js +26 -2
  38. package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
  39. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.d.ts.map +1 -1
  40. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js +63 -38
  41. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -1
  42. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.d.ts +20 -3
  43. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.d.ts.map +1 -1
  44. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js +95 -14
  45. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -1
  46. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.d.ts.map +1 -1
  47. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js +38 -21
  48. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js.map +1 -1
  49. package/dist/unstable/useComposerInput.d.ts +84 -0
  50. package/dist/unstable/useComposerInput.d.ts.map +1 -0
  51. package/dist/unstable/useComposerInput.js +78 -0
  52. package/dist/unstable/useComposerInput.js.map +1 -0
  53. package/dist/utils/hooks/useMediaQuery.js +2 -2
  54. package/dist/utils/hooks/useMediaQuery.js.map +1 -1
  55. package/dist/utils/smooth/useSmooth.d.ts +4 -0
  56. package/dist/utils/smooth/useSmooth.d.ts.map +1 -1
  57. package/dist/utils/smooth/useSmooth.js +7 -1
  58. package/dist/utils/smooth/useSmooth.js.map +1 -1
  59. package/dist/utils/useToolArgsFieldStatus.d.ts +2 -2
  60. package/dist/utils/useToolArgsFieldStatus.d.ts.map +1 -1
  61. package/package.json +16 -14
  62. package/src/index.ts +39 -0
  63. package/src/legacy-runtime/runtime-cores/assistant-transport/commandQueue.ts +5 -2
  64. package/src/legacy-runtime/runtime-cores/assistant-transport/transport-scheduling.test.ts +22 -0
  65. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +12 -2
  66. package/src/primitives/composer/ComposerInput.tsx +9 -20
  67. package/src/primitives/composer/trigger/triggerKeyboardResource.test.ts +18 -22
  68. package/src/primitives/composer/useComposerInputState.ts +34 -0
  69. package/src/primitives/messagePart/MessagePartText.tsx +1 -0
  70. package/src/primitives/thread/ThreadMessages.tsx +1 -0
  71. package/src/primitives/thread.ts +1 -0
  72. package/src/primitives/threadList/ThreadListRoot.tsx +8 -1
  73. package/src/primitives/threadListFocusGroup.ts +24 -0
  74. package/src/primitives/threadListItem/ThreadListItemRoot.tsx +53 -5
  75. package/src/primitives/threadListItem/ThreadListItemTrigger.tsx +45 -0
  76. package/src/primitives/threadListItem/ThreadListKeyboardNav.test.tsx +141 -0
  77. package/src/primitives/threadListItemMore/ThreadListItemMoreContent.tsx +22 -1
  78. package/src/primitives/threadListItemMore/ThreadListItemMoreRoot.tsx +64 -3
  79. package/src/primitives/threadListItemMore/ThreadListItemMoreTrigger.tsx +29 -2
  80. package/src/tests/generative-ui.test.tsx +21 -1
  81. package/src/tests/threadMessageById.test.tsx +224 -0
  82. package/src/unstable/useComposerInput.test.tsx +187 -0
  83. package/src/unstable/useComposerInput.ts +126 -0
  84. package/src/utils/hooks/useMediaQuery.ts +3 -2
  85. package/src/utils/smooth/useSmooth.test.tsx +26 -0
  86. package/src/utils/smooth/useSmooth.ts +7 -1
  87. package/src/primitives/threadListItem/ThreadListItemTrigger.ts +0 -23
@@ -1,11 +1,28 @@
1
- import { FC } from "react";
1
+ import { Dispatch, FC, SetStateAction } from "react";
2
2
  import { DropdownMenu } from "radix-ui";
3
3
 
4
4
  //#region src/primitives/threadListItemMore/ThreadListItemMoreRoot.d.ts
5
+ declare const useThreadListItemMoreSetOpen: () => Dispatch<SetStateAction<boolean>>;
6
+ declare const useThreadListItemMoreSharedFocusGroup: () => boolean;
5
7
  declare namespace ThreadListItemMorePrimitiveRoot {
6
- type Props = DropdownMenu.DropdownMenuProps;
8
+ type Props = DropdownMenu.DropdownMenuProps & {
9
+ /**
10
+ * Join the menu to the thread list's focus group: Right opens it, Left/Escape
11
+ * close it and return focus to the trigger (mirrored in RTL). Forces the menu
12
+ * non-modal, since a focus trap can't let focus cross the trigger/menu
13
+ * boundary. Defaults to a standalone modal dropdown.
14
+ */
15
+ sharedFocusGroup?: boolean | undefined;
16
+ };
7
17
  }
18
+ /**
19
+ * Root container for the overflow menu, built on Radix DropdownMenu.
20
+ *
21
+ * Defaults to a standard, self-contained modal dropdown. Pass
22
+ * {@link ThreadListItemMorePrimitiveRoot.Props.sharedFocusGroup} to fold it into
23
+ * the thread list's keyboard navigation instead.
24
+ */
8
25
  declare const ThreadListItemMorePrimitiveRoot: FC<ThreadListItemMorePrimitiveRoot.Props>;
9
26
  //#endregion
10
- export { ThreadListItemMorePrimitiveRoot };
27
+ export { ThreadListItemMorePrimitiveRoot, useThreadListItemMoreSetOpen, useThreadListItemMoreSharedFocusGroup };
11
28
  //# sourceMappingURL=ThreadListItemMoreRoot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadListItemMoreRoot.d.ts","names":[],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreRoot.tsx"],"mappings":";;;;kBAMiB,+BAAA;EAAA,KACH,KAAA,GAAQ,YAAA,CAAsB,iBAAiB;AAAA;AAAA,cAGhD,+BAAA,EAAiC,EAAE,CAC9C,+BAAA,CAAgC,KAAA"}
1
+ {"version":3,"file":"ThreadListItemMoreRoot.d.ts","names":[],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreRoot.tsx"],"mappings":";;;;cAiBa,4BAAA,QAAmC,QAAQ,CACtD,cAAA;AAAA,cAKW,qCAAA;AAAA,kBAGI,+BAAA;EAAA,KACH,KAAA,GAAQ,YAAA,CAAsB,iBAAiB;IAT3D;;AAAc;AAKhB;;;IAWI,gBAAA;EAAA;AAAA;;;;;;;;cAWS,+BAAA,EAAiC,EAAE,CAC9C,+BAAA,CAAgC,KAAA"}
@@ -1,37 +1,118 @@
1
1
  "use client";
2
2
  import { useDropdownMenuScope } from "./scope.js";
3
3
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
4
+ import { createContext, useContext } from "@assistant-ui/tap/react-shim";
4
5
  import { jsx } from "react/jsx-runtime";
5
6
  import { DropdownMenu } from "radix-ui";
7
+ import { useControllableState } from "@radix-ui/react-use-controllable-state";
6
8
  //#region src/primitives/threadListItemMore/ThreadListItemMoreRoot.tsx
9
+ const ThreadListItemMoreSetOpenContext = createContext(() => {});
10
+ const useThreadListItemMoreSetOpen = () => {
11
+ return useContext(ThreadListItemMoreSetOpenContext);
12
+ };
13
+ const ThreadListItemMoreSharedFocusGroupContext = createContext(false);
14
+ const useThreadListItemMoreSharedFocusGroup = () => {
15
+ return useContext(ThreadListItemMoreSharedFocusGroupContext);
16
+ };
17
+ /**
18
+ * Root container for the overflow menu, built on Radix DropdownMenu.
19
+ *
20
+ * Defaults to a standard, self-contained modal dropdown. Pass
21
+ * {@link ThreadListItemMorePrimitiveRoot.Props.sharedFocusGroup} to fold it into
22
+ * the thread list's keyboard navigation instead.
23
+ */
7
24
  const ThreadListItemMorePrimitiveRoot = (t0) => {
8
- const $ = c(6);
25
+ const $ = c(26);
9
26
  let __scopeThreadListItemMore;
27
+ let defaultOpen;
28
+ let modalProp;
29
+ let onOpenChange;
30
+ let openProp;
10
31
  let rest;
32
+ let t1;
11
33
  if ($[0] !== t0) {
12
- ({__scopeThreadListItemMore, ...rest} = t0);
34
+ ({__scopeThreadListItemMore, open: openProp, defaultOpen, onOpenChange, sharedFocusGroup: t1, modal: modalProp, ...rest} = t0);
13
35
  $[0] = t0;
14
36
  $[1] = __scopeThreadListItemMore;
15
- $[2] = rest;
37
+ $[2] = defaultOpen;
38
+ $[3] = modalProp;
39
+ $[4] = onOpenChange;
40
+ $[5] = openProp;
41
+ $[6] = rest;
42
+ $[7] = t1;
16
43
  } else {
17
44
  __scopeThreadListItemMore = $[1];
18
- rest = $[2];
45
+ defaultOpen = $[2];
46
+ modalProp = $[3];
47
+ onOpenChange = $[4];
48
+ openProp = $[5];
49
+ rest = $[6];
50
+ t1 = $[7];
19
51
  }
52
+ const sharedFocusGroup = t1 === void 0 ? false : t1;
53
+ const modal = sharedFocusGroup ? false : modalProp ?? true;
20
54
  const scope = useDropdownMenuScope(__scopeThreadListItemMore);
21
- let t1;
22
- if ($[3] !== rest || $[4] !== scope) {
23
- t1 = /* @__PURE__ */ jsx(DropdownMenu.Root, {
55
+ const t2 = defaultOpen ?? false;
56
+ let t3;
57
+ if ($[8] !== onOpenChange) {
58
+ t3 = onOpenChange ? { onChange: onOpenChange } : {};
59
+ $[8] = onOpenChange;
60
+ $[9] = t3;
61
+ } else t3 = $[9];
62
+ let t4;
63
+ if ($[10] !== openProp || $[11] !== t2 || $[12] !== t3) {
64
+ t4 = {
65
+ prop: openProp,
66
+ defaultProp: t2,
67
+ caller: "ThreadListItemMorePrimitive.Root",
68
+ ...t3
69
+ };
70
+ $[10] = openProp;
71
+ $[11] = t2;
72
+ $[12] = t3;
73
+ $[13] = t4;
74
+ } else t4 = $[13];
75
+ const [open, setOpen] = useControllableState(t4);
76
+ let t5;
77
+ if ($[14] !== modal || $[15] !== open || $[16] !== rest || $[17] !== scope || $[18] !== setOpen) {
78
+ t5 = /* @__PURE__ */ jsx(DropdownMenu.Root, {
24
79
  ...scope,
25
- ...rest
80
+ ...rest,
81
+ modal,
82
+ open,
83
+ onOpenChange: setOpen
84
+ });
85
+ $[14] = modal;
86
+ $[15] = open;
87
+ $[16] = rest;
88
+ $[17] = scope;
89
+ $[18] = setOpen;
90
+ $[19] = t5;
91
+ } else t5 = $[19];
92
+ let t6;
93
+ if ($[20] !== setOpen || $[21] !== t5) {
94
+ t6 = /* @__PURE__ */ jsx(ThreadListItemMoreSetOpenContext.Provider, {
95
+ value: setOpen,
96
+ children: t5
97
+ });
98
+ $[20] = setOpen;
99
+ $[21] = t5;
100
+ $[22] = t6;
101
+ } else t6 = $[22];
102
+ let t7;
103
+ if ($[23] !== sharedFocusGroup || $[24] !== t6) {
104
+ t7 = /* @__PURE__ */ jsx(ThreadListItemMoreSharedFocusGroupContext.Provider, {
105
+ value: sharedFocusGroup,
106
+ children: t6
26
107
  });
27
- $[3] = rest;
28
- $[4] = scope;
29
- $[5] = t1;
30
- } else t1 = $[5];
31
- return t1;
108
+ $[23] = sharedFocusGroup;
109
+ $[24] = t6;
110
+ $[25] = t7;
111
+ } else t7 = $[25];
112
+ return t7;
32
113
  };
33
114
  ThreadListItemMorePrimitiveRoot.displayName = "ThreadListItemMorePrimitive.Root";
34
115
  //#endregion
35
- export { ThreadListItemMorePrimitiveRoot };
116
+ export { ThreadListItemMorePrimitiveRoot, useThreadListItemMoreSetOpen, useThreadListItemMoreSharedFocusGroup };
36
117
 
37
118
  //# sourceMappingURL=ThreadListItemMoreRoot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadListItemMoreRoot.js","names":["c","_c","FC","DropdownMenu","DropdownMenuPrimitive","ScopedProps","useDropdownMenuScope","ThreadListItemMorePrimitiveRoot","Props","DropdownMenuProps","t0","$","__scopeThreadListItemMore","rest","scope","t1","displayName"],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport type { FC } from \"react\";\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport { type ScopedProps, useDropdownMenuScope } from \"./scope\";\n\nexport namespace ThreadListItemMorePrimitiveRoot {\n export type Props = DropdownMenuPrimitive.DropdownMenuProps;\n}\n\nexport const ThreadListItemMorePrimitiveRoot: FC<\n ThreadListItemMorePrimitiveRoot.Props\n> = ({\n __scopeThreadListItemMore,\n ...rest\n}: ScopedProps<ThreadListItemMorePrimitiveRoot.Props>) => {\n const scope = useDropdownMenuScope(__scopeThreadListItemMore);\n\n return <DropdownMenuPrimitive.Root {...scope} {...rest} />;\n};\n\nThreadListItemMorePrimitiveRoot.displayName =\n \"ThreadListItemMorePrimitive.Root\";\n"],"mappings":";;;;;;AAUA,MAAaO,mCAETG,OAAA;CAAA,MAAAC,IAAAV,EAAA,CAAA;CAAA,IAAAW;CAAA,IAAAC;CAAA,IAAAF,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAAE,8BAAAC,QAAAH;EAGgDC,EAAA,KAAAD;EAAAC,EAAA,KAAAC;EAAAD,EAAA,KAAAE;CAAA,OAAA;EAAAD,4BAAAD,EAAA;EAAAE,OAAAF,EAAA;CAAA;CACnD,MAAAG,QAAcR,qBAAqBM,yBAAyB;CAAE,IAAAG;CAAA,IAAAJ,EAAA,OAAAE,QAAAF,EAAA,OAAAG,OAAA;EAEvDC,KAAA,oBAAA,aAAA,MAAA;GAAA,GAAgCD;GAAK,GAAMD;EAAI,CAAA;EAAIF,EAAA,KAAAE;EAAAF,EAAA,KAAAG;EAAAH,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CAAA,OAAnDI;AAAmD;AAG5DR,gCAAgCS,cAC9B"}
1
+ {"version":3,"file":"ThreadListItemMoreRoot.js","names":["c","_c","createContext","Dispatch","FC","SetStateAction","useContext","DropdownMenu","DropdownMenuPrimitive","useControllableState","ScopedProps","useDropdownMenuScope","ThreadListItemMoreSetOpenContext","useThreadListItemMoreSetOpen","ThreadListItemMoreSharedFocusGroupContext","useThreadListItemMoreSharedFocusGroup","ThreadListItemMorePrimitiveRoot","Props","DropdownMenuProps","sharedFocusGroup","t0","$","__scopeThreadListItemMore","defaultOpen","modalProp","onOpenChange","openProp","rest","t1","open","modal","undefined","scope","t2","t3","onChange","t4","prop","defaultProp","caller","setOpen","t5","t6","t7","displayName"],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n type Dispatch,\n type FC,\n type SetStateAction,\n useContext,\n} from \"react\";\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\";\nimport { type ScopedProps, useDropdownMenuScope } from \"./scope\";\n\nconst ThreadListItemMoreSetOpenContext = createContext<\n Dispatch<SetStateAction<boolean>>\n>(() => {});\n\nexport const useThreadListItemMoreSetOpen = (): Dispatch<\n SetStateAction<boolean>\n> => useContext(ThreadListItemMoreSetOpenContext);\n\nconst ThreadListItemMoreSharedFocusGroupContext = createContext(false);\n\nexport const useThreadListItemMoreSharedFocusGroup = (): boolean =>\n useContext(ThreadListItemMoreSharedFocusGroupContext);\n\nexport namespace ThreadListItemMorePrimitiveRoot {\n export type Props = DropdownMenuPrimitive.DropdownMenuProps & {\n /**\n * Join the menu to the thread list's focus group: Right opens it, Left/Escape\n * close it and return focus to the trigger (mirrored in RTL). Forces the menu\n * non-modal, since a focus trap can't let focus cross the trigger/menu\n * boundary. Defaults to a standalone modal dropdown.\n */\n sharedFocusGroup?: boolean | undefined;\n };\n}\n\n/**\n * Root container for the overflow menu, built on Radix DropdownMenu.\n *\n * Defaults to a standard, self-contained modal dropdown. Pass\n * {@link ThreadListItemMorePrimitiveRoot.Props.sharedFocusGroup} to fold it into\n * the thread list's keyboard navigation instead.\n */\nexport const ThreadListItemMorePrimitiveRoot: FC<\n ThreadListItemMorePrimitiveRoot.Props\n> = ({\n __scopeThreadListItemMore,\n open: openProp,\n defaultOpen,\n onOpenChange,\n sharedFocusGroup = false,\n modal: modalProp,\n ...rest\n}: ScopedProps<ThreadListItemMorePrimitiveRoot.Props>) => {\n const modal = sharedFocusGroup ? false : (modalProp ?? true);\n const scope = useDropdownMenuScope(__scopeThreadListItemMore);\n const [open, setOpen] = useControllableState({\n prop: openProp,\n defaultProp: defaultOpen ?? false,\n caller: \"ThreadListItemMorePrimitive.Root\",\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n\n return (\n <ThreadListItemMoreSharedFocusGroupContext.Provider\n value={sharedFocusGroup}\n >\n <ThreadListItemMoreSetOpenContext.Provider value={setOpen}>\n <DropdownMenuPrimitive.Root\n {...scope}\n {...rest}\n modal={modal}\n open={open}\n onOpenChange={setOpen}\n />\n </ThreadListItemMoreSetOpenContext.Provider>\n </ThreadListItemMoreSharedFocusGroupContext.Provider>\n );\n};\n\nThreadListItemMorePrimitiveRoot.displayName =\n \"ThreadListItemMorePrimitive.Root\";\n"],"mappings":";;;;;;;;AAaA,MAAMY,mCAAmCV,oBAEjC,CAAC,CAAC;AAEV,MAAaW,qCAA+B;CAAA,OAEvCP,WAAWM,gCAAgC;AAAC;AAEjD,MAAME,4CAA4CZ,cAAc,KAAK;AAErE,MAAaa,8CAAwC;CAAA,OACnDT,WAAWQ,yCAAyC;AAAC;;;;;;;;AAqBvD,MAAaE,mCAETI,OAAA;CAAA,MAAAC,IAAApB,EAAA,EAAA;CAAA,IAAAqB;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAP,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAAE,2BAAAO,MAAAH,UAAAH,aAAAE,cAAAN,kBAAAS,IAAAE,OAAAN,cAAAG,QAAAP;EAQgDC,EAAA,KAAAD;EAAAC,EAAA,KAAAC;EAAAD,EAAA,KAAAE;EAAAF,EAAA,KAAAG;EAAAH,EAAA,KAAAI;EAAAJ,EAAA,KAAAK;EAAAL,EAAA,KAAAM;EAAAN,EAAA,KAAAO;CAAA,OAAA;EAAAN,4BAAAD,EAAA;EAAAE,cAAAF,EAAA;EAAAG,YAAAH,EAAA;EAAAI,eAAAJ,EAAA;EAAAK,WAAAL,EAAA;EAAAM,OAAAN,EAAA;EAAAO,KAAAP,EAAA;CAAA;CAHnD,MAAAF,mBAAAS,OAAAG,KAAAA,IAAA,QAAAH;CAIA,MAAAE,QAAcX,mBAAA,QAA4BK,aAAA;CAC1C,MAAAQ,QAAcrB,qBAAqBW,yBAAyB;CAG7C,MAAAW,KAAAV,eAAA;CAAoB,IAAAW;CAAA,IAAAb,EAAA,OAAAI,cAAA;EAE7BS,KAAAT,eAAA,EAAAU,UAA2BV,aAAkB,IAA7C,CAA6C;EAACJ,EAAA,KAAAI;EAAAJ,EAAA,KAAAa;CAAA,OAAAA,KAAAb,EAAA;CAAA,IAAAe;CAAA,IAAAf,EAAA,QAAAK,YAAAL,EAAA,QAAAY,MAAAZ,EAAA,QAAAa,IAAA;EAJPE,KAAA;GAAAC,MACrCX;GAAQY,aACDL;GAAoBM,QACzB;GAAkC,GACtCL;EACN;EAACb,EAAA,MAAAK;EAAAL,EAAA,MAAAY;EAAAZ,EAAA,MAAAa;EAAAb,EAAA,MAAAe;CAAA,OAAAA,KAAAf,EAAA;CALD,MAAA,CAAAQ,MAAAW,WAAwB/B,qBAAqB2B,EAK5C;CAAE,IAAAK;CAAA,IAAApB,EAAA,QAAAS,SAAAT,EAAA,QAAAQ,QAAAR,EAAA,QAAAM,QAAAN,EAAA,QAAAW,SAAAX,EAAA,QAAAmB,SAAA;EAOGC,KAAA,oBAAA,aAAA,MAAA;GAAA,GACMT;GAAK,GACLL;GACGG;GACDD;GACQW,cAAAA;EAAO,CAAA;EACrBnB,EAAA,MAAAS;EAAAT,EAAA,MAAAQ;EAAAR,EAAA,MAAAM;EAAAN,EAAA,MAAAW;EAAAX,EAAA,MAAAmB;EAAAnB,EAAA,MAAAoB;CAAA,OAAAA,KAAApB,EAAA;CAAA,IAAAqB;CAAA,IAAArB,EAAA,QAAAmB,WAAAnB,EAAA,QAAAoB,IAAA;EAPJC,KAAA,oBAAA,iCAAA,UAAA;GAAkDF,OAAAA;aAChDC;EAOF,CAAA;EAA4CpB,EAAA,MAAAmB;EAAAnB,EAAA,MAAAoB;EAAApB,EAAA,MAAAqB;CAAA,OAAAA,KAAArB,EAAA;CAAA,IAAAsB;CAAA,IAAAtB,EAAA,QAAAF,oBAAAE,EAAA,QAAAqB,IAAA;EAX9CC,KAAA,oBAAA,0CAAA,UAAA;GACSxB,OAAAA;aAEPuB;EASF,CAAA;EAAqDrB,EAAA,MAAAF;EAAAE,EAAA,MAAAqB;EAAArB,EAAA,MAAAsB;CAAA,OAAAA,KAAAtB,EAAA;CAAA,OAZrDsB;AAYqD;AAIzD3B,gCAAgC4B,cAC9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadListItemMoreTrigger.d.ts","names":[],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreTrigger.tsx"],"mappings":";;;;;kBAQiB,kCAAA;EAAA,KACH,OAAA,GAAU,YAAA,QAAoB,YAAA,CAAsB,OAAA;EAAA,KACpD,KAAA,GAAQ,mBAAA,QAA2B,YAAA,CAAsB,OAAA;AAAA;AAAA,cAG1D,kCAAA,kBAAkC,yBAAA,CAAA,IAAA,CAAA,YAAA,CAAA,wBAAA,mBAAA,aAAA,CAAA,iBAAA"}
1
+ {"version":3,"file":"ThreadListItemMoreTrigger.d.ts","names":[],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreTrigger.tsx"],"mappings":";;;;;kBAkBiB,kCAAA;EAAA,KACH,OAAA,GAAU,YAAA,QAAoB,YAAA,CAAsB,OAAA;EAAA,KACpD,KAAA,GAAQ,mBAAA,QAA2B,YAAA,CAAsB,OAAA;AAAA;AAAA,cAG1D,kCAAA,kBAAkC,yBAAA,CAAA,IAAA,CAAA,YAAA,CAAA,wBAAA,mBAAA,aAAA,CAAA,iBAAA"}
@@ -1,37 +1,54 @@
1
1
  "use client";
2
2
  import { DropdownMenuRenderTrigger } from "../dropdownMenuRenderPrimitives.js";
3
+ import { useThreadListItemFocus } from "../threadListFocusGroup.js";
3
4
  import { useDropdownMenuScope } from "./scope.js";
5
+ import { useThreadListItemMoreSetOpen, useThreadListItemMoreSharedFocusGroup } from "./ThreadListItemMoreRoot.js";
4
6
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
5
7
  import { forwardRef } from "@assistant-ui/tap/react-shim";
6
8
  import { jsx } from "react/jsx-runtime";
9
+ import { useComposedRefs } from "@radix-ui/react-compose-refs";
10
+ import { composeEventHandlers } from "@radix-ui/primitive";
11
+ import { Direction } from "radix-ui";
7
12
  //#region src/primitives/threadListItemMore/ThreadListItemMoreTrigger.tsx
8
13
  const ThreadListItemMorePrimitiveTrigger = forwardRef((t0, ref) => {
9
- const $ = c(7);
10
- let __scopeThreadListItemMore;
11
- let rest;
12
- if ($[0] !== t0) {
13
- ({__scopeThreadListItemMore, ...rest} = t0);
14
- $[0] = t0;
15
- $[1] = __scopeThreadListItemMore;
16
- $[2] = rest;
17
- } else {
18
- __scopeThreadListItemMore = $[1];
19
- rest = $[2];
20
- }
14
+ const $ = c(10);
15
+ const { __scopeThreadListItemMore, ...rest } = t0;
21
16
  const scope = useDropdownMenuScope(__scopeThreadListItemMore);
17
+ const focus = useThreadListItemFocus();
18
+ const setOpen = useThreadListItemMoreSetOpen();
19
+ const sharedFocusGroup = useThreadListItemMoreSharedFocusGroup();
20
+ const composedRef = useComposedRefs(ref, focus?.moreRef);
21
+ const openKey = Direction.useDirection() === "rtl" ? "ArrowLeft" : "ArrowRight";
22
+ const T0 = DropdownMenuRenderTrigger;
22
23
  let t1;
23
- if ($[3] !== ref || $[4] !== rest || $[5] !== scope) {
24
- t1 = /* @__PURE__ */ jsx(DropdownMenuRenderTrigger, {
24
+ if ($[0] !== openKey || $[1] !== setOpen || $[2] !== sharedFocusGroup) {
25
+ t1 = (event) => {
26
+ if (!sharedFocusGroup || event.key !== openKey) return;
27
+ event.preventDefault();
28
+ setOpen(true);
29
+ };
30
+ $[0] = openKey;
31
+ $[1] = setOpen;
32
+ $[2] = sharedFocusGroup;
33
+ $[3] = t1;
34
+ } else t1 = $[3];
35
+ const t2 = composeEventHandlers(rest.onKeyDown, t1);
36
+ let t3;
37
+ if ($[4] !== T0 || $[5] !== composedRef || $[6] !== rest || $[7] !== scope || $[8] !== t2) {
38
+ t3 = /* @__PURE__ */ jsx(T0, {
25
39
  ...scope,
26
40
  ...rest,
27
- ref
41
+ onKeyDown: t2,
42
+ ref: composedRef
28
43
  });
29
- $[3] = ref;
30
- $[4] = rest;
31
- $[5] = scope;
32
- $[6] = t1;
33
- } else t1 = $[6];
34
- return t1;
44
+ $[4] = T0;
45
+ $[5] = composedRef;
46
+ $[6] = rest;
47
+ $[7] = scope;
48
+ $[8] = t2;
49
+ $[9] = t3;
50
+ } else t3 = $[9];
51
+ return t3;
35
52
  });
36
53
  ThreadListItemMorePrimitiveTrigger.displayName = "ThreadListItemMorePrimitive.Trigger";
37
54
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadListItemMoreTrigger.js","names":["c","_c","ComponentRef","forwardRef","DropdownMenu","DropdownMenuPrimitive","WithRenderPropProps","DropdownMenuRenderTrigger","ScopedProps","useDropdownMenuScope","ThreadListItemMorePrimitiveTrigger","Element","Trigger","Props","t0","ref","$","__scopeThreadListItemMore","rest","scope","t1","displayName"],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreTrigger.tsx"],"sourcesContent":["\"use client\";\n\nimport { type ComponentRef, forwardRef } from \"react\";\nimport type { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport type { WithRenderPropProps } from \"../../utils/Primitive\";\nimport { DropdownMenuRenderTrigger } from \"../dropdownMenuRenderPrimitives\";\nimport { type ScopedProps, useDropdownMenuScope } from \"./scope\";\n\nexport namespace ThreadListItemMorePrimitiveTrigger {\n export type Element = ComponentRef<typeof DropdownMenuPrimitive.Trigger>;\n export type Props = WithRenderPropProps<typeof DropdownMenuPrimitive.Trigger>;\n}\n\nexport const ThreadListItemMorePrimitiveTrigger = forwardRef<\n ThreadListItemMorePrimitiveTrigger.Element,\n ThreadListItemMorePrimitiveTrigger.Props\n>(\n (\n {\n __scopeThreadListItemMore,\n ...rest\n }: ScopedProps<ThreadListItemMorePrimitiveTrigger.Props>,\n ref,\n ) => {\n const scope = useDropdownMenuScope(__scopeThreadListItemMore);\n\n return <DropdownMenuRenderTrigger {...scope} {...rest} ref={ref} />;\n },\n);\n\nThreadListItemMorePrimitiveTrigger.displayName =\n \"ThreadListItemMorePrimitive.Trigger\";\n"],"mappings":";;;;;;;AAaA,MAAaU,qCAAqCP,YAIhDW,IAAAC,QAAA;CAAA,MAAAC,IAAAf,EAAA,CAAA;CAAA,IAAAgB;CAAA,IAAAC;CAAA,IAAAF,EAAA,OAAAF,IAAA;EACE,CAAA,CAAAG,8BAAAC,QAAAJ;EAGwDE,EAAA,KAAAF;EAAAE,EAAA,KAAAC;EAAAD,EAAA,KAAAE;CAAA,OAAA;EAAAD,4BAAAD,EAAA;EAAAE,OAAAF,EAAA;CAAA;CAGxD,MAAAG,QAAcV,qBAAqBQ,yBAAyB;CAAE,IAAAG;CAAA,IAAAJ,EAAA,OAAAD,OAAAC,EAAA,OAAAE,QAAAF,EAAA,OAAAG,OAAA;EAEvDC,KAAA,oBAAC,2BAAD;GAA0B,GAAKD;GAAK,GAAMD;GAAWH;EAAG,CAAA;EAAIC,EAAA,KAAAD;EAAAC,EAAA,KAAAE;EAAAF,EAAA,KAAAG;EAAAH,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CAAA,OAA5DI;AAA4D,CAEvE;AAEAV,mCAAmCW,cACjC"}
1
+ {"version":3,"file":"ThreadListItemMoreTrigger.js","names":["c","_c","ComponentRef","forwardRef","Direction","DropdownMenu","DropdownMenuPrimitive","useComposedRefs","composeEventHandlers","WithRenderPropProps","DropdownMenuRenderTrigger","useThreadListItemFocus","useThreadListItemMoreSharedFocusGroup","useThreadListItemMoreSetOpen","ScopedProps","useDropdownMenuScope","ThreadListItemMorePrimitiveTrigger","Element","Trigger","Props","t0","ref","$","__scopeThreadListItemMore","rest","scope","focus","setOpen","sharedFocusGroup","composedRef","moreRef","direction","useDirection","openKey","T0","t1","event","key","preventDefault","t2","onKeyDown","t3","displayName"],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreTrigger.tsx"],"sourcesContent":["\"use client\";\n\nimport { type ComponentRef, forwardRef } from \"react\";\nimport {\n Direction,\n type DropdownMenu as DropdownMenuPrimitive,\n} from \"radix-ui\";\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport type { WithRenderPropProps } from \"../../utils/Primitive\";\nimport { DropdownMenuRenderTrigger } from \"../dropdownMenuRenderPrimitives\";\nimport { useThreadListItemFocus } from \"../threadListFocusGroup\";\nimport {\n useThreadListItemMoreSharedFocusGroup,\n useThreadListItemMoreSetOpen,\n} from \"./ThreadListItemMoreRoot\";\nimport { type ScopedProps, useDropdownMenuScope } from \"./scope\";\n\nexport namespace ThreadListItemMorePrimitiveTrigger {\n export type Element = ComponentRef<typeof DropdownMenuPrimitive.Trigger>;\n export type Props = WithRenderPropProps<typeof DropdownMenuPrimitive.Trigger>;\n}\n\nexport const ThreadListItemMorePrimitiveTrigger = forwardRef<\n ThreadListItemMorePrimitiveTrigger.Element,\n ThreadListItemMorePrimitiveTrigger.Props\n>(\n (\n {\n __scopeThreadListItemMore,\n ...rest\n }: ScopedProps<ThreadListItemMorePrimitiveTrigger.Props>,\n ref,\n ) => {\n const scope = useDropdownMenuScope(__scopeThreadListItemMore);\n const focus = useThreadListItemFocus();\n const setOpen = useThreadListItemMoreSetOpen();\n const sharedFocusGroup = useThreadListItemMoreSharedFocusGroup();\n const composedRef = useComposedRefs(ref, focus?.moreRef);\n const direction = Direction.useDirection();\n const openKey = direction === \"rtl\" ? \"ArrowLeft\" : \"ArrowRight\";\n\n return (\n <DropdownMenuRenderTrigger\n {...scope}\n {...rest}\n onKeyDown={composeEventHandlers(rest.onKeyDown, (event) => {\n if (!sharedFocusGroup || event.key !== openKey) return;\n event.preventDefault();\n setOpen(true);\n })}\n ref={composedRef}\n />\n );\n },\n);\n\nThreadListItemMorePrimitiveTrigger.displayName =\n \"ThreadListItemMorePrimitive.Trigger\";\n"],"mappings":";;;;;;;;;;;;AAuBA,MAAagB,qCAAqCb,YAIhDiB,IAAAC,QAAA;CAAA,MAAAC,IAAArB,EAAA,EAAA;CACE,MAAA,EAAAsB,2BAAA,GAAAC,SAAAJ;CAMA,MAAAK,QAAcV,qBAAqBQ,yBAAyB;CAC5D,MAAAG,QAAcf,uBAAuB;CACrC,MAAAgB,UAAgBd,6BAA6B;CAC7C,MAAAe,mBAAyBhB,sCAAsC;CAC/D,MAAAiB,cAAoBtB,gBAAgBc,KAAKK,OAAKI,OAAS;CAEvD,MAAAG,UADkB7B,UAAS4B,aACXD,MAAc,QAAd,cAAA;CAGb,MAAAG,KAAAxB;CAAyB,IAAAyB;CAAA,IAAAb,EAAA,OAAAW,WAAAX,EAAA,OAAAK,WAAAL,EAAA,OAAAM,kBAAA;EAGwBO,MAAAC,UAAA;GAC9C,IAAI,CAACR,oBAAoBQ,MAAKC,QAASJ,SAAO;GAC9CG,MAAKE,eAAgB;GACrBX,QAAQ,IAAI;EAAC;EACdL,EAAA,KAAAW;EAAAX,EAAA,KAAAK;EAAAL,EAAA,KAAAM;EAAAN,EAAA,KAAAa;CAAA,OAAAA,KAAAb,EAAA;CAJU,MAAAiB,KAAA/B,qBAAqBgB,KAAIgB,WAAYL,EAI/C;CAAC,IAAAM;CAAA,IAAAnB,EAAA,OAAAY,MAAAZ,EAAA,OAAAO,eAAAP,EAAA,OAAAE,QAAAF,EAAA,OAAAG,SAAAH,EAAA,OAAAiB,IAAA;EAPJE,KAAA,oBAAC,IAAD;GAA0B,GACpBhB;GAAK,GACLD;GACO,WAAAe;GAKNV,KAAAA;EAAW,CAAA;EAChBP,EAAA,KAAAY;EAAAZ,EAAA,KAAAO;EAAAP,EAAA,KAAAE;EAAAF,EAAA,KAAAG;EAAAH,EAAA,KAAAiB;EAAAjB,EAAA,KAAAmB;CAAA,OAAAA,KAAAnB,EAAA;CAAA,OATFmB;AASE,CAGR;AAEAzB,mCAAmC0B,cACjC"}
@@ -0,0 +1,84 @@
1
+ import { TriggerPopoverAriaProps } from "../primitives/composer/useComposerInputState.js";
2
+ import { ComposerSendOptions } from "@assistant-ui/core/store";
3
+
4
+ //#region src/unstable/useComposerInput.d.ts
5
+ type Unstable_UseComposerInputOptions = {
6
+ /**
7
+ * Disables the input in addition to the composer's own disabled sources
8
+ * (thread disabled, active dictation). When disabled, `isDisabled` is `true`
9
+ * and `canSend` is `false`.
10
+ */
11
+ disabled?: boolean | undefined;
12
+ };
13
+ type Unstable_ComposerInput = {
14
+ /** Current composer text, or `""` when the composer is not editing. */value: string;
15
+ /**
16
+ * Writes `text` into the composer, mirroring `ComposerPrimitive.Input`:
17
+ * a no-op unless the composer is editing, committed via `flushTapSync` so the
18
+ * controlled value stays in sync within the same tick.
19
+ */
20
+ setText(text: string): void;
21
+ /**
22
+ * Sends the current message when `canSend` is `true`; otherwise a no-op.
23
+ * Accepts the same options as the composer send action.
24
+ */
25
+ send(options?: ComposerSendOptions): void;
26
+ /**
27
+ * Whether the input is disabled, combining the `disabled` option with the
28
+ * composer's own disabled sources (thread disabled, active dictation).
29
+ */
30
+ isDisabled: boolean;
31
+ /**
32
+ * Whether a send is currently available. Matches `ComposerPrimitive.Send`
33
+ * gating (non-empty editing composer, not running without queue support) and
34
+ * is additionally `false` while `isDisabled`.
35
+ */
36
+ canSend: boolean;
37
+ };
38
+ /**
39
+ * @deprecated Under active development and might change without notice.
40
+ *
41
+ * Headless bridge to the composer's text value and send action, for building a
42
+ * custom composer input without `ComposerPrimitive.Input`. It is a thin bridge,
43
+ * not a second input: it does not own keyboard behavior, autosize, IME or
44
+ * contentEditable sync, paste/drop attachments, focus management, or rich-text
45
+ * state. Spread `unstable_useTriggerPopoverAriaProps()` onto your element for
46
+ * trigger-popover combobox semantics.
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * const { value, setText, send, isDisabled, canSend } = unstable_useComposerInput();
51
+ * <textarea
52
+ * value={value}
53
+ * disabled={isDisabled}
54
+ * onChange={(e) => setText(e.target.value)}
55
+ * onKeyDown={(e) => {
56
+ * if (e.key === "Enter" && !e.shiftKey && canSend) {
57
+ * e.preventDefault();
58
+ * send();
59
+ * }
60
+ * }}
61
+ * />
62
+ * ```
63
+ */
64
+ declare function unstable_useComposerInput(options?: Unstable_UseComposerInputOptions): Unstable_ComposerInput;
65
+ type Unstable_TriggerPopoverAriaProps = TriggerPopoverAriaProps;
66
+ /**
67
+ * @deprecated Under active development and might change without notice.
68
+ *
69
+ * ARIA combobox attributes for the focused element (typically the composer
70
+ * input) describing the open trigger popover, per the WAI-ARIA editable
71
+ * combobox pattern. Returns an empty object outside a `TriggerPopoverRoot` or
72
+ * when no popover is open. Spread these last so they take precedence over any
73
+ * matching ARIA props you set yourself, mirroring `ComposerPrimitive.Input`.
74
+ *
75
+ * @example
76
+ * ```tsx
77
+ * const aria = unstable_useTriggerPopoverAriaProps();
78
+ * <textarea {...aria} />
79
+ * ```
80
+ */
81
+ declare function unstable_useTriggerPopoverAriaProps(): Unstable_TriggerPopoverAriaProps;
82
+ //#endregion
83
+ export { Unstable_ComposerInput, Unstable_TriggerPopoverAriaProps, Unstable_UseComposerInputOptions, unstable_useComposerInput, unstable_useTriggerPopoverAriaProps };
84
+ //# sourceMappingURL=useComposerInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useComposerInput.d.ts","names":[],"sources":["../../src/unstable/useComposerInput.ts"],"mappings":";;;;KAcY,gCAAA;;AAAZ;;;;EAME,QAAQ;AAAA;AAAA,KAGE,sBAAA;yEAEV,KAAA;EAAA;;;;;EAMA,OAAA,CAAQ,IAAA;EAUR;;;AAMO;EAXP,IAAA,CAAK,OAAA,GAAU,mBAAmB;EAwCK;;;;EAnCvC,UAAA;EAqCC;;AAAsB;AA4BzB;;EA3DE,OAAA;AAAA;AA2DoE;AAiBtE;;;;AAAuF;;;;;;;;;;;;;;;;;;;;;AAjBjB,iBA9BtD,yBAAA,CACd,OAAA,GAAU,gCAAA,GACT,sBAAsB;AAAA,KA4Bb,gCAAA,GAAmC,uBAAuB;;;;;;;;;;;;;;;;iBAiBtD,mCAAA,IAAuC,gCAAgC"}
@@ -0,0 +1,78 @@
1
+ "use client";
2
+ import { useComposerInputDisabled, useComposerInputValue, useTriggerPopoverAriaProps } from "../primitives/composer/useComposerInputState.js";
3
+ import { useAui } from "@assistant-ui/store";
4
+ import { useComposerSend } from "@assistant-ui/core/react";
5
+ import { useCallback } from "@assistant-ui/tap/react-shim";
6
+ import { flushTapSync } from "@assistant-ui/tap";
7
+ //#region src/unstable/useComposerInput.ts
8
+ /**
9
+ * @deprecated Under active development and might change without notice.
10
+ *
11
+ * Headless bridge to the composer's text value and send action, for building a
12
+ * custom composer input without `ComposerPrimitive.Input`. It is a thin bridge,
13
+ * not a second input: it does not own keyboard behavior, autosize, IME or
14
+ * contentEditable sync, paste/drop attachments, focus management, or rich-text
15
+ * state. Spread `unstable_useTriggerPopoverAriaProps()` onto your element for
16
+ * trigger-popover combobox semantics.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * const { value, setText, send, isDisabled, canSend } = unstable_useComposerInput();
21
+ * <textarea
22
+ * value={value}
23
+ * disabled={isDisabled}
24
+ * onChange={(e) => setText(e.target.value)}
25
+ * onKeyDown={(e) => {
26
+ * if (e.key === "Enter" && !e.shiftKey && canSend) {
27
+ * e.preventDefault();
28
+ * send();
29
+ * }
30
+ * }}
31
+ * />
32
+ * ```
33
+ */
34
+ function unstable_useComposerInput(options) {
35
+ const aui = useAui();
36
+ const value = useComposerInputValue();
37
+ const isDisabled = useComposerInputDisabled(options?.disabled);
38
+ const setText = useCallback((text) => {
39
+ if (!aui.composer().getState().isEditing) return;
40
+ flushTapSync(() => {
41
+ aui.composer().setText(text);
42
+ });
43
+ }, [aui]);
44
+ const { send: rawSend, disabled: sendDisabled } = useComposerSend();
45
+ const canSend = !sendDisabled && !isDisabled;
46
+ return {
47
+ value,
48
+ setText,
49
+ send: useCallback((sendOptions) => {
50
+ if (!canSend) return;
51
+ rawSend(sendOptions);
52
+ }, [canSend, rawSend]),
53
+ isDisabled,
54
+ canSend
55
+ };
56
+ }
57
+ /**
58
+ * @deprecated Under active development and might change without notice.
59
+ *
60
+ * ARIA combobox attributes for the focused element (typically the composer
61
+ * input) describing the open trigger popover, per the WAI-ARIA editable
62
+ * combobox pattern. Returns an empty object outside a `TriggerPopoverRoot` or
63
+ * when no popover is open. Spread these last so they take precedence over any
64
+ * matching ARIA props you set yourself, mirroring `ComposerPrimitive.Input`.
65
+ *
66
+ * @example
67
+ * ```tsx
68
+ * const aria = unstable_useTriggerPopoverAriaProps();
69
+ * <textarea {...aria} />
70
+ * ```
71
+ */
72
+ function unstable_useTriggerPopoverAriaProps() {
73
+ return useTriggerPopoverAriaProps();
74
+ }
75
+ //#endregion
76
+ export { unstable_useComposerInput, unstable_useTriggerPopoverAriaProps };
77
+
78
+ //# sourceMappingURL=useComposerInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useComposerInput.js","names":["useCallback","useAui","flushTapSync","useComposerSend","ComposerSendOptions","TriggerPopoverAriaProps","useComposerInputDisabled","useComposerInputValue","useTriggerPopoverAriaProps","Unstable_UseComposerInputOptions","disabled","Unstable_ComposerInput","value","setText","text","send","options","isDisabled","canSend","unstable_useComposerInput","aui","composer","getState","isEditing","rawSend","sendDisabled","sendOptions","Unstable_TriggerPopoverAriaProps","unstable_useTriggerPopoverAriaProps"],"sources":["../../src/unstable/useComposerInput.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport { useComposerSend } from \"@assistant-ui/core/react\";\nimport type { ComposerSendOptions } from \"@assistant-ui/core/store\";\nimport {\n type TriggerPopoverAriaProps,\n useComposerInputDisabled,\n useComposerInputValue,\n useTriggerPopoverAriaProps,\n} from \"../primitives/composer/useComposerInputState\";\n\nexport type Unstable_UseComposerInputOptions = {\n /**\n * Disables the input in addition to the composer's own disabled sources\n * (thread disabled, active dictation). When disabled, `isDisabled` is `true`\n * and `canSend` is `false`.\n */\n disabled?: boolean | undefined;\n};\n\nexport type Unstable_ComposerInput = {\n /** Current composer text, or `\"\"` when the composer is not editing. */\n value: string;\n /**\n * Writes `text` into the composer, mirroring `ComposerPrimitive.Input`:\n * a no-op unless the composer is editing, committed via `flushTapSync` so the\n * controlled value stays in sync within the same tick.\n */\n setText(text: string): void;\n /**\n * Sends the current message when `canSend` is `true`; otherwise a no-op.\n * Accepts the same options as the composer send action.\n */\n send(options?: ComposerSendOptions): void;\n /**\n * Whether the input is disabled, combining the `disabled` option with the\n * composer's own disabled sources (thread disabled, active dictation).\n */\n isDisabled: boolean;\n /**\n * Whether a send is currently available. Matches `ComposerPrimitive.Send`\n * gating (non-empty editing composer, not running without queue support) and\n * is additionally `false` while `isDisabled`.\n */\n canSend: boolean;\n};\n\n/**\n * @deprecated Under active development and might change without notice.\n *\n * Headless bridge to the composer's text value and send action, for building a\n * custom composer input without `ComposerPrimitive.Input`. It is a thin bridge,\n * not a second input: it does not own keyboard behavior, autosize, IME or\n * contentEditable sync, paste/drop attachments, focus management, or rich-text\n * state. Spread `unstable_useTriggerPopoverAriaProps()` onto your element for\n * trigger-popover combobox semantics.\n *\n * @example\n * ```tsx\n * const { value, setText, send, isDisabled, canSend } = unstable_useComposerInput();\n * <textarea\n * value={value}\n * disabled={isDisabled}\n * onChange={(e) => setText(e.target.value)}\n * onKeyDown={(e) => {\n * if (e.key === \"Enter\" && !e.shiftKey && canSend) {\n * e.preventDefault();\n * send();\n * }\n * }}\n * />\n * ```\n */\nexport function unstable_useComposerInput(\n options?: Unstable_UseComposerInputOptions,\n): Unstable_ComposerInput {\n const aui = useAui();\n const value = useComposerInputValue();\n const isDisabled = useComposerInputDisabled(options?.disabled);\n\n const setText = useCallback(\n (text: string) => {\n if (!aui.composer().getState().isEditing) return;\n flushTapSync(() => {\n aui.composer().setText(text);\n });\n },\n [aui],\n );\n\n const { send: rawSend, disabled: sendDisabled } = useComposerSend();\n const canSend = !sendDisabled && !isDisabled;\n const send = useCallback(\n (sendOptions?: ComposerSendOptions) => {\n if (!canSend) return;\n rawSend(sendOptions);\n },\n [canSend, rawSend],\n );\n\n return { value, setText, send, isDisabled, canSend };\n}\n\nexport type Unstable_TriggerPopoverAriaProps = TriggerPopoverAriaProps;\n\n/**\n * @deprecated Under active development and might change without notice.\n *\n * ARIA combobox attributes for the focused element (typically the composer\n * input) describing the open trigger popover, per the WAI-ARIA editable\n * combobox pattern. Returns an empty object outside a `TriggerPopoverRoot` or\n * when no popover is open. Spread these last so they take precedence over any\n * matching ARIA props you set yourself, mirroring `ComposerPrimitive.Input`.\n *\n * @example\n * ```tsx\n * const aria = unstable_useTriggerPopoverAriaProps();\n * <textarea {...aria} />\n * ```\n */\nexport function unstable_useTriggerPopoverAriaProps(): Unstable_TriggerPopoverAriaProps {\n return useTriggerPopoverAriaProps();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EA,SAAgBmB,0BACdH,SACwB;CACxB,MAAMI,MAAMnB,OAAO;CACnB,MAAMW,QAAQL,sBAAsB;CACpC,MAAMU,aAAaX,yBAAyBU,SAASN,QAAQ;CAE7D,MAAMG,UAAUb,aACbc,SAAiB;EAChB,IAAI,CAACM,IAAIC,SAAS,CAAC,CAACC,SAAS,CAAC,CAACC,WAAW;EAC1CrB,mBAAmB;GACjBkB,IAAIC,SAAS,CAAC,CAACR,QAAQC,IAAI;EAC7B,CAAC;CACH,GACA,CAACM,GAAG,CACN;CAEA,MAAM,EAAEL,MAAMS,SAASd,UAAUe,iBAAiBtB,gBAAgB;CAClE,MAAMe,UAAU,CAACO,gBAAgB,CAACR;CASlC,OAAO;EAAEL;EAAOC;EAASE,MARZf,aACV0B,gBAAsC;GACrC,IAAI,CAACR,SAAS;GACdM,QAAQE,WAAW;EACrB,GACA,CAACR,SAASM,OAAO,CAGMT;EAAME;EAAYC;CAAQ;AACrD;;;;;;;;;;;;;;;;AAmBA,SAAgBU,sCAAwE;CACtF,OAAOpB,2BAA2B;AACpC"}
@@ -9,7 +9,7 @@ const useMediaQuery = (query) => {
9
9
  let t0;
10
10
  if ($[0] !== query) {
11
11
  t0 = (callback) => {
12
- if (typeof window === "undefined" || query === null) return noopUnsubscribe;
12
+ if (typeof window === "undefined" || query === null || !window.matchMedia) return noopUnsubscribe;
13
13
  const mql = window.matchMedia(query);
14
14
  mql.addEventListener("change", callback);
15
15
  return () => mql.removeEventListener("change", callback);
@@ -21,7 +21,7 @@ const useMediaQuery = (query) => {
21
21
  let t1;
22
22
  if ($[2] !== query) {
23
23
  t1 = () => {
24
- if (typeof window === "undefined" || query === null) return false;
24
+ if (typeof window === "undefined" || query === null || !window.matchMedia) return false;
25
25
  return window.matchMedia(query).matches;
26
26
  };
27
27
  $[2] = query;
@@ -1 +1 @@
1
- {"version":3,"file":"useMediaQuery.js","names":["c","_c","useCallback","useSyncExternalStore","getServerSnapshot","noopUnsubscribe","useMediaQuery","query","$","t0","callback","window","mql","matchMedia","addEventListener","removeEventListener","subscribe","t1","matches","getSnapshot"],"sources":["../../../src/utils/hooks/useMediaQuery.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useSyncExternalStore } from \"react\";\n\nconst getServerSnapshot = () => false;\nconst noopUnsubscribe = () => {};\n\nexport const useMediaQuery = (query: string | null): boolean => {\n const subscribe = useCallback(\n (callback: () => void) => {\n if (typeof window === \"undefined\" || query === null)\n return noopUnsubscribe;\n const mql = window.matchMedia(query);\n mql.addEventListener(\"change\", callback);\n return () => mql.removeEventListener(\"change\", callback);\n },\n [query],\n );\n\n const getSnapshot = useCallback(() => {\n if (typeof window === \"undefined\" || query === null) return false;\n return window.matchMedia(query).matches;\n }, [query]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n};\n"],"mappings":";;;;AAIA,MAAMI,0BAA0B;AAChC,MAAMC,wBAAwB,CAAC;AAE/B,MAAaC,iBAAgBC,UAAA;CAAA,MAAAC,IAAAP,EAAA,CAAA;CAAA,IAAAQ;CAAA,IAAAD,EAAA,OAAAD,OAAA;EAEzBE,MAAAC,aAAA;GACE,IAAI,OAAOC,WAAW,eAAeJ,UAAU,MAAI,OAC1CF;GACT,MAAAO,MAAYD,OAAME,WAAYN,KAAK;GACnCK,IAAGE,iBAAkB,UAAUJ,QAAQ;GAAC,aAC3BE,IAAGG,oBAAqB,UAAUL,QAAQ;EAAC;EACzDF,EAAA,KAAAD;EAAAC,EAAA,KAAAC;CAAA,OAAAA,KAAAD,EAAA;CAPH,MAAAQ,YAAkBP;CAShB,IAAAQ;CAAA,IAAAT,EAAA,OAAAD,OAAA;EAE8BU,WAAA;GAC9B,IAAI,OAAON,WAAW,eAAeJ,UAAU,MAAI,OAAS;GAAM,OAC3DI,OAAME,WAAYN,KAAK,CAAC,CAAAW;EAAQ;EACxCV,EAAA,KAAAD;EAAAC,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CAAW,OAELL,qBAAqBa,WAAWG,IAAaf,iBAAiB;AAAC"}
1
+ {"version":3,"file":"useMediaQuery.js","names":["c","_c","useCallback","useSyncExternalStore","getServerSnapshot","noopUnsubscribe","useMediaQuery","query","$","t0","callback","window","matchMedia","mql","addEventListener","removeEventListener","subscribe","t1","matches","getSnapshot"],"sources":["../../../src/utils/hooks/useMediaQuery.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useSyncExternalStore } from \"react\";\n\nconst getServerSnapshot = () => false;\nconst noopUnsubscribe = () => {};\n\nexport const useMediaQuery = (query: string | null): boolean => {\n const subscribe = useCallback(\n (callback: () => void) => {\n if (typeof window === \"undefined\" || query === null || !window.matchMedia)\n return noopUnsubscribe;\n const mql = window.matchMedia(query);\n mql.addEventListener(\"change\", callback);\n return () => mql.removeEventListener(\"change\", callback);\n },\n [query],\n );\n\n const getSnapshot = useCallback(() => {\n if (typeof window === \"undefined\" || query === null || !window.matchMedia)\n return false;\n return window.matchMedia(query).matches;\n }, [query]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n};\n"],"mappings":";;;;AAIA,MAAMI,0BAA0B;AAChC,MAAMC,wBAAwB,CAAC;AAE/B,MAAaC,iBAAgBC,UAAA;CAAA,MAAAC,IAAAP,EAAA,CAAA;CAAA,IAAAQ;CAAA,IAAAD,EAAA,OAAAD,OAAA;EAEzBE,MAAAC,aAAA;GACE,IAAI,OAAOC,WAAW,eAAeJ,UAAU,QAA3C,CAAoDI,OAAMC,YAAW,OAChEP;GACT,MAAAQ,MAAYF,OAAMC,WAAYL,KAAK;GACnCM,IAAGC,iBAAkB,UAAUJ,QAAQ;GAAC,aAC3BG,IAAGE,oBAAqB,UAAUL,QAAQ;EAAC;EACzDF,EAAA,KAAAD;EAAAC,EAAA,KAAAC;CAAA,OAAAA,KAAAD,EAAA;CAPH,MAAAQ,YAAkBP;CAShB,IAAAQ;CAAA,IAAAT,EAAA,OAAAD,OAAA;EAE8BU,WAAA;GAC9B,IAAI,OAAON,WAAW,eAAeJ,UAAU,QAA3C,CAAoDI,OAAMC,YAAW,OAChE;GAAM,OACRD,OAAMC,WAAYL,KAAK,CAAC,CAAAW;EAAQ;EACxCV,EAAA,KAAAD;EAAAC,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CAAW,OAELL,qBAAqBa,WAAWG,IAAaf,iBAAiB;AAAC"}
@@ -39,6 +39,10 @@ type SmoothOptions = {
39
39
  * the reveal. Returns the part state with `text` replaced by the revealed
40
40
  * prefix and `status` reporting `running` until the reveal catches up.
41
41
  *
42
+ * The reveal auto-disables under `prefers-reduced-motion: reduce`,
43
+ * committing the full text immediately; this takes precedence over an
44
+ * explicit `smooth={true}`.
45
+ *
42
46
  * @example
43
47
  * ```tsx
44
48
  * const { text, status } = useSmooth(useMessagePartText(), {
@@ -1 +1 @@
1
- {"version":3,"file":"useSmooth.d.ts","names":[],"sources":["../../../src/utils/smooth/useSmooth.ts"],"mappings":";;;;;AAiBA;KAAY,aAAA;;;;;;EAMV,OAAA;EAmBW;AAAA;AAsGb;;;EAnHE,iBAAA;EAoH2B;;;;EA/G3B,gBAAA;EAiHuC;;;;;;;EAzGvC,WAAA;AAAA;;;;;AAyG2D;;;;;;;;;;;;cAHhD,SAAA,GACX,KAAA,EAAO,gBAAA,IAAoB,eAAA,GAAkB,oBAAA,GAC7C,MAAA,aAAkB,aAAA,KACjB,gBAAA,IAAoB,eAAA,GAAkB,oBAAA"}
1
+ {"version":3,"file":"useSmooth.d.ts","names":[],"sources":["../../../src/utils/smooth/useSmooth.ts"],"mappings":";;;;;AAkBA;KAAY,aAAA;;;;;;EAMV,OAAA;EAmBW;AAAA;AA0Gb;;;EAvHE,iBAAA;EAwH2B;;;;EAnH3B,gBAAA;EAqHuC;;;;;;;EA7GvC,WAAA;AAAA;;;;;AA6G2D;;;;;;;;;;;;;;;;cAHhD,SAAA,GACX,KAAA,EAAO,gBAAA,IAAoB,eAAA,GAAkB,oBAAA,GAC7C,MAAA,aAAkB,aAAA,KACjB,gBAAA,IAAoB,eAAA,GAAkB,oBAAA"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { writableStore } from "../../context/ReadonlyStore.js";
3
+ import { useMediaQuery } from "../hooks/useMediaQuery.js";
3
4
  import { useSmoothStatusStore } from "./SmoothContext.js";
4
5
  import { useAui, useAuiState } from "@assistant-ui/store";
5
6
  import { useEffect, useMemo, useRef, useState } from "@assistant-ui/tap/react-shim";
@@ -66,6 +67,10 @@ const positiveOr = (value, fallback) => value !== void 0 && value > 0 ? value :
66
67
  * the reveal. Returns the part state with `text` replaced by the revealed
67
68
  * prefix and `status` reporting `running` until the reveal catches up.
68
69
  *
70
+ * The reveal auto-disables under `prefers-reduced-motion: reduce`,
71
+ * committing the full text immediately; this takes precedence over an
72
+ * explicit `smooth={true}`.
73
+ *
69
74
  * @example
70
75
  * ```tsx
71
76
  * const { text, status } = useSmooth(useMessagePartText(), {
@@ -76,8 +81,9 @@ const positiveOr = (value, fallback) => value !== void 0 && value > 0 ? value :
76
81
  */
77
82
  const useSmooth = (state, smooth = false) => {
78
83
  const { text } = state;
84
+ const reduceMotion = useMediaQuery("(prefers-reduced-motion: reduce)");
79
85
  const options = typeof smooth === "object" && smooth !== null ? smooth : void 0;
80
- const enabled = smooth !== false && smooth !== null;
86
+ const enabled = smooth !== false && smooth !== null && !reduceMotion;
81
87
  const drainMs = positiveOr(options?.drainMs, DEFAULT_DRAIN_MS);
82
88
  const maxCharIntervalMs = positiveOr(options?.maxCharIntervalMs, DEFAULT_MAX_CHAR_INTERVAL_MS);
83
89
  const maxCharsPerFrame = positiveOr(options?.maxCharsPerFrame, Infinity);
@@ -1 +1 @@
1
- {"version":3,"file":"useSmooth.js","names":["useEffect","useMemo","useRef","useState","useAui","useAuiState","MessagePartStatus","ReasoningMessagePart","TextMessagePart","MessagePartState","useCallbackRef","useSmoothStatusStore","writableStore","SmoothOptions","drainMs","maxCharIntervalMs","maxCharsPerFrame","minCommitMs","DEFAULT_DRAIN_MS","DEFAULT_MAX_CHAR_INTERVAL_MS","TextStreamAnimator","animationFrameId","lastUpdateTime","Date","now","lastCommitTime","targetText","Infinity","constructor","currentText","setText","newText","start","animate","stop","cancelAnimationFrame","currentTime","deltaTime","timeToConsume","remainingChars","length","baseTimePerChar","Math","min","frameLimit","charsToAdd","requestAnimationFrame","slice","isComplete","SMOOTH_STATUS","Object","freeze","type","positiveOr","value","fallback","undefined","useSmooth","state","smooth","text","options","enabled","displayedText","setDisplayedText","status","aui","part","prevPart","setPrevPart","startsWith","smoothStatusStore","optional","target","setState","animatorRef","animatorPartRef","partChanged","current"],"sources":["../../../src/utils/smooth/useSmooth.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport type {\n MessagePartStatus,\n ReasoningMessagePart,\n TextMessagePart,\n MessagePartState,\n} from \"@assistant-ui/core\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useSmoothStatusStore } from \"./SmoothContext\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\n\n/**\n * Tuning options for the smooth text streaming animation.\n */\nexport type SmoothOptions = {\n /**\n * Target time in milliseconds to drain the backlog of unrevealed\n * characters. Larger values reveal long backlogs more gradually.\n * @default 250\n */\n drainMs?: number | undefined;\n /**\n * Maximum time in milliseconds between revealed characters, i.e. the\n * slowest reveal rate when the backlog is short.\n * @default 5\n */\n maxCharIntervalMs?: number | undefined;\n /**\n * Maximum number of characters revealed per animation frame.\n * @default Infinity\n */\n maxCharsPerFrame?: number | undefined;\n /**\n * Minimum time in milliseconds between committed updates. The reveal keeps\n * advancing every frame, but the visible text (and the downstream re-render\n * and markdown re-parse it triggers) is committed at most once per interval.\n * The final frame always commits. `0` commits every frame.\n * @default 0\n */\n minCommitMs?: number | undefined;\n};\n\nconst DEFAULT_DRAIN_MS = 250;\nconst DEFAULT_MAX_CHAR_INTERVAL_MS = 5;\n\nclass TextStreamAnimator {\n private animationFrameId: number | null = null;\n private lastUpdateTime: number = Date.now();\n public lastCommitTime: number = 0;\n\n public targetText: string = \"\";\n public drainMs: number = DEFAULT_DRAIN_MS;\n public maxCharIntervalMs: number = DEFAULT_MAX_CHAR_INTERVAL_MS;\n public maxCharsPerFrame: number = Infinity;\n public minCommitMs: number = 0;\n\n constructor(\n public currentText: string,\n private setText: (newText: string) => void,\n ) {}\n\n start() {\n if (this.animationFrameId !== null) return;\n this.lastUpdateTime = Date.now();\n this.animate();\n }\n\n stop() {\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n }\n\n private animate = () => {\n const currentTime = Date.now();\n const deltaTime = currentTime - this.lastUpdateTime;\n let timeToConsume = deltaTime;\n\n const remainingChars = this.targetText.length - this.currentText.length;\n const baseTimePerChar = Math.min(\n this.maxCharIntervalMs,\n this.drainMs / remainingChars,\n );\n\n const frameLimit = Math.min(remainingChars, this.maxCharsPerFrame);\n let charsToAdd = 0;\n while (timeToConsume >= baseTimePerChar && charsToAdd < frameLimit) {\n charsToAdd++;\n timeToConsume -= baseTimePerChar;\n }\n // A cap-limited frame must not bank its surplus time, or the next\n // frame would burst past the cap.\n if (charsToAdd === frameLimit && frameLimit === this.maxCharsPerFrame) {\n timeToConsume = 0;\n }\n\n if (charsToAdd !== remainingChars) {\n this.animationFrameId = requestAnimationFrame(this.animate);\n } else {\n this.animationFrameId = null;\n }\n if (charsToAdd === 0) return;\n\n this.currentText = this.targetText.slice(\n 0,\n this.currentText.length + charsToAdd,\n );\n this.lastUpdateTime = currentTime - timeToConsume;\n\n const isComplete = charsToAdd === remainingChars;\n if (isComplete || currentTime - this.lastCommitTime >= this.minCommitMs) {\n this.lastCommitTime = currentTime;\n this.setText(this.currentText);\n }\n };\n}\n\nconst SMOOTH_STATUS: MessagePartStatus = Object.freeze({\n type: \"running\",\n});\n\nconst positiveOr = (value: number | undefined, fallback: number): number =>\n value !== undefined && value > 0 ? value : fallback;\n\n/**\n * Animates streamed message part text with a typewriter-style reveal.\n *\n * Takes the current part state and a `smooth` argument: `false` disables,\n * `true` uses the default rate, and a {@link SmoothOptions} object tunes\n * the reveal. Returns the part state with `text` replaced by the revealed\n * prefix and `status` reporting `running` until the reveal catches up.\n *\n * @example\n * ```tsx\n * const { text, status } = useSmooth(useMessagePartText(), {\n * drainMs: 500,\n * maxCharsPerFrame: 30,\n * });\n * ```\n */\nexport const useSmooth = (\n state: MessagePartState & (TextMessagePart | ReasoningMessagePart),\n smooth: boolean | SmoothOptions = false,\n): MessagePartState & (TextMessagePart | ReasoningMessagePart) => {\n const { text } = state;\n const options =\n typeof smooth === \"object\" && smooth !== null ? smooth : undefined;\n const enabled = smooth !== false && smooth !== null;\n const drainMs = positiveOr(options?.drainMs, DEFAULT_DRAIN_MS);\n const maxCharIntervalMs = positiveOr(\n options?.maxCharIntervalMs,\n DEFAULT_MAX_CHAR_INTERVAL_MS,\n );\n const maxCharsPerFrame = positiveOr(options?.maxCharsPerFrame, Infinity);\n const minCommitMs = positiveOr(options?.minCommitMs, 0);\n\n const [displayedText, setDisplayedText] = useState(\n state.status.type === \"running\" ? \"\" : text,\n );\n\n // Render-phase resync on part flip or text discontinuity, so the\n // first paint after a thread switch never shows the previous\n // part's text (#4051). `displayedText` is already a prefix of\n // `text` during normal streaming, so use it as the previous-text\n // reference instead of carrying separate state — avoids the\n // double render per streaming token. Read part identity through\n // `useAuiState` so we actually subscribe to its changes instead\n // of relying on a render-time proxy reference that may be stable\n // across thread swaps.\n const aui = useAui();\n const part = useAuiState(() => aui.part());\n const [prevPart, setPrevPart] = useState(part);\n if (part !== prevPart || !text.startsWith(displayedText)) {\n setPrevPart(part);\n setDisplayedText(state.status.type === \"running\" ? \"\" : text);\n }\n\n const smoothStatusStore = useSmoothStatusStore({ optional: true });\n const setText = useCallbackRef((text: string) => {\n setDisplayedText(text);\n if (smoothStatusStore) {\n const target =\n displayedText !== text || state.status.type === \"running\"\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n });\n\n // TODO this is hacky\n useEffect(() => {\n if (smoothStatusStore) {\n const target =\n enabled && (displayedText !== text || state.status.type === \"running\")\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n }, [smoothStatusStore, enabled, text, displayedText, state.status]);\n\n const [animatorRef] = useState<TextStreamAnimator>(\n new TextStreamAnimator(displayedText, setText),\n );\n\n useEffect(() => {\n animatorRef.drainMs = drainMs;\n animatorRef.maxCharIntervalMs = maxCharIntervalMs;\n animatorRef.maxCharsPerFrame = maxCharsPerFrame;\n animatorRef.minCommitMs = minCommitMs;\n }, [animatorRef, drainMs, maxCharIntervalMs, maxCharsPerFrame, minCommitMs]);\n\n const animatorPartRef = useRef(part);\n useEffect(() => {\n if (!enabled) {\n animatorRef.stop();\n return;\n }\n\n // Discontinuity: part flipped, or new text breaks continuation\n // of the animator's current target. Either case requires\n // resetting the cursor — without the part check, a new part\n // whose text happens to share a prefix with the previous target\n // would keep the stale cursor and flicker.\n const partChanged = animatorPartRef.current !== part;\n animatorPartRef.current = part;\n if (partChanged || !text.startsWith(animatorRef.targetText)) {\n if (state.status.type === \"running\") {\n animatorRef.currentText = \"\";\n animatorRef.targetText = text;\n animatorRef.lastCommitTime = 0;\n animatorRef.start();\n } else {\n animatorRef.currentText = text;\n animatorRef.targetText = text;\n animatorRef.stop();\n }\n return;\n }\n\n animatorRef.targetText = text;\n animatorRef.start();\n }, [animatorRef, enabled, text, state.status.type, part]);\n\n useEffect(() => {\n return () => {\n animatorRef.stop();\n };\n }, [animatorRef]);\n\n return useMemo(\n () =>\n enabled\n ? {\n ...state,\n text: displayedText,\n status: text === displayedText ? state.status : SMOOTH_STATUS,\n }\n : state,\n [enabled, displayedText, state, text],\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"useSmooth.js","names":["useEffect","useMemo","useRef","useState","useAui","useAuiState","MessagePartStatus","ReasoningMessagePart","TextMessagePart","MessagePartState","useCallbackRef","useMediaQuery","useSmoothStatusStore","writableStore","SmoothOptions","drainMs","maxCharIntervalMs","maxCharsPerFrame","minCommitMs","DEFAULT_DRAIN_MS","DEFAULT_MAX_CHAR_INTERVAL_MS","TextStreamAnimator","animationFrameId","lastUpdateTime","Date","now","lastCommitTime","targetText","Infinity","constructor","currentText","setText","newText","start","animate","stop","cancelAnimationFrame","currentTime","deltaTime","timeToConsume","remainingChars","length","baseTimePerChar","Math","min","frameLimit","charsToAdd","requestAnimationFrame","slice","isComplete","SMOOTH_STATUS","Object","freeze","type","positiveOr","value","fallback","undefined","useSmooth","state","smooth","text","reduceMotion","options","enabled","displayedText","setDisplayedText","status","aui","part","prevPart","setPrevPart","startsWith","smoothStatusStore","optional","target","setState","animatorRef","animatorPartRef","partChanged","current"],"sources":["../../../src/utils/smooth/useSmooth.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport type {\n MessagePartStatus,\n ReasoningMessagePart,\n TextMessagePart,\n MessagePartState,\n} from \"@assistant-ui/core\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useMediaQuery } from \"../hooks/useMediaQuery\";\nimport { useSmoothStatusStore } from \"./SmoothContext\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\n\n/**\n * Tuning options for the smooth text streaming animation.\n */\nexport type SmoothOptions = {\n /**\n * Target time in milliseconds to drain the backlog of unrevealed\n * characters. Larger values reveal long backlogs more gradually.\n * @default 250\n */\n drainMs?: number | undefined;\n /**\n * Maximum time in milliseconds between revealed characters, i.e. the\n * slowest reveal rate when the backlog is short.\n * @default 5\n */\n maxCharIntervalMs?: number | undefined;\n /**\n * Maximum number of characters revealed per animation frame.\n * @default Infinity\n */\n maxCharsPerFrame?: number | undefined;\n /**\n * Minimum time in milliseconds between committed updates. The reveal keeps\n * advancing every frame, but the visible text (and the downstream re-render\n * and markdown re-parse it triggers) is committed at most once per interval.\n * The final frame always commits. `0` commits every frame.\n * @default 0\n */\n minCommitMs?: number | undefined;\n};\n\nconst DEFAULT_DRAIN_MS = 250;\nconst DEFAULT_MAX_CHAR_INTERVAL_MS = 5;\n\nclass TextStreamAnimator {\n private animationFrameId: number | null = null;\n private lastUpdateTime: number = Date.now();\n public lastCommitTime: number = 0;\n\n public targetText: string = \"\";\n public drainMs: number = DEFAULT_DRAIN_MS;\n public maxCharIntervalMs: number = DEFAULT_MAX_CHAR_INTERVAL_MS;\n public maxCharsPerFrame: number = Infinity;\n public minCommitMs: number = 0;\n\n constructor(\n public currentText: string,\n private setText: (newText: string) => void,\n ) {}\n\n start() {\n if (this.animationFrameId !== null) return;\n this.lastUpdateTime = Date.now();\n this.animate();\n }\n\n stop() {\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n }\n\n private animate = () => {\n const currentTime = Date.now();\n const deltaTime = currentTime - this.lastUpdateTime;\n let timeToConsume = deltaTime;\n\n const remainingChars = this.targetText.length - this.currentText.length;\n const baseTimePerChar = Math.min(\n this.maxCharIntervalMs,\n this.drainMs / remainingChars,\n );\n\n const frameLimit = Math.min(remainingChars, this.maxCharsPerFrame);\n let charsToAdd = 0;\n while (timeToConsume >= baseTimePerChar && charsToAdd < frameLimit) {\n charsToAdd++;\n timeToConsume -= baseTimePerChar;\n }\n // A cap-limited frame must not bank its surplus time, or the next\n // frame would burst past the cap.\n if (charsToAdd === frameLimit && frameLimit === this.maxCharsPerFrame) {\n timeToConsume = 0;\n }\n\n if (charsToAdd !== remainingChars) {\n this.animationFrameId = requestAnimationFrame(this.animate);\n } else {\n this.animationFrameId = null;\n }\n if (charsToAdd === 0) return;\n\n this.currentText = this.targetText.slice(\n 0,\n this.currentText.length + charsToAdd,\n );\n this.lastUpdateTime = currentTime - timeToConsume;\n\n const isComplete = charsToAdd === remainingChars;\n if (isComplete || currentTime - this.lastCommitTime >= this.minCommitMs) {\n this.lastCommitTime = currentTime;\n this.setText(this.currentText);\n }\n };\n}\n\nconst SMOOTH_STATUS: MessagePartStatus = Object.freeze({\n type: \"running\",\n});\n\nconst positiveOr = (value: number | undefined, fallback: number): number =>\n value !== undefined && value > 0 ? value : fallback;\n\n/**\n * Animates streamed message part text with a typewriter-style reveal.\n *\n * Takes the current part state and a `smooth` argument: `false` disables,\n * `true` uses the default rate, and a {@link SmoothOptions} object tunes\n * the reveal. Returns the part state with `text` replaced by the revealed\n * prefix and `status` reporting `running` until the reveal catches up.\n *\n * The reveal auto-disables under `prefers-reduced-motion: reduce`,\n * committing the full text immediately; this takes precedence over an\n * explicit `smooth={true}`.\n *\n * @example\n * ```tsx\n * const { text, status } = useSmooth(useMessagePartText(), {\n * drainMs: 500,\n * maxCharsPerFrame: 30,\n * });\n * ```\n */\nexport const useSmooth = (\n state: MessagePartState & (TextMessagePart | ReasoningMessagePart),\n smooth: boolean | SmoothOptions = false,\n): MessagePartState & (TextMessagePart | ReasoningMessagePart) => {\n const { text } = state;\n const reduceMotion = useMediaQuery(\"(prefers-reduced-motion: reduce)\");\n const options =\n typeof smooth === \"object\" && smooth !== null ? smooth : undefined;\n const enabled = smooth !== false && smooth !== null && !reduceMotion;\n const drainMs = positiveOr(options?.drainMs, DEFAULT_DRAIN_MS);\n const maxCharIntervalMs = positiveOr(\n options?.maxCharIntervalMs,\n DEFAULT_MAX_CHAR_INTERVAL_MS,\n );\n const maxCharsPerFrame = positiveOr(options?.maxCharsPerFrame, Infinity);\n const minCommitMs = positiveOr(options?.minCommitMs, 0);\n\n const [displayedText, setDisplayedText] = useState(\n state.status.type === \"running\" ? \"\" : text,\n );\n\n // Render-phase resync on part flip or text discontinuity, so the\n // first paint after a thread switch never shows the previous\n // part's text (#4051). `displayedText` is already a prefix of\n // `text` during normal streaming, so use it as the previous-text\n // reference instead of carrying separate state — avoids the\n // double render per streaming token. Read part identity through\n // `useAuiState` so we actually subscribe to its changes instead\n // of relying on a render-time proxy reference that may be stable\n // across thread swaps.\n const aui = useAui();\n const part = useAuiState(() => aui.part());\n const [prevPart, setPrevPart] = useState(part);\n if (part !== prevPart || !text.startsWith(displayedText)) {\n setPrevPart(part);\n setDisplayedText(state.status.type === \"running\" ? \"\" : text);\n }\n\n const smoothStatusStore = useSmoothStatusStore({ optional: true });\n const setText = useCallbackRef((text: string) => {\n setDisplayedText(text);\n if (smoothStatusStore) {\n const target =\n displayedText !== text || state.status.type === \"running\"\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n });\n\n // TODO this is hacky\n useEffect(() => {\n if (smoothStatusStore) {\n const target =\n enabled && (displayedText !== text || state.status.type === \"running\")\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n }, [smoothStatusStore, enabled, text, displayedText, state.status]);\n\n const [animatorRef] = useState<TextStreamAnimator>(\n new TextStreamAnimator(displayedText, setText),\n );\n\n useEffect(() => {\n animatorRef.drainMs = drainMs;\n animatorRef.maxCharIntervalMs = maxCharIntervalMs;\n animatorRef.maxCharsPerFrame = maxCharsPerFrame;\n animatorRef.minCommitMs = minCommitMs;\n }, [animatorRef, drainMs, maxCharIntervalMs, maxCharsPerFrame, minCommitMs]);\n\n const animatorPartRef = useRef(part);\n useEffect(() => {\n if (!enabled) {\n animatorRef.stop();\n return;\n }\n\n // Discontinuity: part flipped, or new text breaks continuation\n // of the animator's current target. Either case requires\n // resetting the cursor — without the part check, a new part\n // whose text happens to share a prefix with the previous target\n // would keep the stale cursor and flicker.\n const partChanged = animatorPartRef.current !== part;\n animatorPartRef.current = part;\n if (partChanged || !text.startsWith(animatorRef.targetText)) {\n if (state.status.type === \"running\") {\n animatorRef.currentText = \"\";\n animatorRef.targetText = text;\n animatorRef.lastCommitTime = 0;\n animatorRef.start();\n } else {\n animatorRef.currentText = text;\n animatorRef.targetText = text;\n animatorRef.stop();\n }\n return;\n }\n\n animatorRef.targetText = text;\n animatorRef.start();\n }, [animatorRef, enabled, text, state.status.type, part]);\n\n useEffect(() => {\n return () => {\n animatorRef.stop();\n };\n }, [animatorRef]);\n\n return useMemo(\n () =>\n enabled\n ? {\n ...state,\n text: displayedText,\n status: text === displayedText ? state.status : SMOOTH_STATUS,\n }\n : state,\n [enabled, displayedText, state, text],\n );\n};\n"],"mappings":"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"}