@assistant-ui/react 0.14.23 → 0.14.24
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/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/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/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/package.json +13 -13
- 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/thread/ThreadMessages.tsx +1 -0
- package/src/primitives/thread.ts +1 -0
- package/src/tests/threadMessageById.test.tsx +224 -0
- package/src/unstable/useComposerInput.test.tsx +187 -0
- package/src/unstable/useComposerInput.ts +126 -0
|
@@ -4,15 +4,15 @@ import { ThreadPrimitiveEmpty } from "./thread/ThreadEmpty.js";
|
|
|
4
4
|
import { ThreadPrimitiveIf } from "./thread/ThreadIf.js";
|
|
5
5
|
import { ThreadPrimitiveViewport } from "./thread/ThreadViewport.js";
|
|
6
6
|
import { ThreadPrimitiveViewportFooter } from "./thread/ThreadViewportFooter.js";
|
|
7
|
-
import { ThreadPrimitiveMessageByIndex, ThreadPrimitiveMessages } from "./thread/ThreadMessages.js";
|
|
7
|
+
import { ThreadPrimitiveMessageByIndex, ThreadPrimitiveMessages, ThreadPrimitiveUnstable_MessageById } from "./thread/ThreadMessages.js";
|
|
8
8
|
import { ThreadPrimitiveScrollToBottom } from "./thread/ThreadScrollToBottom.js";
|
|
9
9
|
import { ThreadPrimitiveSuggestion } from "./thread/ThreadSuggestion.js";
|
|
10
10
|
import { ThreadPrimitiveSuggestionByIndex, ThreadPrimitiveSuggestions } from "./thread/ThreadSuggestions.js";
|
|
11
11
|
|
|
12
12
|
//#region src/primitives/thread.d.ts
|
|
13
13
|
declare namespace thread_d_exports {
|
|
14
|
-
export { ThreadPrimitiveEmpty as Empty, ThreadPrimitiveIf as If, ThreadPrimitiveMessageByIndex as MessageByIndex, ThreadPrimitiveMessages as Messages, ThreadPrimitiveRoot as Root, ThreadPrimitiveScrollToBottom as ScrollToBottom, ThreadPrimitiveSuggestion as Suggestion, ThreadPrimitiveSuggestionByIndex as SuggestionByIndex, ThreadPrimitiveSuggestions as Suggestions, ThreadPrimitiveViewport as Viewport, ThreadPrimitiveViewportFooter as ViewportFooter, ThreadPrimitiveViewportProvider as ViewportProvider };
|
|
14
|
+
export { ThreadPrimitiveEmpty as Empty, ThreadPrimitiveIf as If, ThreadPrimitiveMessageByIndex as MessageByIndex, ThreadPrimitiveMessages as Messages, ThreadPrimitiveRoot as Root, ThreadPrimitiveScrollToBottom as ScrollToBottom, ThreadPrimitiveSuggestion as Suggestion, ThreadPrimitiveSuggestionByIndex as SuggestionByIndex, ThreadPrimitiveSuggestions as Suggestions, ThreadPrimitiveUnstable_MessageById as Unstable_MessageById, ThreadPrimitiveViewport as Viewport, ThreadPrimitiveViewportFooter as ViewportFooter, ThreadPrimitiveViewportProvider as ViewportProvider };
|
|
15
15
|
}
|
|
16
16
|
//#endregion
|
|
17
|
-
export { ThreadPrimitiveEmpty as Empty, ThreadPrimitiveIf as If, ThreadPrimitiveMessageByIndex as MessageByIndex, ThreadPrimitiveMessages as Messages, ThreadPrimitiveRoot as Root, ThreadPrimitiveScrollToBottom as ScrollToBottom, ThreadPrimitiveSuggestion as Suggestion, ThreadPrimitiveSuggestionByIndex as SuggestionByIndex, ThreadPrimitiveSuggestions as Suggestions, ThreadPrimitiveViewport as Viewport, ThreadPrimitiveViewportFooter as ViewportFooter, ThreadPrimitiveViewportProvider as ViewportProvider, thread_d_exports };
|
|
17
|
+
export { ThreadPrimitiveEmpty as Empty, ThreadPrimitiveIf as If, ThreadPrimitiveMessageByIndex as MessageByIndex, ThreadPrimitiveMessages as Messages, ThreadPrimitiveRoot as Root, ThreadPrimitiveScrollToBottom as ScrollToBottom, ThreadPrimitiveSuggestion as Suggestion, ThreadPrimitiveSuggestionByIndex as SuggestionByIndex, ThreadPrimitiveSuggestions as Suggestions, ThreadPrimitiveUnstable_MessageById as Unstable_MessageById, ThreadPrimitiveViewport as Viewport, ThreadPrimitiveViewportFooter as ViewportFooter, ThreadPrimitiveViewportProvider as ViewportProvider, thread_d_exports };
|
|
18
18
|
//# sourceMappingURL=thread.d.ts.map
|
|
@@ -5,7 +5,7 @@ import { ThreadPrimitiveEmpty } from "./thread/ThreadEmpty.js";
|
|
|
5
5
|
import { ThreadPrimitiveIf } from "./thread/ThreadIf.js";
|
|
6
6
|
import { ThreadPrimitiveViewport } from "./thread/ThreadViewport.js";
|
|
7
7
|
import { ThreadPrimitiveViewportFooter } from "./thread/ThreadViewportFooter.js";
|
|
8
|
-
import { ThreadPrimitiveMessageByIndex, ThreadPrimitiveMessages } from "./thread/ThreadMessages.js";
|
|
8
|
+
import { ThreadPrimitiveMessageByIndex, ThreadPrimitiveMessages, ThreadPrimitiveUnstable_MessageById } from "./thread/ThreadMessages.js";
|
|
9
9
|
import { ThreadPrimitiveScrollToBottom } from "./thread/ThreadScrollToBottom.js";
|
|
10
10
|
import { ThreadPrimitiveSuggestion } from "./thread/ThreadSuggestion.js";
|
|
11
11
|
import { ThreadPrimitiveSuggestionByIndex, ThreadPrimitiveSuggestions } from "./thread/ThreadSuggestions.js";
|
|
@@ -20,11 +20,12 @@ var thread_exports = /* @__PURE__ */ __exportAll({
|
|
|
20
20
|
Suggestion: () => ThreadPrimitiveSuggestion,
|
|
21
21
|
SuggestionByIndex: () => ThreadPrimitiveSuggestionByIndex,
|
|
22
22
|
Suggestions: () => ThreadPrimitiveSuggestions,
|
|
23
|
+
Unstable_MessageById: () => ThreadPrimitiveUnstable_MessageById,
|
|
23
24
|
Viewport: () => ThreadPrimitiveViewport,
|
|
24
25
|
ViewportFooter: () => ThreadPrimitiveViewportFooter,
|
|
25
26
|
ViewportProvider: () => ThreadPrimitiveViewportProvider
|
|
26
27
|
});
|
|
27
28
|
//#endregion
|
|
28
|
-
export { ThreadPrimitiveEmpty as Empty, ThreadPrimitiveIf as If, ThreadPrimitiveMessageByIndex as MessageByIndex, ThreadPrimitiveMessages as Messages, ThreadPrimitiveRoot as Root, ThreadPrimitiveScrollToBottom as ScrollToBottom, ThreadPrimitiveSuggestion as Suggestion, ThreadPrimitiveSuggestionByIndex as SuggestionByIndex, ThreadPrimitiveSuggestions as Suggestions, ThreadPrimitiveViewport as Viewport, ThreadPrimitiveViewportFooter as ViewportFooter, ThreadPrimitiveViewportProvider as ViewportProvider, thread_exports };
|
|
29
|
+
export { ThreadPrimitiveEmpty as Empty, ThreadPrimitiveIf as If, ThreadPrimitiveMessageByIndex as MessageByIndex, ThreadPrimitiveMessages as Messages, ThreadPrimitiveRoot as Root, ThreadPrimitiveScrollToBottom as ScrollToBottom, ThreadPrimitiveSuggestion as Suggestion, ThreadPrimitiveSuggestionByIndex as SuggestionByIndex, ThreadPrimitiveSuggestions as Suggestions, ThreadPrimitiveUnstable_MessageById as Unstable_MessageById, ThreadPrimitiveViewport as Viewport, ThreadPrimitiveViewportFooter as ViewportFooter, ThreadPrimitiveViewportProvider as ViewportProvider, thread_exports };
|
|
29
30
|
|
|
30
31
|
//# sourceMappingURL=thread.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread.js","names":["ThreadPrimitiveRoot","Root","ThreadPrimitiveEmpty","Empty","ThreadPrimitiveIf","If","ThreadPrimitiveViewport","Viewport","ThreadPrimitiveViewportProvider","ViewportProvider","ThreadPrimitiveViewportFooter","ViewportFooter","ThreadPrimitiveMessages","Messages","ThreadPrimitiveMessageByIndex","MessageByIndex","ThreadPrimitiveScrollToBottom","ScrollToBottom","ThreadPrimitiveSuggestion","Suggestion","ThreadPrimitiveSuggestions","Suggestions","ThreadPrimitiveSuggestionByIndex","SuggestionByIndex"],"sources":["../../src/primitives/thread.ts"],"sourcesContent":["export { ThreadPrimitiveRoot as Root } from \"./thread/ThreadRoot\";\nexport { ThreadPrimitiveEmpty as Empty } from \"./thread/ThreadEmpty\";\nexport { ThreadPrimitiveIf as If } from \"./thread/ThreadIf\";\nexport { ThreadPrimitiveViewport as Viewport } from \"./thread/ThreadViewport\";\nexport { ThreadPrimitiveViewportProvider as ViewportProvider } from \"../context/providers/ThreadViewportProvider\";\nexport { ThreadPrimitiveViewportFooter as ViewportFooter } from \"./thread/ThreadViewportFooter\";\nexport { ThreadPrimitiveMessages as Messages } from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveMessageByIndex as MessageByIndex } from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveScrollToBottom as ScrollToBottom } from \"./thread/ThreadScrollToBottom\";\nexport { ThreadPrimitiveSuggestion as Suggestion } from \"./thread/ThreadSuggestion\";\nexport {\n ThreadPrimitiveSuggestions as Suggestions,\n ThreadPrimitiveSuggestionByIndex as SuggestionByIndex,\n} from \"./thread/ThreadSuggestions\";\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"thread.js","names":["ThreadPrimitiveRoot","Root","ThreadPrimitiveEmpty","Empty","ThreadPrimitiveIf","If","ThreadPrimitiveViewport","Viewport","ThreadPrimitiveViewportProvider","ViewportProvider","ThreadPrimitiveViewportFooter","ViewportFooter","ThreadPrimitiveMessages","Messages","ThreadPrimitiveMessageByIndex","MessageByIndex","ThreadPrimitiveUnstable_MessageById","Unstable_MessageById","ThreadPrimitiveScrollToBottom","ScrollToBottom","ThreadPrimitiveSuggestion","Suggestion","ThreadPrimitiveSuggestions","Suggestions","ThreadPrimitiveSuggestionByIndex","SuggestionByIndex"],"sources":["../../src/primitives/thread.ts"],"sourcesContent":["export { ThreadPrimitiveRoot as Root } from \"./thread/ThreadRoot\";\nexport { ThreadPrimitiveEmpty as Empty } from \"./thread/ThreadEmpty\";\nexport { ThreadPrimitiveIf as If } from \"./thread/ThreadIf\";\nexport { ThreadPrimitiveViewport as Viewport } from \"./thread/ThreadViewport\";\nexport { ThreadPrimitiveViewportProvider as ViewportProvider } from \"../context/providers/ThreadViewportProvider\";\nexport { ThreadPrimitiveViewportFooter as ViewportFooter } from \"./thread/ThreadViewportFooter\";\nexport { ThreadPrimitiveMessages as Messages } from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveMessageByIndex as MessageByIndex } from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveUnstable_MessageById as Unstable_MessageById } from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveScrollToBottom as ScrollToBottom } from \"./thread/ThreadScrollToBottom\";\nexport { ThreadPrimitiveSuggestion as Suggestion } from \"./thread/ThreadSuggestion\";\nexport {\n ThreadPrimitiveSuggestions as Suggestions,\n ThreadPrimitiveSuggestionByIndex as SuggestionByIndex,\n} from \"./thread/ThreadSuggestions\";\n"],"mappings":""}
|
|
@@ -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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.24",
|
|
4
4
|
"description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -55,19 +55,19 @@
|
|
|
55
55
|
],
|
|
56
56
|
"sideEffects": false,
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@assistant-ui/core": "^0.2.
|
|
59
|
-
"@assistant-ui/store": "^0.2.
|
|
60
|
-
"@assistant-ui/tap": "^0.9.
|
|
58
|
+
"@assistant-ui/core": "^0.2.19",
|
|
59
|
+
"@assistant-ui/store": "^0.2.19",
|
|
60
|
+
"@assistant-ui/tap": "^0.9.3",
|
|
61
61
|
"@radix-ui/primitive": "^1.1.4",
|
|
62
62
|
"@radix-ui/react-compose-refs": "^1.1.3",
|
|
63
63
|
"@radix-ui/react-context": "^1.1.4",
|
|
64
|
-
"@radix-ui/react-primitive": "^2.1.
|
|
64
|
+
"@radix-ui/react-primitive": "^2.1.6",
|
|
65
65
|
"@radix-ui/react-use-callback-ref": "^1.1.2",
|
|
66
66
|
"@radix-ui/react-use-escape-keydown": "^1.1.2",
|
|
67
|
-
"assistant-cloud": "^0.1.
|
|
68
|
-
"assistant-stream": "^0.3.
|
|
69
|
-
"nanoid": "^5.1.
|
|
70
|
-
"radix-ui": "^1.
|
|
67
|
+
"assistant-cloud": "^0.1.34",
|
|
68
|
+
"assistant-stream": "^0.3.24",
|
|
69
|
+
"nanoid": "^5.1.15",
|
|
70
|
+
"radix-ui": "^1.6.0",
|
|
71
71
|
"react-textarea-autosize": "^8.5.9",
|
|
72
72
|
"safe-content-frame": "^0.0.21",
|
|
73
73
|
"zod": "^4.4.3",
|
|
@@ -90,15 +90,15 @@
|
|
|
90
90
|
"devDependencies": {
|
|
91
91
|
"@testing-library/react": "^16.3.2",
|
|
92
92
|
"@types/json-schema": "^7.0.15",
|
|
93
|
-
"@types/node": "^
|
|
93
|
+
"@types/node": "^26.0.0",
|
|
94
94
|
"@types/react": "^19.2.17",
|
|
95
95
|
"@types/react-dom": "^19.2.3",
|
|
96
96
|
"jsdom": "^29.1.1",
|
|
97
97
|
"react": "^19.2.7",
|
|
98
98
|
"react-dom": "^19.2.7",
|
|
99
|
-
"vitest": "^4.1.
|
|
100
|
-
"@assistant-ui/vite": "0.0.
|
|
101
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
99
|
+
"vitest": "^4.1.9",
|
|
100
|
+
"@assistant-ui/vite": "0.0.6",
|
|
101
|
+
"@assistant-ui/x-buildutils": "0.0.16"
|
|
102
102
|
},
|
|
103
103
|
"publishConfig": {
|
|
104
104
|
"access": "public",
|
package/src/index.ts
CHANGED
|
@@ -136,6 +136,7 @@ export type {
|
|
|
136
136
|
ExternalStoreSharedOptions,
|
|
137
137
|
ExternalStoreThreadListAdapter,
|
|
138
138
|
ExternalStoreThreadData,
|
|
139
|
+
ExternalStoreBranchChange,
|
|
139
140
|
} from "@assistant-ui/core";
|
|
140
141
|
export {
|
|
141
142
|
createMessageQueue,
|
|
@@ -215,6 +216,26 @@ export {
|
|
|
215
216
|
useAssistantInteractable,
|
|
216
217
|
type AssistantInteractableProps,
|
|
217
218
|
useInteractableState,
|
|
219
|
+
unstable_Interactables,
|
|
220
|
+
unstable_useInteractable,
|
|
221
|
+
type Unstable_InteractableConfig,
|
|
222
|
+
type Unstable_InferInteractableState,
|
|
223
|
+
type Unstable_InteractableVersionInfo,
|
|
224
|
+
unstable_useInteractableState,
|
|
225
|
+
unstable_useInteractableVersions,
|
|
226
|
+
unstable_interactableTool,
|
|
227
|
+
type Unstable_InteractableToolConfig,
|
|
228
|
+
type Unstable_InteractableToolRenderProps,
|
|
229
|
+
type Unstable_InteractableStateSchema,
|
|
230
|
+
type Unstable_InteractablesState,
|
|
231
|
+
type Unstable_InteractableDefinition,
|
|
232
|
+
type Unstable_InteractableRegistration,
|
|
233
|
+
type Unstable_InteractablesMethods,
|
|
234
|
+
type Unstable_InteractablePersistedState,
|
|
235
|
+
type Unstable_InteractablePersistenceAdapter,
|
|
236
|
+
type Unstable_InteractablePersistenceStatus,
|
|
237
|
+
type Unstable_InteractablesClientSchema,
|
|
238
|
+
type Unstable_InteractablesConfig,
|
|
218
239
|
useToolArgsStatus,
|
|
219
240
|
type ToolArgsStatus,
|
|
220
241
|
} from "@assistant-ui/core/react";
|
|
@@ -228,6 +249,14 @@ export type {
|
|
|
228
249
|
|
|
229
250
|
export { mergeModelContexts } from "@assistant-ui/core";
|
|
230
251
|
|
|
252
|
+
export {
|
|
253
|
+
unstable_getInteractableSnapshots,
|
|
254
|
+
unstable_formatInteractableSnapshot,
|
|
255
|
+
unstable_getInteractableVersions,
|
|
256
|
+
type Unstable_InteractableSnapshotEntry,
|
|
257
|
+
type Unstable_InteractableVersion,
|
|
258
|
+
} from "@assistant-ui/core";
|
|
259
|
+
|
|
231
260
|
export type { Tool } from "assistant-stream";
|
|
232
261
|
|
|
233
262
|
export { tool } from "@assistant-ui/core";
|
|
@@ -280,6 +309,7 @@ export * as ThreadListItemMorePrimitive from "./primitives/threadListItemMore";
|
|
|
280
309
|
export * as SelectionToolbarPrimitive from "./primitives/selectionToolbar";
|
|
281
310
|
|
|
282
311
|
export { groupPartByType, type GroupByContext } from "@assistant-ui/core/react";
|
|
312
|
+
export { unstable_useThreadMessageIds } from "@assistant-ui/core/react";
|
|
283
313
|
export { useMessagePartText } from "./primitives/messagePart/useMessagePartText";
|
|
284
314
|
export { useMessagePartReasoning } from "./primitives/messagePart/useMessagePartReasoning";
|
|
285
315
|
export { useMessagePartSource } from "./primitives/messagePart/useMessagePartSource";
|
|
@@ -447,6 +477,15 @@ export {
|
|
|
447
477
|
type Unstable_ComposerInputHistory,
|
|
448
478
|
} from "./unstable/useComposerInputHistory";
|
|
449
479
|
|
|
480
|
+
// Unstable - headless composer input bridge (value/send without ComposerPrimitive.Input)
|
|
481
|
+
export {
|
|
482
|
+
unstable_useComposerInput,
|
|
483
|
+
unstable_useTriggerPopoverAriaProps,
|
|
484
|
+
type Unstable_UseComposerInputOptions,
|
|
485
|
+
type Unstable_ComposerInput,
|
|
486
|
+
type Unstable_TriggerPopoverAriaProps,
|
|
487
|
+
} from "./unstable/useComposerInput";
|
|
488
|
+
|
|
450
489
|
export type { Assistant } from "./augmentations";
|
|
451
490
|
|
|
452
491
|
// --- mcp-apps ---
|
|
@@ -19,14 +19,17 @@ export const useCommandQueue = (opts: { onQueue: () => void }) => {
|
|
|
19
19
|
const [, rerender] = useState(0);
|
|
20
20
|
const queueStateRef = useRef<CommandQueueState>(createInitialQueueState());
|
|
21
21
|
|
|
22
|
-
const enqueue = (
|
|
22
|
+
const enqueue = (
|
|
23
|
+
command: AssistantTransportCommand,
|
|
24
|
+
options?: { schedule?: boolean },
|
|
25
|
+
) => {
|
|
23
26
|
queueStateRef.current = {
|
|
24
27
|
queued: [...queueStateRef.current.queued, command],
|
|
25
28
|
inTransit: queueStateRef.current.inTransit,
|
|
26
29
|
};
|
|
27
30
|
rerender((prev) => prev + 1);
|
|
28
31
|
|
|
29
|
-
onQueueRef.current();
|
|
32
|
+
if (options?.schedule !== false) onQueueRef.current();
|
|
30
33
|
};
|
|
31
34
|
|
|
32
35
|
const flush = (): QueuedCommand[] => {
|
|
@@ -99,6 +99,28 @@ describe("assistant transport scheduling contracts", () => {
|
|
|
99
99
|
expect(result.current.runBatchesRef.current[1]).toHaveLength(1);
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
+
it("can enqueue without scheduling until a run is started", async () => {
|
|
103
|
+
const { result } = renderHook(() => useTransportSchedulingHarness());
|
|
104
|
+
|
|
105
|
+
act(() => {
|
|
106
|
+
result.current.commandQueue.enqueue(createMessageCommand("staged"), {
|
|
107
|
+
schedule: false,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await Promise.resolve();
|
|
112
|
+
expect(result.current.runBatchesRef.current).toHaveLength(0);
|
|
113
|
+
|
|
114
|
+
act(() => {
|
|
115
|
+
result.current.runManager.schedule();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await waitFor(() => {
|
|
119
|
+
expect(result.current.runBatchesRef.current).toHaveLength(1);
|
|
120
|
+
});
|
|
121
|
+
expect(result.current.runBatchesRef.current[0]).toHaveLength(1);
|
|
122
|
+
});
|
|
123
|
+
|
|
102
124
|
it("cancel returns combined in-flight and queued commands", async () => {
|
|
103
125
|
const onCancel = vi.fn();
|
|
104
126
|
const { result } = renderHook(() =>
|
package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts
CHANGED
|
@@ -316,13 +316,23 @@ const useAssistantTransportThreadRuntime = <T>(
|
|
|
316
316
|
onNew: async (message: AppendMessage): Promise<void> => {
|
|
317
317
|
parentIdRef.current = message.parentId;
|
|
318
318
|
const command = convertAppendMessageToCommand(message);
|
|
319
|
-
commandQueue.enqueue(command
|
|
319
|
+
commandQueue.enqueue(command, {
|
|
320
|
+
schedule: message.startRun ?? message.role === "user",
|
|
321
|
+
});
|
|
320
322
|
},
|
|
321
323
|
...(options.capabilities?.edit && {
|
|
322
324
|
onEdit: async (message: AppendMessage): Promise<void> => {
|
|
323
325
|
parentIdRef.current = message.parentId;
|
|
324
326
|
const command = convertAppendMessageToCommand(message);
|
|
325
|
-
commandQueue.enqueue(command
|
|
327
|
+
commandQueue.enqueue(command, {
|
|
328
|
+
schedule: message.startRun ?? message.role === "user",
|
|
329
|
+
});
|
|
330
|
+
},
|
|
331
|
+
}),
|
|
332
|
+
...(commandQueue.state.queued.length > 0 && {
|
|
333
|
+
onReload: async (parentId: string | null) => {
|
|
334
|
+
parentIdRef.current = parentId;
|
|
335
|
+
runManager.schedule();
|
|
326
336
|
},
|
|
327
337
|
}),
|
|
328
338
|
onCancel: async () => {
|
|
@@ -21,10 +21,14 @@ import TextareaAutosize, {
|
|
|
21
21
|
import { useEscapeKeydown } from "@radix-ui/react-use-escape-keydown";
|
|
22
22
|
import { useOnScrollToBottom } from "../../utils/hooks/useOnScrollToBottom";
|
|
23
23
|
import { useMediaQuery } from "../../utils/hooks/useMediaQuery";
|
|
24
|
-
import {
|
|
24
|
+
import { useAui } from "@assistant-ui/store";
|
|
25
25
|
import { flushTapSync } from "@assistant-ui/tap";
|
|
26
26
|
import { useComposerInputPluginRegistryOptional } from "./ComposerInputPluginContext";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
useComposerInputDisabled,
|
|
29
|
+
useComposerInputValue,
|
|
30
|
+
useTriggerPopoverAriaProps,
|
|
31
|
+
} from "./useComposerInputState";
|
|
28
32
|
|
|
29
33
|
const TOUCH_PRIMARY_QUERY = "(pointer: coarse) and (not (any-pointer: fine))";
|
|
30
34
|
|
|
@@ -167,7 +171,6 @@ export const ComposerPrimitiveInput = forwardRef<
|
|
|
167
171
|
) => {
|
|
168
172
|
const aui = useAui();
|
|
169
173
|
const pluginRegistry = useComposerInputPluginRegistryOptional();
|
|
170
|
-
const activeAria = useTriggerPopoverActiveAriaOptional();
|
|
171
174
|
|
|
172
175
|
const declaredSubmitMode =
|
|
173
176
|
submitMode ?? (submitOnEnter === false ? "none" : "enter");
|
|
@@ -181,15 +184,8 @@ export const ComposerPrimitiveInput = forwardRef<
|
|
|
181
184
|
? "none"
|
|
182
185
|
: declaredSubmitMode;
|
|
183
186
|
|
|
184
|
-
const value =
|
|
185
|
-
|
|
186
|
-
return s.composer.text;
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
const isDisabled =
|
|
190
|
-
useAuiState(
|
|
191
|
-
(s) => s.thread.isDisabled || s.composer.dictation?.inputDisabled,
|
|
192
|
-
) || disabledProp;
|
|
187
|
+
const value = useComposerInputValue();
|
|
188
|
+
const isDisabled = useComposerInputDisabled(disabledProp);
|
|
193
189
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
194
190
|
const ref = useComposedRefs(forwardedRef, textareaRef);
|
|
195
191
|
// suppress text/cursor broadcasts during IME composition
|
|
@@ -322,14 +318,7 @@ export const ComposerPrimitiveInput = forwardRef<
|
|
|
322
318
|
return aui.on("threadListItem.switchedTo", focus);
|
|
323
319
|
}, [unstable_focusOnThreadSwitched, focus, aui]);
|
|
324
320
|
|
|
325
|
-
const ariaComboboxProps =
|
|
326
|
-
? {
|
|
327
|
-
"aria-controls": activeAria.popoverId,
|
|
328
|
-
"aria-expanded": true as const,
|
|
329
|
-
"aria-haspopup": "listbox" as const,
|
|
330
|
-
"aria-activedescendant": activeAria.highlightedItemId,
|
|
331
|
-
}
|
|
332
|
-
: {};
|
|
321
|
+
const ariaComboboxProps = useTriggerPopoverAriaProps();
|
|
333
322
|
|
|
334
323
|
const inputProps = {
|
|
335
324
|
name: "input" as const,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { createTapRoot, useResource } from "@assistant-ui/tap";
|
|
2
|
+
import { createTapRoot, flushTapSync, useResource } from "@assistant-ui/tap";
|
|
3
3
|
import type {
|
|
4
4
|
Unstable_TriggerCategory,
|
|
5
5
|
Unstable_TriggerItem,
|
|
@@ -23,8 +23,6 @@ const makeKeyEvent = (key: string, shiftKey = false) => ({
|
|
|
23
23
|
preventDefault: vi.fn(),
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
const tick = () => new Promise<void>((resolve) => setTimeout(resolve, 0));
|
|
27
|
-
|
|
28
26
|
const render = (
|
|
29
27
|
overrides: Partial<Parameters<typeof TriggerKeyboardResource>[0]> = {},
|
|
30
28
|
) => {
|
|
@@ -106,55 +104,53 @@ describe("TriggerKeyboardResource", () => {
|
|
|
106
104
|
expect(props.selectItem).not.toHaveBeenCalled();
|
|
107
105
|
});
|
|
108
106
|
|
|
109
|
-
it("moves the highlight forward on ArrowDown",
|
|
107
|
+
it("moves the highlight forward on ArrowDown", () => {
|
|
110
108
|
const { sub } = render();
|
|
111
109
|
|
|
112
|
-
const consumed =
|
|
113
|
-
|
|
110
|
+
const consumed = flushTapSync(() =>
|
|
111
|
+
sub.getValue().handleKeyDown(makeKeyEvent("ArrowDown")),
|
|
112
|
+
);
|
|
114
113
|
|
|
115
114
|
expect(consumed).toBe(true);
|
|
116
115
|
expect(sub.getValue().highlightedIndex).toBe(1);
|
|
117
116
|
});
|
|
118
117
|
|
|
119
|
-
it("wraps the highlight to the top on ArrowDown past the last entry",
|
|
118
|
+
it("wraps the highlight to the top on ArrowDown past the last entry", () => {
|
|
120
119
|
const { sub } = render();
|
|
121
120
|
const handle = sub.getValue().handleKeyDown;
|
|
122
121
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
flushTapSync(() => {
|
|
123
|
+
handle(makeKeyEvent("ArrowDown"));
|
|
124
|
+
handle(makeKeyEvent("ArrowDown"));
|
|
125
|
+
handle(makeKeyEvent("ArrowDown"));
|
|
126
|
+
});
|
|
127
127
|
|
|
128
128
|
expect(sub.getValue().highlightedIndex).toBe(0);
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
it("moves the highlight backward on ArrowUp",
|
|
131
|
+
it("moves the highlight backward on ArrowUp", () => {
|
|
132
132
|
const { sub } = render();
|
|
133
133
|
const handle = sub.getValue().handleKeyDown;
|
|
134
134
|
|
|
135
|
-
handle(makeKeyEvent("ArrowDown"));
|
|
136
|
-
|
|
137
|
-
handle(makeKeyEvent("ArrowUp"));
|
|
138
|
-
await tick();
|
|
135
|
+
flushTapSync(() => handle(makeKeyEvent("ArrowDown")));
|
|
136
|
+
flushTapSync(() => handle(makeKeyEvent("ArrowUp")));
|
|
139
137
|
|
|
140
138
|
expect(sub.getValue().highlightedIndex).toBe(0);
|
|
141
139
|
});
|
|
142
140
|
|
|
143
|
-
it("wraps the highlight to the bottom on ArrowUp from the first entry",
|
|
141
|
+
it("wraps the highlight to the bottom on ArrowUp from the first entry", () => {
|
|
144
142
|
const { sub } = render();
|
|
145
143
|
|
|
146
|
-
sub.getValue().handleKeyDown(makeKeyEvent("ArrowUp"));
|
|
147
|
-
await tick();
|
|
144
|
+
flushTapSync(() => sub.getValue().handleKeyDown(makeKeyEvent("ArrowUp")));
|
|
148
145
|
|
|
149
146
|
expect(sub.getValue().highlightedIndex).toBe(2);
|
|
150
147
|
});
|
|
151
148
|
|
|
152
|
-
it("keeps the highlight at 0 on ArrowDown when navigableList is empty",
|
|
149
|
+
it("keeps the highlight at 0 on ArrowDown when navigableList is empty", () => {
|
|
153
150
|
const { sub } = render({ navigableList: [] });
|
|
154
151
|
const e = makeKeyEvent("ArrowDown");
|
|
155
152
|
|
|
156
|
-
const consumed = sub.getValue().handleKeyDown(e);
|
|
157
|
-
await tick();
|
|
153
|
+
const consumed = flushTapSync(() => sub.getValue().handleKeyDown(e));
|
|
158
154
|
|
|
159
155
|
expect(consumed).toBe(true);
|
|
160
156
|
expect(e.preventDefault).toHaveBeenCalled();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useAuiState } from "@assistant-ui/store";
|
|
4
|
+
import { useTriggerPopoverActiveAriaOptional } from "./trigger/TriggerPopoverRootContext";
|
|
5
|
+
|
|
6
|
+
export type TriggerPopoverAriaProps = {
|
|
7
|
+
"aria-controls"?: string;
|
|
8
|
+
"aria-expanded"?: true;
|
|
9
|
+
"aria-haspopup"?: "listbox";
|
|
10
|
+
"aria-activedescendant"?: string | undefined;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function useComposerInputValue() {
|
|
14
|
+
return useAuiState((s) => (s.composer.isEditing ? s.composer.text : ""));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useComposerInputDisabled(disabled?: boolean | undefined) {
|
|
18
|
+
const composerDisabled = useAuiState(
|
|
19
|
+
(s) => s.thread.isDisabled || s.composer.dictation?.inputDisabled,
|
|
20
|
+
);
|
|
21
|
+
return Boolean(composerDisabled) || Boolean(disabled);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function useTriggerPopoverAriaProps(): TriggerPopoverAriaProps {
|
|
25
|
+
const activeAria = useTriggerPopoverActiveAriaOptional();
|
|
26
|
+
if (!activeAria) return {};
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
"aria-controls": activeAria.popoverId,
|
|
30
|
+
"aria-expanded": true,
|
|
31
|
+
"aria-haspopup": "listbox",
|
|
32
|
+
"aria-activedescendant": activeAria.highlightedItemId,
|
|
33
|
+
};
|
|
34
|
+
}
|
package/src/primitives/thread.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { ThreadPrimitiveViewportProvider as ViewportProvider } from "../context/
|
|
|
6
6
|
export { ThreadPrimitiveViewportFooter as ViewportFooter } from "./thread/ThreadViewportFooter";
|
|
7
7
|
export { ThreadPrimitiveMessages as Messages } from "./thread/ThreadMessages";
|
|
8
8
|
export { ThreadPrimitiveMessageByIndex as MessageByIndex } from "./thread/ThreadMessages";
|
|
9
|
+
export { ThreadPrimitiveUnstable_MessageById as Unstable_MessageById } from "./thread/ThreadMessages";
|
|
9
10
|
export { ThreadPrimitiveScrollToBottom as ScrollToBottom } from "./thread/ThreadScrollToBottom";
|
|
10
11
|
export { ThreadPrimitiveSuggestion as Suggestion } from "./thread/ThreadSuggestion";
|
|
11
12
|
export {
|