@assistant-ui/react-native 0.1.41 → 0.1.43
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 +5 -4
- package/dist/index.js +4 -3
- package/dist/primitives/actionBar/ActionBarCopy.d.ts +3 -3
- package/dist/primitives/actionBar/ActionBarCopy.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarCopy.js +23 -19
- package/dist/primitives/actionBar/ActionBarCopy.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarEdit.d.ts +7 -5
- package/dist/primitives/actionBar/ActionBarEdit.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarEdit.js +21 -11
- package/dist/primitives/actionBar/ActionBarEdit.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.d.ts +3 -3
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.js +26 -16
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.d.ts +3 -3
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.js +26 -16
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarReload.d.ts +7 -5
- package/dist/primitives/actionBar/ActionBarReload.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarReload.js +21 -11
- package/dist/primitives/actionBar/ActionBarReload.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarSpeak.d.ts +14 -0
- package/dist/primitives/actionBar/ActionBarSpeak.d.ts.map +1 -0
- package/dist/primitives/actionBar/ActionBarSpeak.js +61 -0
- package/dist/primitives/actionBar/ActionBarSpeak.js.map +1 -0
- package/dist/primitives/actionBar/ActionBarStopSpeaking.d.ts +14 -0
- package/dist/primitives/actionBar/ActionBarStopSpeaking.d.ts.map +1 -0
- package/dist/primitives/actionBar/ActionBarStopSpeaking.js +61 -0
- package/dist/primitives/actionBar/ActionBarStopSpeaking.js.map +1 -0
- package/dist/primitives/actionBar.d.ts +4 -2
- package/dist/primitives/actionBar.js +6 -2
- package/dist/primitives/actionBar.js.map +1 -1
- package/dist/primitives/attachment/AttachmentName.d.ts +2 -3
- package/dist/primitives/attachment/AttachmentName.d.ts.map +1 -1
- package/dist/primitives/attachment/AttachmentRemove.d.ts +2 -3
- package/dist/primitives/attachment/AttachmentRemove.d.ts.map +1 -1
- package/dist/primitives/attachment/AttachmentRoot.d.ts +2 -3
- package/dist/primitives/attachment/AttachmentRoot.d.ts.map +1 -1
- package/dist/primitives/attachment/AttachmentThumb.d.ts +2 -3
- package/dist/primitives/attachment/AttachmentThumb.d.ts.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerCount.d.ts +2 -3
- package/dist/primitives/branchPicker/BranchPickerCount.d.ts.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerNext.d.ts +7 -5
- package/dist/primitives/branchPicker/BranchPickerNext.d.ts.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerNext.js +21 -11
- package/dist/primitives/branchPicker/BranchPickerNext.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerNumber.d.ts +2 -3
- package/dist/primitives/branchPicker/BranchPickerNumber.d.ts.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerPrevious.d.ts +7 -5
- package/dist/primitives/branchPicker/BranchPickerPrevious.d.ts.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerPrevious.js +21 -11
- package/dist/primitives/branchPicker/BranchPickerPrevious.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.d.ts +2 -3
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.d.ts.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtRoot.d.ts +2 -3
- package/dist/primitives/chainOfThought/ChainOfThoughtRoot.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.d.ts +4 -7
- package/dist/primitives/composer/ComposerAddAttachment.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.js +2 -4
- package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
- package/dist/primitives/composer/ComposerAttachments.d.ts +4 -5
- package/dist/primitives/composer/ComposerAttachments.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerCancel.d.ts +2 -3
- package/dist/primitives/composer/ComposerCancel.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerInput.d.ts +2 -3
- package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerQuote.d.ts +2 -3
- package/dist/primitives/composer/ComposerQuote.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerQuoteDismiss.d.ts +2 -3
- package/dist/primitives/composer/ComposerQuoteDismiss.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerQuoteText.d.ts +2 -3
- package/dist/primitives/composer/ComposerQuoteText.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerRoot.d.ts +2 -3
- package/dist/primitives/composer/ComposerRoot.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerSend.d.ts +2 -3
- package/dist/primitives/composer/ComposerSend.d.ts.map +1 -1
- package/dist/primitives/error/ErrorMessage.d.ts +2 -3
- package/dist/primitives/error/ErrorMessage.d.ts.map +1 -1
- package/dist/primitives/error/ErrorRoot.d.ts +2 -3
- package/dist/primitives/error/ErrorRoot.d.ts.map +1 -1
- package/dist/primitives/message/MessageAttachments.d.ts +4 -5
- package/dist/primitives/message/MessageAttachments.d.ts.map +1 -1
- package/dist/primitives/message/MessageContent.d.ts +2 -3
- package/dist/primitives/message/MessageContent.d.ts.map +1 -1
- package/dist/primitives/message/MessageIf.d.ts +2 -3
- package/dist/primitives/message/MessageIf.d.ts.map +1 -1
- package/dist/primitives/message/MessageParts.d.ts +3 -3
- package/dist/primitives/message/MessageParts.d.ts.map +1 -1
- package/dist/primitives/message/MessageRoot.d.ts +2 -3
- package/dist/primitives/message/MessageRoot.d.ts.map +1 -1
- package/dist/primitives/messagePart/MessagePartImage.d.ts +11 -0
- package/dist/primitives/messagePart/MessagePartImage.d.ts.map +1 -0
- package/dist/primitives/messagePart/MessagePartImage.js +39 -0
- package/dist/primitives/messagePart/MessagePartImage.js.map +1 -0
- package/dist/primitives/messagePart/MessagePartInProgress.d.ts +2 -0
- package/dist/primitives/messagePart/MessagePartInProgress.js +2 -0
- package/dist/primitives/messagePart/MessagePartText.d.ts +11 -0
- package/dist/primitives/messagePart/MessagePartText.d.ts.map +1 -0
- package/dist/primitives/messagePart/MessagePartText.js +32 -0
- package/dist/primitives/messagePart/MessagePartText.js.map +1 -0
- package/dist/primitives/messagePart.d.ts +10 -0
- package/dist/primitives/messagePart.d.ts.map +1 -0
- package/dist/primitives/messagePart.js +16 -0
- package/dist/primitives/messagePart.js.map +1 -0
- package/dist/primitives/queueItem/QueueItemRemove.d.ts +2 -3
- package/dist/primitives/queueItem/QueueItemRemove.d.ts.map +1 -1
- package/dist/primitives/queueItem/QueueItemSteer.d.ts +2 -3
- package/dist/primitives/queueItem/QueueItemSteer.d.ts.map +1 -1
- package/dist/primitives/queueItem/QueueItemText.d.ts +2 -3
- package/dist/primitives/queueItem/QueueItemText.d.ts.map +1 -1
- package/dist/primitives/suggestion/SuggestionDescription.d.ts +2 -3
- package/dist/primitives/suggestion/SuggestionDescription.d.ts.map +1 -1
- package/dist/primitives/suggestion/SuggestionTitle.d.ts +2 -3
- package/dist/primitives/suggestion/SuggestionTitle.d.ts.map +1 -1
- package/dist/primitives/suggestion/SuggestionTrigger.d.ts +2 -3
- package/dist/primitives/suggestion/SuggestionTrigger.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadEmpty.d.ts +2 -3
- package/dist/primitives/thread/ThreadEmpty.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadIf.d.ts +2 -3
- package/dist/primitives/thread/ThreadIf.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadMessages.d.ts +9 -5
- package/dist/primitives/thread/ThreadMessages.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadMessages.js +244 -171
- package/dist/primitives/thread/ThreadMessages.js.map +1 -1
- package/dist/primitives/thread/ThreadRoot.d.ts +2 -3
- package/dist/primitives/thread/ThreadRoot.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadSuggestion.d.ts +2 -3
- package/dist/primitives/thread/ThreadSuggestion.d.ts.map +1 -1
- package/dist/primitives/threadList/ThreadListItems.d.ts +2 -3
- package/dist/primitives/threadList/ThreadListItems.d.ts.map +1 -1
- package/dist/primitives/threadList/ThreadListNew.d.ts +2 -3
- package/dist/primitives/threadList/ThreadListNew.d.ts.map +1 -1
- package/dist/primitives/threadList/ThreadListRoot.d.ts +2 -3
- package/dist/primitives/threadList/ThreadListRoot.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemArchive.d.ts +2 -3
- package/dist/primitives/threadListItem/ThreadListItemArchive.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemDelete.d.ts +2 -3
- package/dist/primitives/threadListItem/ThreadListItemDelete.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemRoot.d.ts +2 -3
- package/dist/primitives/threadListItem/ThreadListItemRoot.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemTrigger.d.ts +2 -3
- package/dist/primitives/threadListItem/ThreadListItemTrigger.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemUnarchive.d.ts +2 -3
- package/dist/primitives/threadListItem/ThreadListItemUnarchive.d.ts.map +1 -1
- package/package.json +13 -11
- package/src/index.ts +30 -0
- package/src/primitives/accessibilityRole.test.tsx +20 -0
- package/src/primitives/actionBar/ActionBar.test.tsx +299 -0
- package/src/primitives/actionBar/ActionBarCopy.tsx +8 -3
- package/src/primitives/actionBar/ActionBarEdit.tsx +16 -4
- package/src/primitives/actionBar/ActionBarFeedbackNegative.tsx +14 -3
- package/src/primitives/actionBar/ActionBarFeedbackPositive.tsx +14 -3
- package/src/primitives/actionBar/ActionBarReload.tsx +16 -4
- package/src/primitives/actionBar/ActionBarSpeak.tsx +42 -0
- package/src/primitives/actionBar/ActionBarStopSpeaking.tsx +42 -0
- package/src/primitives/actionBar.ts +8 -0
- package/src/primitives/branchPicker/BranchPickerNext.test.tsx +52 -3
- package/src/primitives/branchPicker/BranchPickerNext.tsx +16 -4
- package/src/primitives/branchPicker/BranchPickerPrevious.test.tsx +52 -6
- package/src/primitives/branchPicker/BranchPickerPrevious.tsx +16 -4
- package/src/primitives/composer/ComposerAddAttachment.tsx +3 -8
- package/src/primitives/messagePart/MessagePartImage.test.tsx +91 -0
- package/src/primitives/messagePart/MessagePartImage.tsx +33 -0
- package/src/primitives/messagePart/MessagePartInProgress.ts +1 -0
- package/src/primitives/messagePart/MessagePartText.test.tsx +80 -0
- package/src/primitives/messagePart/MessagePartText.tsx +36 -0
- package/src/primitives/messagePart.ts +10 -0
- package/src/primitives/thread/ThreadMessages.test.tsx +335 -54
- package/src/primitives/thread/ThreadMessages.tsx +104 -26
|
@@ -8,12 +8,17 @@ const h = vi.hoisted(() => ({
|
|
|
8
8
|
state: { disabled: false },
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
|
-
vi.mock("@assistant-ui/core/react", () =>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
vi.mock("@assistant-ui/core/react", async (importOriginal) => {
|
|
12
|
+
const actual =
|
|
13
|
+
await importOriginal<typeof import("@assistant-ui/core/react")>();
|
|
14
|
+
return {
|
|
15
|
+
...actual,
|
|
16
|
+
useBranchPickerPrevious: () => ({
|
|
17
|
+
previous: h.previous,
|
|
18
|
+
disabled: h.state.disabled,
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
});
|
|
17
22
|
|
|
18
23
|
(globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
|
|
19
24
|
|
|
@@ -97,4 +102,45 @@ describe("BranchPickerPrevious", () => {
|
|
|
97
102
|
|
|
98
103
|
expect(h.previous).toHaveBeenCalledTimes(1);
|
|
99
104
|
});
|
|
105
|
+
|
|
106
|
+
it("passes the hook disabled state and an explicit override to render children", async () => {
|
|
107
|
+
h.state.disabled = true;
|
|
108
|
+
let hookState: unknown = null;
|
|
109
|
+
|
|
110
|
+
await act(async () => {
|
|
111
|
+
root.render(
|
|
112
|
+
<BranchPickerPrevious testID="t">
|
|
113
|
+
{(state) => {
|
|
114
|
+
hookState = state;
|
|
115
|
+
return state.disabled ? "disabled" : "enabled";
|
|
116
|
+
}}
|
|
117
|
+
</BranchPickerPrevious>,
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(hookState).toMatchObject({ pressed: false, disabled: true });
|
|
122
|
+
expect(container.textContent).toBe("disabled");
|
|
123
|
+
|
|
124
|
+
let overrideState: unknown = null;
|
|
125
|
+
|
|
126
|
+
await act(async () => {
|
|
127
|
+
root.render(
|
|
128
|
+
<BranchPickerPrevious testID="t" disabled={false}>
|
|
129
|
+
{(state) => {
|
|
130
|
+
overrideState = state;
|
|
131
|
+
return state.disabled ? "disabled" : "enabled";
|
|
132
|
+
}}
|
|
133
|
+
</BranchPickerPrevious>,
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(overrideState).toMatchObject({ pressed: false, disabled: false });
|
|
138
|
+
expect(container.textContent).toBe("enabled");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("renders plain children", async () => {
|
|
142
|
+
const el = await mount();
|
|
143
|
+
|
|
144
|
+
expect(el.textContent).toBe("prev");
|
|
145
|
+
});
|
|
100
146
|
});
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Pressable,
|
|
4
|
+
type PressableProps,
|
|
5
|
+
type PressableStateCallbackType,
|
|
6
|
+
} from "react-native";
|
|
2
7
|
import { useBranchPickerPrevious } from "@assistant-ui/core/react";
|
|
3
8
|
|
|
4
9
|
export type BranchPickerPreviousProps = Omit<
|
|
5
10
|
PressableProps,
|
|
6
11
|
"onPress" | "children"
|
|
7
12
|
> & {
|
|
8
|
-
children:
|
|
13
|
+
children:
|
|
14
|
+
| ReactNode
|
|
15
|
+
| ((
|
|
16
|
+
state: PressableStateCallbackType & { disabled: boolean },
|
|
17
|
+
) => ReactNode);
|
|
9
18
|
};
|
|
10
19
|
|
|
11
20
|
export const BranchPickerPrevious = ({
|
|
@@ -14,15 +23,18 @@ export const BranchPickerPrevious = ({
|
|
|
14
23
|
...pressableProps
|
|
15
24
|
}: BranchPickerPreviousProps) => {
|
|
16
25
|
const { previous, disabled } = useBranchPickerPrevious();
|
|
26
|
+
const isDisabled = disabledProp ?? disabled;
|
|
17
27
|
|
|
18
28
|
return (
|
|
19
29
|
<Pressable
|
|
20
30
|
onPress={previous}
|
|
21
|
-
disabled={
|
|
31
|
+
disabled={isDisabled}
|
|
22
32
|
accessibilityRole="button"
|
|
23
33
|
{...pressableProps}
|
|
24
34
|
>
|
|
25
|
-
{children
|
|
35
|
+
{typeof children === "function"
|
|
36
|
+
? (state) => children({ ...state, disabled: isDisabled })
|
|
37
|
+
: children}
|
|
26
38
|
</Pressable>
|
|
27
39
|
);
|
|
28
40
|
};
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import { Pressable, type PressableProps } from "react-native";
|
|
2
2
|
import { useComposerAddAttachment } from "@assistant-ui/core/react";
|
|
3
3
|
|
|
4
|
-
export type ComposerAddAttachmentProps = Omit<
|
|
5
|
-
PressableProps,
|
|
6
|
-
"onPress" | "children"
|
|
7
|
-
> & {
|
|
4
|
+
export type ComposerAddAttachmentProps = Omit<PressableProps, "children"> & {
|
|
8
5
|
children: PressableProps["children"];
|
|
9
6
|
};
|
|
10
7
|
|
|
11
8
|
/**
|
|
12
|
-
* A button
|
|
9
|
+
* A button for adding attachments. It is disabled while the composer cannot accept attachments.
|
|
13
10
|
*
|
|
14
|
-
*
|
|
15
|
-
* This component calls `useComposerAddAttachment()` from `@assistant-ui/core/react`.
|
|
16
|
-
* You must handle the file selection in your own component using the returned `addAttachment` callback.
|
|
11
|
+
* React Native has no file input, so the caller opens the platform picker (for example `expo-image-picker`) in `onPress` and passes the selection to `aui.composer.addAttachment`.
|
|
17
12
|
*/
|
|
18
13
|
export const ComposerAddAttachment = ({
|
|
19
14
|
children,
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { act } from "react";
|
|
2
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import type { ThreadMessageLike } from "@assistant-ui/core";
|
|
5
|
+
import {
|
|
6
|
+
AssistantRuntimeProvider,
|
|
7
|
+
MessageByIndexProvider,
|
|
8
|
+
PartByIndexProvider,
|
|
9
|
+
useExternalStoreRuntime,
|
|
10
|
+
} from "@assistant-ui/core/react";
|
|
11
|
+
import { MessagePartPrimitiveImage } from "./MessagePartImage";
|
|
12
|
+
|
|
13
|
+
vi.mock("react-native", async (importOriginal) => {
|
|
14
|
+
const actual = await importOriginal<typeof import("react-native")>();
|
|
15
|
+
const React = await import("react");
|
|
16
|
+
|
|
17
|
+
const ImageMock = React.forwardRef(function ImageMock(
|
|
18
|
+
props: { source?: { uri?: string }; testID?: string },
|
|
19
|
+
ref: React.Ref<HTMLImageElement>,
|
|
20
|
+
) {
|
|
21
|
+
return React.createElement("img", {
|
|
22
|
+
ref,
|
|
23
|
+
src: props.source?.uri,
|
|
24
|
+
"data-testid": props.testID,
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return { ...actual, Image: ImageMock };
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
(globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
|
|
32
|
+
|
|
33
|
+
const messages: ThreadMessageLike[] = [
|
|
34
|
+
{
|
|
35
|
+
role: "assistant",
|
|
36
|
+
content: [
|
|
37
|
+
{ type: "text", text: "Answer" },
|
|
38
|
+
{ type: "image", image: "https://example.com/image.png" },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const App = ({ partIndex }: { partIndex: number }) => {
|
|
44
|
+
const runtime = useExternalStoreRuntime({
|
|
45
|
+
messages,
|
|
46
|
+
convertMessage: (message) => message,
|
|
47
|
+
onNew: async () => {
|
|
48
|
+
throw new Error("This thread is read-only");
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
54
|
+
<MessageByIndexProvider index={0}>
|
|
55
|
+
<PartByIndexProvider index={partIndex}>
|
|
56
|
+
<MessagePartPrimitiveImage testID="image" />
|
|
57
|
+
</PartByIndexProvider>
|
|
58
|
+
</MessageByIndexProvider>
|
|
59
|
+
</AssistantRuntimeProvider>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
describe("MessagePartPrimitiveImage", () => {
|
|
64
|
+
let container: HTMLDivElement;
|
|
65
|
+
let root: Root;
|
|
66
|
+
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
container = document.createElement("div");
|
|
69
|
+
document.body.appendChild(container);
|
|
70
|
+
root = createRoot(container);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterEach(async () => {
|
|
74
|
+
await act(async () => root.unmount());
|
|
75
|
+
container.remove();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const mount = async (partIndex: number) => {
|
|
79
|
+
await act(async () => root.render(<App partIndex={partIndex} />));
|
|
80
|
+
return container.querySelector<HTMLImageElement>('[data-testid="image"]');
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
it("renders the current image part URI", async () => {
|
|
84
|
+
const image = await mount(1);
|
|
85
|
+
expect(image?.getAttribute("src")).toBe("https://example.com/image.png");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("renders nothing for a non-image part", async () => {
|
|
89
|
+
expect(await mount(0)).toBeNull();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ComponentRef,
|
|
3
|
+
type ForwardRefExoticComponent,
|
|
4
|
+
forwardRef,
|
|
5
|
+
type RefAttributes,
|
|
6
|
+
} from "react";
|
|
7
|
+
import { Image, type ImageProps } from "react-native";
|
|
8
|
+
import { useAuiState } from "@assistant-ui/store";
|
|
9
|
+
|
|
10
|
+
export namespace MessagePartPrimitiveImage {
|
|
11
|
+
export type Element = ComponentRef<typeof Image>;
|
|
12
|
+
export type Props = Omit<ImageProps, "source">;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const MessagePartPrimitiveImage: ForwardRefExoticComponent<
|
|
16
|
+
MessagePartPrimitiveImage.Props &
|
|
17
|
+
RefAttributes<MessagePartPrimitiveImage.Element>
|
|
18
|
+
> = forwardRef<
|
|
19
|
+
MessagePartPrimitiveImage.Element,
|
|
20
|
+
MessagePartPrimitiveImage.Props
|
|
21
|
+
>((props, forwardedRef) => {
|
|
22
|
+
const uri = useAuiState((s) => {
|
|
23
|
+
const part = s.part;
|
|
24
|
+
return part.type === "image" ? part.image : undefined;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (uri === undefined) return null;
|
|
28
|
+
return <Image source={{ uri }} {...props} ref={forwardedRef} />;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
MessagePartPrimitiveImage.displayName = "MessagePartPrimitive.Image";
|
|
32
|
+
|
|
33
|
+
export type MessagePartImageProps = MessagePartPrimitiveImage.Props;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { MessagePartPrimitiveInProgress } from "@assistant-ui/core/react";
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { act } from "react";
|
|
2
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
4
|
+
import type { ThreadMessageLike } from "@assistant-ui/core";
|
|
5
|
+
import {
|
|
6
|
+
AssistantRuntimeProvider,
|
|
7
|
+
MessageByIndexProvider,
|
|
8
|
+
PartByIndexProvider,
|
|
9
|
+
useExternalStoreRuntime,
|
|
10
|
+
} from "@assistant-ui/core/react";
|
|
11
|
+
import { MessagePartPrimitiveText } from "./MessagePartText";
|
|
12
|
+
|
|
13
|
+
(globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
|
|
14
|
+
|
|
15
|
+
const messages: ThreadMessageLike[] = [
|
|
16
|
+
{
|
|
17
|
+
role: "assistant",
|
|
18
|
+
content: [
|
|
19
|
+
{ type: "text", text: "Answer" },
|
|
20
|
+
{ type: "reasoning", text: "Thinking" },
|
|
21
|
+
{ type: "image", image: "https://example.com/image.png" },
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const App = ({ partIndex }: { partIndex: number }) => {
|
|
27
|
+
const runtime = useExternalStoreRuntime({
|
|
28
|
+
messages,
|
|
29
|
+
convertMessage: (message) => message,
|
|
30
|
+
onNew: async () => {
|
|
31
|
+
throw new Error("This thread is read-only");
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
37
|
+
<MessageByIndexProvider index={0}>
|
|
38
|
+
<PartByIndexProvider index={partIndex}>
|
|
39
|
+
<MessagePartPrimitiveText testID="text" />
|
|
40
|
+
</PartByIndexProvider>
|
|
41
|
+
</MessageByIndexProvider>
|
|
42
|
+
</AssistantRuntimeProvider>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
describe("MessagePartPrimitiveText", () => {
|
|
47
|
+
let container: HTMLDivElement;
|
|
48
|
+
let root: Root;
|
|
49
|
+
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
container = document.createElement("div");
|
|
52
|
+
document.body.appendChild(container);
|
|
53
|
+
root = createRoot(container);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
afterEach(async () => {
|
|
57
|
+
await act(async () => root.unmount());
|
|
58
|
+
container.remove();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const mount = async (partIndex: number) => {
|
|
62
|
+
await act(async () => root.render(<App partIndex={partIndex} />));
|
|
63
|
+
return container.querySelector('[data-testid="text"]') as HTMLElement;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
it("renders text from the current text part", async () => {
|
|
67
|
+
const text = await mount(0);
|
|
68
|
+
expect(text.textContent).toBe("Answer");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("renders text from the current reasoning part", async () => {
|
|
72
|
+
const text = await mount(1);
|
|
73
|
+
expect(text.textContent).toBe("Thinking");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("renders empty text for a non-text part", async () => {
|
|
77
|
+
const text = await mount(2);
|
|
78
|
+
expect(text.textContent).toBe("");
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ComponentRef,
|
|
3
|
+
type ForwardRefExoticComponent,
|
|
4
|
+
forwardRef,
|
|
5
|
+
type RefAttributes,
|
|
6
|
+
} from "react";
|
|
7
|
+
import { Text, type TextProps } from "react-native";
|
|
8
|
+
import { useAuiState } from "@assistant-ui/store";
|
|
9
|
+
|
|
10
|
+
export namespace MessagePartPrimitiveText {
|
|
11
|
+
export type Element = ComponentRef<typeof Text>;
|
|
12
|
+
export type Props = Omit<TextProps, "children">;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const MessagePartPrimitiveText: ForwardRefExoticComponent<
|
|
16
|
+
MessagePartPrimitiveText.Props &
|
|
17
|
+
RefAttributes<MessagePartPrimitiveText.Element>
|
|
18
|
+
> = forwardRef<
|
|
19
|
+
MessagePartPrimitiveText.Element,
|
|
20
|
+
MessagePartPrimitiveText.Props
|
|
21
|
+
>((props, forwardedRef) => {
|
|
22
|
+
const text = useAuiState((s) => {
|
|
23
|
+
const part = s.part;
|
|
24
|
+
return part.type === "text" || part.type === "reasoning" ? part.text : "";
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Text {...props} ref={forwardedRef}>
|
|
29
|
+
{text}
|
|
30
|
+
</Text>
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
MessagePartPrimitiveText.displayName = "MessagePartPrimitive.Text";
|
|
35
|
+
|
|
36
|
+
export type MessagePartTextProps = MessagePartPrimitiveText.Props;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export {
|
|
2
|
+
MessagePartPrimitiveText as Text,
|
|
3
|
+
type MessagePartTextProps as TextProps,
|
|
4
|
+
} from "./messagePart/MessagePartText";
|
|
5
|
+
export {
|
|
6
|
+
MessagePartPrimitiveImage as Image,
|
|
7
|
+
type MessagePartImageProps as ImageProps,
|
|
8
|
+
} from "./messagePart/MessagePartImage";
|
|
9
|
+
export { MessagePartPrimitiveInProgress as InProgress } from "./messagePart/MessagePartInProgress";
|
|
10
|
+
export { PartPrimitiveMessages as Messages } from "@assistant-ui/core/react";
|