@assistant-ui/react-native 0.1.44 → 0.1.46
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/LICENSE +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/primitives/actionBar/ActionBarCopy.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarEdit.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarReload.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarSpeak.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarStopSpeaking.js.map +1 -1
- package/dist/primitives/attachment/AttachmentName.js.map +1 -1
- package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
- package/dist/primitives/attachment/AttachmentRoot.js.map +1 -1
- package/dist/primitives/attachment/AttachmentThumb.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerCount.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerNext.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerNumber.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerPrevious.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtRoot.js.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
- package/dist/primitives/composer/ComposerCancel.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerQuote.js.map +1 -1
- package/dist/primitives/composer/ComposerQuoteDismiss.js.map +1 -1
- package/dist/primitives/composer/ComposerQuoteText.js.map +1 -1
- package/dist/primitives/composer/ComposerRoot.js.map +1 -1
- package/dist/primitives/composer/ComposerSend.js.map +1 -1
- package/dist/primitives/error/ErrorMessage.js.map +1 -1
- package/dist/primitives/error/ErrorRoot.js.map +1 -1
- package/dist/primitives/message/MessageContent.d.ts.map +1 -1
- package/dist/primitives/message/MessageContent.js +43 -33
- package/dist/primitives/message/MessageContent.js.map +1 -1
- package/dist/primitives/message/MessageIf.js.map +1 -1
- package/dist/primitives/message/MessageParts.js.map +1 -1
- package/dist/primitives/message/MessageRoot.js.map +1 -1
- package/dist/primitives/message.d.ts +1 -0
- package/dist/primitives/message.d.ts.map +1 -1
- package/dist/primitives/message.js +3 -2
- package/dist/primitives/message.js.map +1 -1
- package/dist/primitives/messagePart/MessagePartImage.js.map +1 -1
- package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemText.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionDescription.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionTitle.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
- package/dist/primitives/thread/ThreadEmpty.js.map +1 -1
- package/dist/primitives/thread/ThreadIf.js.map +1 -1
- package/dist/primitives/thread/ThreadMessages.d.ts +7 -5
- package/dist/primitives/thread/ThreadMessages.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadMessages.js +109 -376
- package/dist/primitives/thread/ThreadMessages.js.map +1 -1
- package/dist/primitives/thread/ThreadRoot.js.map +1 -1
- package/dist/primitives/thread/ThreadRowsFlatList.d.ts +29 -0
- package/dist/primitives/thread/ThreadRowsFlatList.d.ts.map +1 -0
- package/dist/primitives/thread/ThreadRowsFlatList.js +226 -0
- package/dist/primitives/thread/ThreadRowsFlatList.js.map +1 -0
- package/dist/primitives/thread/ThreadSuggestion.js.map +1 -1
- package/dist/primitives/thread/flatListScroll.d.ts +52 -0
- package/dist/primitives/thread/flatListScroll.d.ts.map +1 -0
- package/dist/primitives/thread/flatListScroll.js +380 -0
- package/dist/primitives/thread/flatListScroll.js.map +1 -0
- package/dist/primitives/thread.d.ts +2 -0
- package/dist/primitives/thread.d.ts.map +1 -1
- package/dist/primitives/thread.js +5 -2
- package/dist/primitives/thread.js.map +1 -1
- package/dist/primitives/threadList/ThreadListItems.js.map +1 -1
- package/dist/primitives/threadList/ThreadListNew.js.map +1 -1
- package/dist/primitives/threadList/ThreadListRoot.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemArchive.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemDelete.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemUnarchive.js.map +1 -1
- package/package.json +13 -9
- package/src/index.ts +18 -1
- package/src/primitives/message/MessageContent.test.tsx +46 -1
- package/src/primitives/message/MessageContent.tsx +3 -1
- package/src/primitives/message.test.tsx +74 -0
- package/src/primitives/message.ts +1 -0
- package/src/primitives/thread/ThreadMessages.test.tsx +28 -1
- package/src/primitives/thread/ThreadMessages.tsx +49 -343
- package/src/primitives/thread/ThreadRowsFlatList.test.tsx +253 -0
- package/src/primitives/thread/ThreadRowsFlatList.tsx +159 -0
- package/src/primitives/thread/flatListScroll.ts +467 -0
- package/src/primitives/thread.ts +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.46",
|
|
4
4
|
"description": "React Native bindings for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"assistant",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
],
|
|
32
32
|
"sideEffects": false,
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@assistant-ui/core": "^0.3.
|
|
35
|
-
"@assistant-ui/store": "^0.3.
|
|
36
|
-
"@assistant-ui/tap": "^0.9.
|
|
37
|
-
"assistant-cloud": "^0.2.
|
|
38
|
-
"assistant-stream": "^0.3.
|
|
34
|
+
"@assistant-ui/core": "^0.3.24",
|
|
35
|
+
"@assistant-ui/store": "^0.3.17",
|
|
36
|
+
"@assistant-ui/tap": "^0.9.21",
|
|
37
|
+
"assistant-cloud": "^0.2.5",
|
|
38
|
+
"assistant-stream": "^0.3.48"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@types/react": "*",
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
51
|
+
"@assistant-ui/x-buildutils": "0.0.30",
|
|
52
|
+
"@assistant-ui/x-react18": "0.0.0",
|
|
52
53
|
"@babel/core": "^7.29.7",
|
|
53
54
|
"@types/react": "^19.3.0",
|
|
54
55
|
"@types/react-dom": "^19.3.0",
|
|
@@ -56,8 +57,8 @@
|
|
|
56
57
|
"react": "^19.3.0",
|
|
57
58
|
"react-dom": "^19.3.0",
|
|
58
59
|
"react-native": "^0.87.1",
|
|
59
|
-
"react-native-web": "^0.21.
|
|
60
|
-
"vitest": "^5.0.
|
|
60
|
+
"react-native-web": "^0.21.3",
|
|
61
|
+
"vitest": "^5.0.3"
|
|
61
62
|
},
|
|
62
63
|
"publishConfig": {
|
|
63
64
|
"access": "public",
|
|
@@ -75,7 +76,10 @@
|
|
|
75
76
|
"scripts": {
|
|
76
77
|
"build": "aui-build",
|
|
77
78
|
"test": "vitest run",
|
|
79
|
+
"test:coverage": "vitest run --coverage --coverage.reporter=text --coverage.reporter=html --coverage.reporter=json-summary",
|
|
78
80
|
"typecheck": "tsc --noEmit",
|
|
81
|
+
"test:peer-react18": "vitest run --config vitest.peer-react18.config.ts",
|
|
82
|
+
"test:types:peer-react18": "aui-react18-types",
|
|
79
83
|
"test:watch": "vitest"
|
|
80
84
|
}
|
|
81
85
|
}
|
package/src/index.ts
CHANGED
|
@@ -20,9 +20,13 @@ export type {
|
|
|
20
20
|
ToolApprovalDisplay,
|
|
21
21
|
ToolApprovalOption,
|
|
22
22
|
ToolApprovalOptionKind,
|
|
23
|
+
ToolApprovalAnswer,
|
|
24
|
+
ToolApprovalQuestion,
|
|
25
|
+
ToolApprovalQuestionOption,
|
|
23
26
|
ToolApprovalResponse,
|
|
24
27
|
ToolCallMessagePart,
|
|
25
28
|
ToolCallMessagePartStatus,
|
|
29
|
+
MessagePartTiming,
|
|
26
30
|
ToolCallTiming,
|
|
27
31
|
ToolModelContentPart,
|
|
28
32
|
ImageMessagePart,
|
|
@@ -179,6 +183,11 @@ export * as SuggestionPrimitive from "./primitives/suggestion";
|
|
|
179
183
|
export * as ErrorPrimitive from "./primitives/error";
|
|
180
184
|
|
|
181
185
|
export { groupPartByType, type GroupByContext } from "@assistant-ui/core/react";
|
|
186
|
+
export {
|
|
187
|
+
createThreadRowsSelector,
|
|
188
|
+
type ThreadRow,
|
|
189
|
+
type ThreadRowsOptions,
|
|
190
|
+
} from "@assistant-ui/core/react";
|
|
182
191
|
export { unstable_useThreadMessageIds } from "@assistant-ui/core/react";
|
|
183
192
|
|
|
184
193
|
// Re-export shared providers from core/react
|
|
@@ -404,9 +413,17 @@ export type {
|
|
|
404
413
|
ThreadSuggestion,
|
|
405
414
|
ToolCallMessagePartMcpMetadata,
|
|
406
415
|
ToolExecutionStatus,
|
|
416
|
+
DirectiveFormatter,
|
|
417
|
+
DirectiveSegment,
|
|
418
|
+
TriggerAdapter,
|
|
419
|
+
TriggerCategory,
|
|
420
|
+
TriggerItem,
|
|
407
421
|
Unstable_DirectiveFormatter,
|
|
408
422
|
Unstable_DirectiveSegment,
|
|
409
423
|
Unstable_TriggerItem,
|
|
410
424
|
VoiceSessionState,
|
|
411
425
|
} from "@assistant-ui/core";
|
|
412
|
-
export {
|
|
426
|
+
export {
|
|
427
|
+
defaultDirectiveFormatter,
|
|
428
|
+
unstable_defaultDirectiveFormatter,
|
|
429
|
+
} from "@assistant-ui/core";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { act, type ReactElement } from "react";
|
|
1
|
+
import { act, useState, type ReactElement } from "react";
|
|
2
2
|
import { createRoot, type Root } from "react-dom/client";
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
4
|
import { MessageContent } from "./MessageContent";
|
|
@@ -79,6 +79,51 @@ describe("MessageContent", () => {
|
|
|
79
79
|
});
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
+
it("keeps a text seed while streaming and resets it for a replacement id", async () => {
|
|
83
|
+
const SeededText = ({ text }: { text: string }) => {
|
|
84
|
+
const [seed] = useState(text);
|
|
85
|
+
return <span>{`${seed}:${text}`}</span>;
|
|
86
|
+
};
|
|
87
|
+
const renderText: NonNullable<
|
|
88
|
+
Parameters<typeof MessageContent>[0]["renderText"]
|
|
89
|
+
> = ({ part }) => <SeededText text={part.text} />;
|
|
90
|
+
|
|
91
|
+
h.state.message.content = [{ type: "text", id: "p1", text: "old" }];
|
|
92
|
+
await mount({ renderText });
|
|
93
|
+
h.state.message.content = [
|
|
94
|
+
{ type: "text", id: "p1", text: "old streamed" },
|
|
95
|
+
];
|
|
96
|
+
await mount({ renderText });
|
|
97
|
+
expect(container.textContent).toBe("old:old streamed");
|
|
98
|
+
|
|
99
|
+
h.state.message.content = [{ type: "text", id: "p2", text: "new" }];
|
|
100
|
+
await mount({ renderText });
|
|
101
|
+
expect(container.textContent).toBe("new:new");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("keeps each seed with its id when identified parts swap", async () => {
|
|
105
|
+
const SeededText = ({ text }: { text: string }) => {
|
|
106
|
+
const [seed] = useState(text);
|
|
107
|
+
return <span>{`${seed}:${text}`}</span>;
|
|
108
|
+
};
|
|
109
|
+
const renderText: NonNullable<
|
|
110
|
+
Parameters<typeof MessageContent>[0]["renderText"]
|
|
111
|
+
> = ({ part }) => <SeededText text={part.text} />;
|
|
112
|
+
h.state.message.content = [
|
|
113
|
+
{ type: "text", id: "p1", text: "first" },
|
|
114
|
+
{ type: "text", id: "p2", text: "second" },
|
|
115
|
+
];
|
|
116
|
+
await mount({ renderText });
|
|
117
|
+
h.state.message.content = [
|
|
118
|
+
{ type: "text", id: "p2", text: "second updated" },
|
|
119
|
+
{ type: "text", id: "p1", text: "first updated" },
|
|
120
|
+
];
|
|
121
|
+
await mount({ renderText });
|
|
122
|
+
expect(
|
|
123
|
+
Array.from(container.querySelectorAll("span"), (el) => el.textContent),
|
|
124
|
+
).toEqual(["second:second updated", "first:first updated"]);
|
|
125
|
+
});
|
|
126
|
+
|
|
82
127
|
it("renders a text part through the default text renderer", async () => {
|
|
83
128
|
h.state.message.content = [{ type: "text", text: "hello world" }];
|
|
84
129
|
await mount();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReactElement, Fragment, useMemo } from "react";
|
|
2
2
|
import { Text } from "react-native";
|
|
3
|
+
import { getMessagePartKeys } from "@assistant-ui/core/internal";
|
|
3
4
|
import type {
|
|
4
5
|
ThreadUserMessagePart,
|
|
5
6
|
ThreadAssistantMessagePart,
|
|
@@ -124,11 +125,12 @@ export const MessageContent = ({
|
|
|
124
125
|
renderData,
|
|
125
126
|
}: MessageContentProps) => {
|
|
126
127
|
const content = useAuiState((s) => s.message.parts);
|
|
128
|
+
const partKeys = getMessagePartKeys(content);
|
|
127
129
|
|
|
128
130
|
return (
|
|
129
131
|
<>
|
|
130
132
|
{content.map((part, index) => {
|
|
131
|
-
const key =
|
|
133
|
+
const key = partKeys[index];
|
|
132
134
|
switch (part.type) {
|
|
133
135
|
case "text":
|
|
134
136
|
return (
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { act, type ReactElement } from "react";
|
|
2
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { Text } from "react-native";
|
|
5
|
+
import * as MessagePrimitive from "./message";
|
|
6
|
+
|
|
7
|
+
const h = vi.hoisted(() => ({
|
|
8
|
+
message: { metadata: { custom: {} } as { custom: Record<string, unknown> } },
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
vi.mock("@assistant-ui/store", async (importOriginal) => {
|
|
12
|
+
const actual = await importOriginal<typeof import("@assistant-ui/store")>();
|
|
13
|
+
return {
|
|
14
|
+
...actual,
|
|
15
|
+
useAuiState: <T,>(selector: (state: typeof h) => T) => selector(h),
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
(globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
|
|
20
|
+
|
|
21
|
+
describe("MessagePrimitive.Quote", () => {
|
|
22
|
+
let container: HTMLDivElement;
|
|
23
|
+
let root: Root;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
h.message = { metadata: { custom: {} } };
|
|
27
|
+
container = document.createElement("div");
|
|
28
|
+
document.body.appendChild(container);
|
|
29
|
+
root = createRoot(container);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(async () => {
|
|
33
|
+
await act(async () => {
|
|
34
|
+
root.unmount();
|
|
35
|
+
});
|
|
36
|
+
container.remove();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const render = async (element: ReactElement) => {
|
|
40
|
+
await act(async () => {
|
|
41
|
+
root.render(element);
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
it("renders nothing for a message without a quote", async () => {
|
|
46
|
+
await render(
|
|
47
|
+
<MessagePrimitive.Quote>
|
|
48
|
+
{({ text }) => <Text testID="quote">{text}</Text>}
|
|
49
|
+
</MessagePrimitive.Quote>,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
expect(container.querySelector('[data-testid="quote"]')).toBeNull();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("passes the quote text and source message id to its child", async () => {
|
|
56
|
+
h.message = {
|
|
57
|
+
metadata: { custom: { quote: { text: "quoted text", messageId: "m1" } } },
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
await render(
|
|
61
|
+
<MessagePrimitive.Quote>
|
|
62
|
+
{({ text, messageId }) => (
|
|
63
|
+
<Text testID="quote">
|
|
64
|
+
{messageId}: {text}
|
|
65
|
+
</Text>
|
|
66
|
+
)}
|
|
67
|
+
</MessagePrimitive.Quote>,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
expect(container.querySelector('[data-testid="quote"]')?.textContent).toBe(
|
|
71
|
+
"m1: quoted text",
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -11,6 +11,7 @@ export {
|
|
|
11
11
|
MessagePrimitivePartByIndex as PartByIndex,
|
|
12
12
|
} from "./message/MessageParts";
|
|
13
13
|
export { MessagePrimitiveGroupedParts as GroupedParts } from "@assistant-ui/core/react";
|
|
14
|
+
export { MessagePrimitiveQuote as Quote } from "@assistant-ui/core/react";
|
|
14
15
|
export {
|
|
15
16
|
MessageIf as If,
|
|
16
17
|
type MessageIfProps as IfProps,
|
|
@@ -10,13 +10,18 @@ type Msg = { id: string; role: string };
|
|
|
10
10
|
|
|
11
11
|
const h = vi.hoisted(() => ({
|
|
12
12
|
state: {
|
|
13
|
-
thread: {
|
|
13
|
+
thread: {
|
|
14
|
+
messages: [] as Msg[],
|
|
15
|
+
hasEarlier: false,
|
|
16
|
+
isLoadingEarlier: false,
|
|
17
|
+
},
|
|
14
18
|
message: { role: "user" as string, composer: { isEditing: false } },
|
|
15
19
|
},
|
|
16
20
|
itemState: { role: "user" } as { role: string; parts?: unknown[] },
|
|
17
21
|
events: {} as Record<string, Set<() => void>>,
|
|
18
22
|
flatListProps: null as Record<string, unknown> | null,
|
|
19
23
|
scrollToOffset: vi.fn(),
|
|
24
|
+
loadEarlier: vi.fn(async () => {}),
|
|
20
25
|
}));
|
|
21
26
|
|
|
22
27
|
vi.mock("react-native", async (importOriginal) => {
|
|
@@ -79,7 +84,9 @@ vi.mock("react-native", async (importOriginal) => {
|
|
|
79
84
|
vi.mock("@assistant-ui/store", async () => {
|
|
80
85
|
const React = await import("react");
|
|
81
86
|
|
|
87
|
+
const aui = { thread: { loadEarlier: h.loadEarlier } };
|
|
82
88
|
return {
|
|
89
|
+
useAui: () => aui,
|
|
83
90
|
useAuiState: <T,>(selector: (s: typeof h.state) => T) => selector(h.state),
|
|
84
91
|
useAuiEvent: (
|
|
85
92
|
selector: string | { scope: string; event: string },
|
|
@@ -400,6 +407,26 @@ describe("ThreadMessages", () => {
|
|
|
400
407
|
});
|
|
401
408
|
|
|
402
409
|
describe("MessagesFlatList history", () => {
|
|
410
|
+
it("pages through the runtime when no history prop is given", async () => {
|
|
411
|
+
h.state.thread.hasEarlier = true;
|
|
412
|
+
try {
|
|
413
|
+
await mountFlatList({ components: messageComponents });
|
|
414
|
+
getFlatListProps().onStartReached?.({ distanceFromStart: 0 });
|
|
415
|
+
expect(h.loadEarlier).toHaveBeenCalledOnce();
|
|
416
|
+
|
|
417
|
+
h.state.thread.isLoadingEarlier = true;
|
|
418
|
+
await mountFlatList({ components: messageComponents });
|
|
419
|
+
expect(getFlatListProps().onStartReached).toBeUndefined();
|
|
420
|
+
} finally {
|
|
421
|
+
h.state.thread.hasEarlier = false;
|
|
422
|
+
h.state.thread.isLoadingEarlier = false;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
await mountFlatList({ components: messageComponents });
|
|
426
|
+
expect(getFlatListProps().onStartReached).toBeUndefined();
|
|
427
|
+
expect(getFlatListProps().onStartReachedThreshold).toBeUndefined();
|
|
428
|
+
});
|
|
429
|
+
|
|
403
430
|
it("wires loadMore only while history can load more", async () => {
|
|
404
431
|
const loadMore = vi.fn();
|
|
405
432
|
|