@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.
- package/dist/index.d.ts +4 -3
- package/dist/index.js +4 -3
- package/dist/legacy-runtime/hooks/AttachmentContext.d.ts +12 -12
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts +3 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +2 -2
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +6 -2
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +5 -14
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/useComposerInputState.d.ts +13 -0
- package/dist/primitives/composer/useComposerInputState.d.ts.map +1 -0
- package/dist/primitives/composer/useComposerInputState.js +47 -0
- package/dist/primitives/composer/useComposerInputState.js.map +1 -0
- package/dist/primitives/messagePart/MessagePartText.d.ts +2 -0
- package/dist/primitives/messagePart/MessagePartText.d.ts.map +1 -1
- package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
- package/dist/primitives/thread/ThreadMessages.d.ts +2 -2
- package/dist/primitives/thread/ThreadMessages.js +2 -2
- package/dist/primitives/thread.d.ts +3 -3
- package/dist/primitives/thread.js +3 -2
- package/dist/primitives/thread.js.map +1 -1
- package/dist/primitives/threadList/ThreadListRoot.d.ts.map +1 -1
- package/dist/primitives/threadList/ThreadListRoot.js +10 -3
- package/dist/primitives/threadList/ThreadListRoot.js.map +1 -1
- package/dist/primitives/threadListFocusGroup.d.ts +26 -0
- package/dist/primitives/threadListFocusGroup.d.ts.map +1 -0
- package/dist/primitives/threadListFocusGroup.js +15 -0
- package/dist/primitives/threadListFocusGroup.js.map +1 -0
- package/dist/primitives/threadListItem/ThreadListItemRoot.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemRoot.js +45 -27
- package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemTrigger.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemTrigger.js +26 -2
- package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.d.ts.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js +63 -38
- package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.d.ts +20 -3
- package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.d.ts.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js +95 -14
- package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.d.ts.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js +38 -21
- package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js.map +1 -1
- package/dist/unstable/useComposerInput.d.ts +84 -0
- package/dist/unstable/useComposerInput.d.ts.map +1 -0
- package/dist/unstable/useComposerInput.js +78 -0
- package/dist/unstable/useComposerInput.js.map +1 -0
- package/dist/utils/hooks/useMediaQuery.js +2 -2
- package/dist/utils/hooks/useMediaQuery.js.map +1 -1
- package/dist/utils/smooth/useSmooth.d.ts +4 -0
- package/dist/utils/smooth/useSmooth.d.ts.map +1 -1
- package/dist/utils/smooth/useSmooth.js +7 -1
- package/dist/utils/smooth/useSmooth.js.map +1 -1
- package/dist/utils/useToolArgsFieldStatus.d.ts +2 -2
- package/dist/utils/useToolArgsFieldStatus.d.ts.map +1 -1
- package/package.json +16 -14
- package/src/index.ts +39 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/commandQueue.ts +5 -2
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-scheduling.test.ts +22 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +12 -2
- package/src/primitives/composer/ComposerInput.tsx +9 -20
- package/src/primitives/composer/trigger/triggerKeyboardResource.test.ts +18 -22
- package/src/primitives/composer/useComposerInputState.ts +34 -0
- package/src/primitives/messagePart/MessagePartText.tsx +1 -0
- package/src/primitives/thread/ThreadMessages.tsx +1 -0
- package/src/primitives/thread.ts +1 -0
- package/src/primitives/threadList/ThreadListRoot.tsx +8 -1
- package/src/primitives/threadListFocusGroup.ts +24 -0
- package/src/primitives/threadListItem/ThreadListItemRoot.tsx +53 -5
- package/src/primitives/threadListItem/ThreadListItemTrigger.tsx +45 -0
- package/src/primitives/threadListItem/ThreadListKeyboardNav.test.tsx +141 -0
- package/src/primitives/threadListItemMore/ThreadListItemMoreContent.tsx +22 -1
- package/src/primitives/threadListItemMore/ThreadListItemMoreRoot.tsx +64 -3
- package/src/primitives/threadListItemMore/ThreadListItemMoreTrigger.tsx +29 -2
- package/src/tests/generative-ui.test.tsx +21 -1
- package/src/tests/threadMessageById.test.tsx +224 -0
- package/src/unstable/useComposerInput.test.tsx +187 -0
- package/src/unstable/useComposerInput.ts +126 -0
- package/src/utils/hooks/useMediaQuery.ts +3 -2
- package/src/utils/smooth/useSmooth.test.tsx +26 -0
- package/src/utils/smooth/useSmooth.ts +7 -1
- 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":";;;;
|
|
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(
|
|
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] =
|
|
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
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
$[
|
|
28
|
-
$[
|
|
29
|
-
$[
|
|
30
|
-
} else
|
|
31
|
-
return
|
|
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","
|
|
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":";;;;;
|
|
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(
|
|
10
|
-
|
|
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 ($[
|
|
24
|
-
t1 =
|
|
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
|
-
|
|
41
|
+
onKeyDown: t2,
|
|
42
|
+
ref: composedRef
|
|
28
43
|
});
|
|
29
|
-
$[
|
|
30
|
-
$[
|
|
31
|
-
$[
|
|
32
|
-
$[
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
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","
|
|
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":";;;;;
|
|
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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"}
|