@assistant-ui/react 0.11.36 → 0.11.37
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/primitives/index.d.ts +1 -0
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +2 -0
- package/dist/primitives/index.js.map +1 -1
- package/dist/primitives/message/MessageParts.d.ts +31 -1
- package/dist/primitives/message/MessageParts.d.ts.map +1 -1
- package/dist/primitives/message/MessageParts.js +66 -23
- package/dist/primitives/message/MessageParts.js.map +1 -1
- package/dist/primitives/reasoning/index.d.ts +2 -0
- package/dist/primitives/reasoning/index.d.ts.map +1 -0
- package/dist/primitives/reasoning/index.js +6 -0
- package/dist/primitives/reasoning/index.js.map +1 -0
- package/dist/primitives/reasoning/useScrollLock.d.ts +29 -0
- package/dist/primitives/reasoning/useScrollLock.d.ts.map +1 -0
- package/dist/primitives/reasoning/useScrollLock.js +50 -0
- package/dist/primitives/reasoning/useScrollLock.js.map +1 -0
- package/dist/types/MessagePartComponentTypes.d.ts +6 -1
- package/dist/types/MessagePartComponentTypes.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/primitives/index.ts +1 -0
- package/src/primitives/message/MessageParts.tsx +111 -30
- package/src/primitives/reasoning/index.ts +1 -0
- package/src/primitives/reasoning/useScrollLock.tsx +86 -0
- package/src/types/MessagePartComponentTypes.tsx +7 -1
- package/src/types/index.ts +2 -0
|
@@ -15,4 +15,5 @@ export { useMessagePartSource } from "./messagePart/useMessagePartSource";
|
|
|
15
15
|
export { useMessagePartFile } from "./messagePart/useMessagePartFile";
|
|
16
16
|
export { useMessagePartImage } from "./messagePart/useMessagePartImage";
|
|
17
17
|
export { useThreadViewportAutoScroll } from "./thread/useThreadViewportAutoScroll";
|
|
18
|
+
export { useScrollLock } from "./reasoning";
|
|
18
19
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/primitives/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,kBAAkB,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,uBAAuB,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,mBAAmB,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,qBAAqB,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,iBAAiB,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,oBAAoB,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,cAAc,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,gBAAgB,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,eAAe,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,mBAAmB,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,uBAAuB,MAAM,kBAAkB,CAAC;AAE5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/primitives/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,kBAAkB,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,uBAAuB,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,mBAAmB,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,qBAAqB,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,iBAAiB,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,oBAAoB,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,cAAc,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,gBAAgB,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,eAAe,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,mBAAmB,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,uBAAuB,MAAM,kBAAkB,CAAC;AAE5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/primitives/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { useMessagePartSource } from "./messagePart/useMessagePartSource.js";
|
|
|
16
16
|
import { useMessagePartFile } from "./messagePart/useMessagePartFile.js";
|
|
17
17
|
import { useMessagePartImage } from "./messagePart/useMessagePartImage.js";
|
|
18
18
|
import { useThreadViewportAutoScroll } from "./thread/useThreadViewportAutoScroll.js";
|
|
19
|
+
import { useScrollLock } from "./reasoning/index.js";
|
|
19
20
|
export {
|
|
20
21
|
ActionBarPrimitive,
|
|
21
22
|
AssistantModalPrimitive,
|
|
@@ -33,6 +34,7 @@ export {
|
|
|
33
34
|
useMessagePartReasoning,
|
|
34
35
|
useMessagePartSource,
|
|
35
36
|
useMessagePartText,
|
|
37
|
+
useScrollLock,
|
|
36
38
|
useThreadViewportAutoScroll
|
|
37
39
|
};
|
|
38
40
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/primitives/index.ts"],"sourcesContent":["export * as ActionBarPrimitive from \"./actionBar\";\nexport * as AssistantModalPrimitive from \"./assistantModal\";\nexport * as AttachmentPrimitive from \"./attachment\";\nexport * as BranchPickerPrimitive from \"./branchPicker\";\nexport * as ComposerPrimitive from \"./composer\";\nexport * as MessagePartPrimitive from \"./messagePart\";\nexport * as ErrorPrimitive from \"./error\";\nexport * as MessagePrimitive from \"./message\";\nexport * as ThreadPrimitive from \"./thread\";\nexport * as ThreadListPrimitive from \"./threadList\";\nexport * as ThreadListItemPrimitive from \"./threadListItem\";\n\nexport { useMessagePartText } from \"./messagePart/useMessagePartText\";\nexport { useMessagePartReasoning } from \"./messagePart/useMessagePartReasoning\";\nexport { useMessagePartSource } from \"./messagePart/useMessagePartSource\";\nexport { useMessagePartFile } from \"./messagePart/useMessagePartFile\";\nexport { useMessagePartImage } from \"./messagePart/useMessagePartImage\";\nexport { useThreadViewportAutoScroll } from \"./thread/useThreadViewportAutoScroll\";\n"],"mappings":";AAAA,YAAY,wBAAwB;AACpC,YAAY,6BAA6B;AACzC,YAAY,yBAAyB;AACrC,YAAY,2BAA2B;AACvC,YAAY,uBAAuB;AACnC,YAAY,0BAA0B;AACtC,YAAY,oBAAoB;AAChC,YAAY,sBAAsB;AAClC,YAAY,qBAAqB;AACjC,YAAY,yBAAyB;AACrC,YAAY,6BAA6B;AAEzC,SAAS,0BAA0B;AACnC,SAAS,+BAA+B;AACxC,SAAS,4BAA4B;AACrC,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC,SAAS,mCAAmC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/primitives/index.ts"],"sourcesContent":["export * as ActionBarPrimitive from \"./actionBar\";\nexport * as AssistantModalPrimitive from \"./assistantModal\";\nexport * as AttachmentPrimitive from \"./attachment\";\nexport * as BranchPickerPrimitive from \"./branchPicker\";\nexport * as ComposerPrimitive from \"./composer\";\nexport * as MessagePartPrimitive from \"./messagePart\";\nexport * as ErrorPrimitive from \"./error\";\nexport * as MessagePrimitive from \"./message\";\nexport * as ThreadPrimitive from \"./thread\";\nexport * as ThreadListPrimitive from \"./threadList\";\nexport * as ThreadListItemPrimitive from \"./threadListItem\";\n\nexport { useMessagePartText } from \"./messagePart/useMessagePartText\";\nexport { useMessagePartReasoning } from \"./messagePart/useMessagePartReasoning\";\nexport { useMessagePartSource } from \"./messagePart/useMessagePartSource\";\nexport { useMessagePartFile } from \"./messagePart/useMessagePartFile\";\nexport { useMessagePartImage } from \"./messagePart/useMessagePartImage\";\nexport { useThreadViewportAutoScroll } from \"./thread/useThreadViewportAutoScroll\";\nexport { useScrollLock } from \"./reasoning\";\n"],"mappings":";AAAA,YAAY,wBAAwB;AACpC,YAAY,6BAA6B;AACzC,YAAY,yBAAyB;AACrC,YAAY,2BAA2B;AACvC,YAAY,uBAAuB;AACnC,YAAY,0BAA0B;AACtC,YAAY,oBAAoB;AAChC,YAAY,sBAAsB;AAClC,YAAY,qBAAqB;AACjC,YAAY,yBAAyB;AACrC,YAAY,6BAA6B;AAEzC,SAAS,0BAA0B;AACnC,SAAS,+BAA+B;AACxC,SAAS,4BAA4B;AACrC,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC,SAAS,mCAAmC;AAC5C,SAAS,qBAAqB;","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ComponentType, type FC, PropsWithChildren } from "react";
|
|
2
|
-
import type { Unstable_AudioMessagePartComponent, EmptyMessagePartComponent, TextMessagePartComponent, ImageMessagePartComponent, SourceMessagePartComponent, ToolCallMessagePartComponent, ToolCallMessagePartProps, FileMessagePartComponent, ReasoningMessagePartComponent } from "../../types/MessagePartComponentTypes";
|
|
2
|
+
import type { Unstable_AudioMessagePartComponent, EmptyMessagePartComponent, TextMessagePartComponent, ImageMessagePartComponent, SourceMessagePartComponent, ToolCallMessagePartComponent, ToolCallMessagePartProps, FileMessagePartComponent, ReasoningMessagePartComponent, ReasoningGroupComponent } from "../../types/MessagePartComponentTypes";
|
|
3
3
|
export declare namespace MessagePrimitiveParts {
|
|
4
4
|
type Props = {
|
|
5
5
|
/**
|
|
@@ -85,6 +85,36 @@ export declare namespace MessagePrimitiveParts {
|
|
|
85
85
|
startIndex: number;
|
|
86
86
|
endIndex: number;
|
|
87
87
|
}>>;
|
|
88
|
+
/**
|
|
89
|
+
* Component for rendering grouped reasoning parts.
|
|
90
|
+
*
|
|
91
|
+
* When provided, this component will automatically wrap reasoning message parts
|
|
92
|
+
* (one or more consecutive) in a group container. Each reasoning part inside
|
|
93
|
+
* renders its own text independently - no text merging occurs.
|
|
94
|
+
*
|
|
95
|
+
* The component receives:
|
|
96
|
+
* - `startIndex`: The index of the first reasoning part in the group
|
|
97
|
+
* - `endIndex`: The index of the last reasoning part in the group
|
|
98
|
+
* - `children`: The rendered Reasoning components (one per part)
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```tsx
|
|
102
|
+
* // Collapsible reasoning group
|
|
103
|
+
* ReasoningGroup: ({ children }) => (
|
|
104
|
+
* <details className="reasoning-group">
|
|
105
|
+
* <summary>Reasoning</summary>
|
|
106
|
+
* <div className="reasoning-content">
|
|
107
|
+
* {children}
|
|
108
|
+
* </div>
|
|
109
|
+
* </details>
|
|
110
|
+
* )
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* @param startIndex - Index of the first reasoning part in the group
|
|
114
|
+
* @param endIndex - Index of the last reasoning part in the group
|
|
115
|
+
* @param children - Rendered reasoning part components
|
|
116
|
+
*/
|
|
117
|
+
ReasoningGroup?: ReasoningGroupComponent;
|
|
88
118
|
} | undefined;
|
|
89
119
|
};
|
|
90
120
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageParts.d.ts","sourceRoot":"","sources":["../../../src/primitives/message/MessageParts.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,EAAE,EAEP,iBAAiB,EAElB,MAAM,OAAO,CAAC;AASf,OAAO,KAAK,EACV,kCAAkC,EAClC,yBAAyB,EACzB,wBAAwB,EACxB,yBAAyB,EACzB,0BAA0B,EAC1B,4BAA4B,EAC5B,wBAAwB,EACxB,wBAAwB,EACxB,6BAA6B,
|
|
1
|
+
{"version":3,"file":"MessageParts.d.ts","sourceRoot":"","sources":["../../../src/primitives/message/MessageParts.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,EAAE,EAEP,iBAAiB,EAElB,MAAM,OAAO,CAAC;AASf,OAAO,KAAK,EACV,kCAAkC,EAClC,yBAAyB,EACzB,wBAAwB,EACxB,yBAAyB,EACzB,0BAA0B,EAC1B,4BAA4B,EAC5B,wBAAwB,EACxB,wBAAwB,EACxB,6BAA6B,EAC7B,uBAAuB,EACxB,MAAM,uCAAuC,CAAC;AA6F/C,yBAAiB,qBAAqB,CAAC;IACrC,KAAY,KAAK,GAAG;QAClB;;;;;WAKG;QACH,UAAU,CAAC,EACP;YACE,6CAA6C;YAC7C,KAAK,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;YAC9C,2CAA2C;YAC3C,IAAI,CAAC,EAAE,wBAAwB,GAAG,SAAS,CAAC;YAC5C,mEAAmE;YACnE,SAAS,CAAC,EAAE,6BAA6B,GAAG,SAAS,CAAC;YACtD,6CAA6C;YAC7C,MAAM,CAAC,EAAE,0BAA0B,GAAG,SAAS,CAAC;YAChD,4CAA4C;YAC5C,KAAK,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;YAC9C,2CAA2C;YAC3C,IAAI,CAAC,EAAE,wBAAwB,GAAG,SAAS,CAAC;YAC5C,2DAA2D;YAC3D,cAAc,CAAC,EAAE,kCAAkC,GAAG,SAAS,CAAC;YAChE,4CAA4C;YAC5C,KAAK,CAAC,EACF;gBACE,qDAAqD;gBACrD,OAAO,CAAC,EACJ,MAAM,CAAC,MAAM,EAAE,4BAA4B,GAAG,SAAS,CAAC,GACxD,SAAS,CAAC;gBACd,gDAAgD;gBAChD,QAAQ,CAAC,EAAE,aAAa,CAAC,wBAAwB,CAAC,GAAG,SAAS,CAAC;aAChE,GACD;gBACE,qDAAqD;gBACrD,QAAQ,EAAE,aAAa,CAAC,wBAAwB,CAAC,CAAC;aACnD,GACD,SAAS,CAAC;YAEd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eA+CG;YACH,SAAS,CAAC,EAAE,aAAa,CACvB,iBAAiB,CAAC;gBAAE,UAAU,EAAE,MAAM,CAAC;gBAAC,QAAQ,EAAE,MAAM,CAAA;aAAE,CAAC,CAC5D,CAAC;YAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;eA4BG;YACH,cAAc,CAAC,EAAE,uBAAuB,CAAC;SAC1C,GACD,SAAS,CAAC;KACf,CAAC;CACH;AAoGD,yBAAiB,2BAA2B,CAAC;IAC3C,KAAY,KAAK,GAAG;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,qBAAqB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;KACvD,CAAC;CACH;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,2BAA2B,EAAE,EAAE,CAAC,2BAA2B,CAAC,KAAK,CAoB3E,CAAC;AAyCJ;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAAE,CAAC,qBAAqB,CAAC,KAAK,CAsEjE,CAAC"}
|
|
@@ -16,34 +16,55 @@ import { MessagePartPrimitiveImage } from "../messagePart/MessagePartImage.js";
|
|
|
16
16
|
import { MessagePartPrimitiveInProgress } from "../messagePart/MessagePartInProgress.js";
|
|
17
17
|
import { useShallow } from "zustand/shallow";
|
|
18
18
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
19
|
+
var createGroupState = (groupType) => {
|
|
20
|
+
let start = -1;
|
|
21
|
+
return {
|
|
22
|
+
startGroup: (index) => {
|
|
23
|
+
if (start === -1) {
|
|
24
|
+
start = index;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
endGroup: (endIndex, ranges) => {
|
|
28
|
+
if (start !== -1) {
|
|
29
|
+
ranges.push({
|
|
30
|
+
type: groupType,
|
|
31
|
+
startIndex: start,
|
|
32
|
+
endIndex
|
|
33
|
+
});
|
|
34
|
+
start = -1;
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
finalize: (endIndex, ranges) => {
|
|
38
|
+
if (start !== -1) {
|
|
39
|
+
ranges.push({
|
|
40
|
+
type: groupType,
|
|
41
|
+
startIndex: start,
|
|
42
|
+
endIndex
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
};
|
|
19
48
|
var groupMessageParts = (messageTypes) => {
|
|
20
49
|
const ranges = [];
|
|
21
|
-
|
|
50
|
+
const toolGroup = createGroupState("toolGroup");
|
|
51
|
+
const reasoningGroup = createGroupState("reasoningGroup");
|
|
22
52
|
for (let i = 0; i < messageTypes.length; i++) {
|
|
23
53
|
const type = messageTypes[i];
|
|
24
54
|
if (type === "tool-call") {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
55
|
+
reasoningGroup.endGroup(i - 1, ranges);
|
|
56
|
+
toolGroup.startGroup(i);
|
|
57
|
+
} else if (type === "reasoning") {
|
|
58
|
+
toolGroup.endGroup(i - 1, ranges);
|
|
59
|
+
reasoningGroup.startGroup(i);
|
|
28
60
|
} else {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
type: "toolGroup",
|
|
32
|
-
startIndex: currentToolGroupStart,
|
|
33
|
-
endIndex: i - 1
|
|
34
|
-
});
|
|
35
|
-
currentToolGroupStart = -1;
|
|
36
|
-
}
|
|
61
|
+
toolGroup.endGroup(i - 1, ranges);
|
|
62
|
+
reasoningGroup.endGroup(i - 1, ranges);
|
|
37
63
|
ranges.push({ type: "single", index: i });
|
|
38
64
|
}
|
|
39
65
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
type: "toolGroup",
|
|
43
|
-
startIndex: currentToolGroupStart,
|
|
44
|
-
endIndex: messageTypes.length - 1
|
|
45
|
-
});
|
|
46
|
-
}
|
|
66
|
+
toolGroup.finalize(messageTypes.length - 1, ranges);
|
|
67
|
+
reasoningGroup.finalize(messageTypes.length - 1, ranges);
|
|
47
68
|
return ranges;
|
|
48
69
|
};
|
|
49
70
|
var useMessagePartsGroups = () => {
|
|
@@ -79,7 +100,8 @@ var defaultComponents = {
|
|
|
79
100
|
Image: () => /* @__PURE__ */ jsx(MessagePartPrimitiveImage, {}),
|
|
80
101
|
File: () => null,
|
|
81
102
|
Unstable_Audio: () => null,
|
|
82
|
-
ToolGroup: ({ children }) => children
|
|
103
|
+
ToolGroup: ({ children }) => children,
|
|
104
|
+
ReasoningGroup: ({ children }) => children
|
|
83
105
|
};
|
|
84
106
|
var MessagePartComponent = ({
|
|
85
107
|
components: {
|
|
@@ -135,7 +157,7 @@ var MessagePrimitivePartByIndex = memo(
|
|
|
135
157
|
({ index, components }) => {
|
|
136
158
|
return /* @__PURE__ */ jsx(PartByIndexProvider, { index, children: /* @__PURE__ */ jsx(MessagePartComponent, { components }) });
|
|
137
159
|
},
|
|
138
|
-
(prev, next) => prev.index === next.index && prev.components?.Text === next.components?.Text && prev.components?.Reasoning === next.components?.Reasoning && prev.components?.Source === next.components?.Source && prev.components?.Image === next.components?.Image && prev.components?.File === next.components?.File && prev.components?.Unstable_Audio === next.components?.Unstable_Audio && prev.components?.tools === next.components?.tools && prev.components?.ToolGroup === next.components?.ToolGroup
|
|
160
|
+
(prev, next) => prev.index === next.index && prev.components?.Text === next.components?.Text && prev.components?.Reasoning === next.components?.Reasoning && prev.components?.Source === next.components?.Source && prev.components?.Image === next.components?.Image && prev.components?.File === next.components?.File && prev.components?.Unstable_Audio === next.components?.Unstable_Audio && prev.components?.tools === next.components?.tools && prev.components?.ToolGroup === next.components?.ToolGroup && prev.components?.ReasoningGroup === next.components?.ReasoningGroup
|
|
139
161
|
);
|
|
140
162
|
MessagePrimitivePartByIndex.displayName = "MessagePrimitive.PartByIndex";
|
|
141
163
|
var EmptyPartFallback = ({ status, component: Component }) => {
|
|
@@ -182,7 +204,7 @@ var MessagePrimitiveParts = ({
|
|
|
182
204
|
},
|
|
183
205
|
range.index
|
|
184
206
|
);
|
|
185
|
-
} else {
|
|
207
|
+
} else if (range.type === "toolGroup") {
|
|
186
208
|
const ToolGroupComponent = components.ToolGroup ?? defaultComponents.ToolGroup;
|
|
187
209
|
return /* @__PURE__ */ jsx(
|
|
188
210
|
ToolGroupComponent,
|
|
@@ -201,7 +223,28 @@ var MessagePrimitiveParts = ({
|
|
|
201
223
|
)
|
|
202
224
|
)
|
|
203
225
|
},
|
|
204
|
-
range.startIndex
|
|
226
|
+
`tool-${range.startIndex}`
|
|
227
|
+
);
|
|
228
|
+
} else {
|
|
229
|
+
const ReasoningGroupComponent = components.ReasoningGroup ?? defaultComponents.ReasoningGroup;
|
|
230
|
+
return /* @__PURE__ */ jsx(
|
|
231
|
+
ReasoningGroupComponent,
|
|
232
|
+
{
|
|
233
|
+
startIndex: range.startIndex,
|
|
234
|
+
endIndex: range.endIndex,
|
|
235
|
+
children: Array.from(
|
|
236
|
+
{ length: range.endIndex - range.startIndex + 1 },
|
|
237
|
+
(_, i) => /* @__PURE__ */ jsx(
|
|
238
|
+
MessagePrimitivePartByIndex,
|
|
239
|
+
{
|
|
240
|
+
index: range.startIndex + i,
|
|
241
|
+
components
|
|
242
|
+
},
|
|
243
|
+
i
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
},
|
|
247
|
+
`reasoning-${range.startIndex}`
|
|
205
248
|
);
|
|
206
249
|
}
|
|
207
250
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/primitives/message/MessageParts.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n type FC,\n memo,\n PropsWithChildren,\n useMemo,\n} from \"react\";\nimport {\n useAssistantState,\n useAssistantApi,\n PartByIndexProvider,\n TextMessagePartProvider,\n} from \"../../context\";\nimport { MessagePartPrimitiveText } from \"../messagePart/MessagePartText\";\nimport { MessagePartPrimitiveImage } from \"../messagePart/MessagePartImage\";\nimport type {\n Unstable_AudioMessagePartComponent,\n EmptyMessagePartComponent,\n TextMessagePartComponent,\n ImageMessagePartComponent,\n SourceMessagePartComponent,\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n FileMessagePartComponent,\n ReasoningMessagePartComponent,\n} from \"../../types/MessagePartComponentTypes\";\nimport { MessagePartPrimitiveInProgress } from \"../messagePart/MessagePartInProgress\";\nimport { MessagePartStatus } from \"../../types/AssistantTypes\";\nimport { useShallow } from \"zustand/shallow\";\n\ntype MessagePartRange =\n | { type: \"single\"; index: number }\n | { type: \"toolGroup\"; startIndex: number; endIndex: number };\n\n/**\n * Groups consecutive tool-call message parts into ranges.\n * Always groups tool calls, even if there's only one.\n */\nconst groupMessageParts = (\n messageTypes: readonly string[],\n): MessagePartRange[] => {\n const ranges: MessagePartRange[] = [];\n let currentToolGroupStart = -1;\n\n for (let i = 0; i < messageTypes.length; i++) {\n const type = messageTypes[i];\n\n if (type === \"tool-call\") {\n // Start a new tool group if we haven't started one\n if (currentToolGroupStart === -1) {\n currentToolGroupStart = i;\n }\n } else {\n // End current tool group if it exists\n if (currentToolGroupStart !== -1) {\n ranges.push({\n type: \"toolGroup\",\n startIndex: currentToolGroupStart,\n endIndex: i - 1,\n });\n currentToolGroupStart = -1;\n }\n\n // Add non-tool-call part individually\n ranges.push({ type: \"single\", index: i });\n }\n }\n\n // Handle any remaining tool group at the end\n if (currentToolGroupStart !== -1) {\n ranges.push({\n type: \"toolGroup\",\n startIndex: currentToolGroupStart,\n endIndex: messageTypes.length - 1,\n });\n }\n\n return ranges;\n};\n\nconst useMessagePartsGroups = (): MessagePartRange[] => {\n const messageTypes = useAssistantState(\n useShallow((s) => s.message.parts.map((c: any) => c.type)),\n );\n\n return useMemo(() => {\n if (messageTypes.length === 0) {\n return [];\n }\n return groupMessageParts(messageTypes);\n }, [messageTypes]);\n};\n\nexport namespace MessagePrimitiveParts {\n export type Props = {\n /**\n * Component configuration for rendering different types of message content.\n *\n * You can provide custom components for each content type (text, image, file, etc.)\n * and configure tool rendering behavior. If not provided, default components will be used.\n */\n components?:\n | {\n /** Component for rendering empty messages */\n Empty?: EmptyMessagePartComponent | undefined;\n /** Component for rendering text content */\n Text?: TextMessagePartComponent | undefined;\n /** Component for rendering reasoning content (typically hidden) */\n Reasoning?: ReasoningMessagePartComponent | undefined;\n /** Component for rendering source content */\n Source?: SourceMessagePartComponent | undefined;\n /** Component for rendering image content */\n Image?: ImageMessagePartComponent | undefined;\n /** Component for rendering file content */\n File?: FileMessagePartComponent | undefined;\n /** Component for rendering audio content (experimental) */\n Unstable_Audio?: Unstable_AudioMessagePartComponent | undefined;\n /** Configuration for tool call rendering */\n tools?:\n | {\n /** Map of tool names to their specific components */\n by_name?:\n | Record<string, ToolCallMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unregistered tools */\n Fallback?: ComponentType<ToolCallMessagePartProps> | undefined;\n }\n | {\n /** Override component that handles all tool calls */\n Override: ComponentType<ToolCallMessagePartProps>;\n }\n | undefined;\n\n /**\n * Component for rendering grouped consecutive tool calls.\n *\n * When provided, this component will automatically wrap consecutive tool-call\n * message parts, allowing you to create collapsible sections, custom styling,\n * or other grouped presentations for multiple tool calls.\n *\n * The component receives:\n * - `startIndex`: The index of the first tool call in the group\n * - `endIndex`: The index of the last tool call in the group\n * - `children`: The rendered tool call components\n *\n * @example\n * ```tsx\n * // Collapsible tool group\n * ToolGroup: ({ startIndex, endIndex, children }) => (\n * <details className=\"tool-group\">\n * <summary>\n * {endIndex - startIndex + 1} tool calls\n * </summary>\n * <div className=\"tool-group-content\">\n * {children}\n * </div>\n * </details>\n * )\n * ```\n *\n * @example\n * ```tsx\n * // Custom styled tool group with header\n * ToolGroup: ({ startIndex, endIndex, children }) => (\n * <div className=\"border rounded-lg p-4 my-2\">\n * <div className=\"text-sm text-gray-600 mb-2\">\n * Tool execution #{startIndex + 1}-{endIndex + 1}\n * </div>\n * <div className=\"space-y-2\">\n * {children}\n * </div>\n * </div>\n * )\n * ```\n *\n * @param startIndex - Index of the first tool call in the group\n * @param endIndex - Index of the last tool call in the group\n * @param children - Rendered tool call components to display within the group\n *\n * @deprecated This feature is still experimental and subject to change.\n */\n ToolGroup?: ComponentType<\n PropsWithChildren<{ startIndex: number; endIndex: number }>\n >;\n }\n | undefined;\n };\n}\n\nconst ToolUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: ToolCallMessagePartComponent | undefined;\n} & ToolCallMessagePartProps) => {\n const Render = useAssistantState(({ toolUIs }) => {\n const Render =\n toolUIs.tools[props.toolName] ?? toolUIs.fallback ?? Fallback;\n if (Array.isArray(Render)) return Render[0] ?? Fallback;\n return Render;\n });\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst defaultComponents = {\n Text: () => (\n <p style={{ whiteSpace: \"pre-line\" }}>\n <MessagePartPrimitiveText />\n <MessagePartPrimitiveInProgress>\n <span style={{ fontFamily: \"revert\" }}>{\" \\u25CF\"}</span>\n </MessagePartPrimitiveInProgress>\n </p>\n ),\n Reasoning: () => null,\n Source: () => null,\n Image: () => <MessagePartPrimitiveImage />,\n File: () => null,\n Unstable_Audio: () => null,\n ToolGroup: ({ children }) => children,\n} satisfies MessagePrimitiveParts.Props[\"components\"];\n\ntype MessagePartComponentProps = {\n components: MessagePrimitiveParts.Props[\"components\"];\n};\n\nconst MessagePartComponent: FC<MessagePartComponentProps> = ({\n components: {\n Text = defaultComponents.Text,\n Reasoning = defaultComponents.Reasoning,\n Image = defaultComponents.Image,\n Source = defaultComponents.Source,\n File = defaultComponents.File,\n Unstable_Audio: Audio = defaultComponents.Unstable_Audio,\n tools = {},\n } = {},\n}) => {\n const api = useAssistantApi();\n const part = useAssistantState(({ part }) => part);\n\n const type = part.type;\n if (type === \"tool-call\") {\n const addResult = api.part().addToolResult;\n const resume = api.part().resumeToolCall;\n if (\"Override\" in tools)\n return <tools.Override {...part} addResult={addResult} resume={resume} />;\n const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;\n return (\n <ToolUIDisplay\n {...part}\n Fallback={Tool}\n addResult={addResult}\n resume={resume}\n />\n );\n }\n\n if (part.status?.type === \"requires-action\")\n throw new Error(\"Encountered unexpected requires-action status\");\n\n switch (type) {\n case \"text\":\n return <Text {...part} />;\n\n case \"reasoning\":\n return <Reasoning {...part} />;\n\n case \"source\":\n return <Source {...part} />;\n\n case \"image\":\n // eslint-disable-next-line jsx-a11y/alt-text\n return <Image {...part} />;\n\n case \"file\":\n return <File {...part} />;\n\n case \"audio\":\n return <Audio {...part} />;\n\n default:\n const unhandledType: never = type;\n throw new Error(`Unknown message part type: ${unhandledType}`);\n }\n};\n\nexport namespace MessagePrimitivePartByIndex {\n export type Props = {\n index: number;\n components: MessagePrimitiveParts.Props[\"components\"];\n };\n}\n\n/**\n * Renders a single message part at the specified index.\n *\n * This component provides direct access to render a specific message part\n * within the current message context, using the provided component configuration.\n *\n * @example\n * ```tsx\n * <MessagePrimitive.PartByIndex\n * index={0}\n * components={{\n * Text: MyTextComponent,\n * Image: MyImageComponent\n * }}\n * />\n * ```\n */\nexport const MessagePrimitivePartByIndex: FC<MessagePrimitivePartByIndex.Props> =\n memo(\n ({ index, components }) => {\n return (\n <PartByIndexProvider index={index}>\n <MessagePartComponent components={components} />\n </PartByIndexProvider>\n );\n },\n (prev, next) =>\n prev.index === next.index &&\n prev.components?.Text === next.components?.Text &&\n prev.components?.Reasoning === next.components?.Reasoning &&\n prev.components?.Source === next.components?.Source &&\n prev.components?.Image === next.components?.Image &&\n prev.components?.File === next.components?.File &&\n prev.components?.Unstable_Audio === next.components?.Unstable_Audio &&\n prev.components?.tools === next.components?.tools &&\n prev.components?.ToolGroup === next.components?.ToolGroup,\n );\n\nMessagePrimitivePartByIndex.displayName = \"MessagePrimitive.PartByIndex\";\n\nconst EmptyPartFallback: FC<{\n status: MessagePartStatus;\n component: TextMessagePartComponent;\n}> = ({ status, component: Component }) => {\n return (\n <TextMessagePartProvider text=\"\" isRunning={status.type === \"running\"}>\n <Component type=\"text\" text=\"\" status={status} />\n </TextMessagePartProvider>\n );\n};\n\nconst COMPLETE_STATUS: MessagePartStatus = Object.freeze({\n type: \"complete\",\n});\n\nconst EmptyPartsImpl: FC<MessagePartComponentProps> = ({ components }) => {\n const status = useAssistantState(\n (s) => (s.message.status ?? COMPLETE_STATUS) as MessagePartStatus,\n );\n\n if (components?.Empty) return <components.Empty status={status} />;\n\n return (\n <EmptyPartFallback\n status={status}\n component={components?.Text ?? defaultComponents.Text}\n />\n );\n};\n\nconst EmptyParts = memo(\n EmptyPartsImpl,\n (prev, next) =>\n prev.components?.Empty === next.components?.Empty &&\n prev.components?.Text === next.components?.Text,\n);\n\n/**\n * Renders the parts of a message with support for multiple content types.\n *\n * This component automatically handles different types of message content including\n * text, images, files, tool calls, and more. It provides a flexible component\n * system for customizing how each content type is rendered.\n *\n * @example\n * ```tsx\n * <MessagePrimitive.Parts\n * components={{\n * Text: ({ text }) => <p className=\"message-text\">{text}</p>,\n * Image: ({ image }) => <img src={image} alt=\"Message image\" />,\n * tools: {\n * by_name: {\n * calculator: CalculatorTool,\n * weather: WeatherTool,\n * },\n * Fallback: DefaultToolComponent\n * }\n * }}\n * />\n * ```\n */\nexport const MessagePrimitiveParts: FC<MessagePrimitiveParts.Props> = ({\n components,\n}) => {\n const contentLength = useAssistantState(\n ({ message }) => message.parts.length,\n );\n const messageRanges = useMessagePartsGroups();\n\n const partsElements = useMemo(() => {\n if (contentLength === 0) {\n return <EmptyParts components={components} />;\n }\n\n return messageRanges.map((range) => {\n if (range.type === \"single\") {\n return (\n <MessagePrimitivePartByIndex\n key={range.index}\n index={range.index}\n components={components}\n />\n );\n } else {\n const ToolGroupComponent =\n components!.ToolGroup ?? defaultComponents.ToolGroup;\n return (\n <ToolGroupComponent\n key={range.startIndex}\n startIndex={range.startIndex}\n endIndex={range.endIndex}\n >\n {Array.from(\n { length: range.endIndex - range.startIndex + 1 },\n (_, i) => (\n <MessagePrimitivePartByIndex\n key={i}\n index={range.startIndex + i}\n components={components}\n />\n ),\n )}\n </ToolGroupComponent>\n );\n }\n });\n }, [messageRanges, components, contentLength]);\n\n return <>{partsElements}</>;\n};\n\nMessagePrimitiveParts.displayName = \"MessagePrimitive.Parts\";\n"],"mappings":"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","names":["Render","part"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/primitives/message/MessageParts.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n type FC,\n memo,\n PropsWithChildren,\n useMemo,\n} from \"react\";\nimport {\n useAssistantState,\n useAssistantApi,\n PartByIndexProvider,\n TextMessagePartProvider,\n} from \"../../context\";\nimport { MessagePartPrimitiveText } from \"../messagePart/MessagePartText\";\nimport { MessagePartPrimitiveImage } from \"../messagePart/MessagePartImage\";\nimport type {\n Unstable_AudioMessagePartComponent,\n EmptyMessagePartComponent,\n TextMessagePartComponent,\n ImageMessagePartComponent,\n SourceMessagePartComponent,\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n FileMessagePartComponent,\n ReasoningMessagePartComponent,\n ReasoningGroupComponent,\n} from \"../../types/MessagePartComponentTypes\";\nimport { MessagePartPrimitiveInProgress } from \"../messagePart/MessagePartInProgress\";\nimport { MessagePartStatus } from \"../../types/AssistantTypes\";\nimport { useShallow } from \"zustand/shallow\";\n\ntype MessagePartRange =\n | { type: \"single\"; index: number }\n | { type: \"toolGroup\"; startIndex: number; endIndex: number }\n | { type: \"reasoningGroup\"; startIndex: number; endIndex: number };\n\n/**\n * Creates a group state manager for a specific part type.\n * Returns functions to start, end, and finalize groups.\n */\nconst createGroupState = <T extends \"toolGroup\" | \"reasoningGroup\">(\n groupType: T,\n) => {\n let start = -1;\n\n return {\n startGroup: (index: number) => {\n if (start === -1) {\n start = index;\n }\n },\n endGroup: (endIndex: number, ranges: MessagePartRange[]) => {\n if (start !== -1) {\n ranges.push({\n type: groupType,\n startIndex: start,\n endIndex,\n } as MessagePartRange);\n start = -1;\n }\n },\n finalize: (endIndex: number, ranges: MessagePartRange[]) => {\n if (start !== -1) {\n ranges.push({\n type: groupType,\n startIndex: start,\n endIndex,\n } as MessagePartRange);\n }\n },\n };\n};\n\n/**\n * Groups consecutive tool-call and reasoning message parts into ranges.\n * Always groups tool calls and reasoning parts, even if there's only one.\n */\nconst groupMessageParts = (\n messageTypes: readonly string[],\n): MessagePartRange[] => {\n const ranges: MessagePartRange[] = [];\n const toolGroup = createGroupState(\"toolGroup\");\n const reasoningGroup = createGroupState(\"reasoningGroup\");\n\n for (let i = 0; i < messageTypes.length; i++) {\n const type = messageTypes[i];\n\n if (type === \"tool-call\") {\n reasoningGroup.endGroup(i - 1, ranges);\n toolGroup.startGroup(i);\n } else if (type === \"reasoning\") {\n toolGroup.endGroup(i - 1, ranges);\n reasoningGroup.startGroup(i);\n } else {\n toolGroup.endGroup(i - 1, ranges);\n reasoningGroup.endGroup(i - 1, ranges);\n ranges.push({ type: \"single\", index: i });\n }\n }\n\n toolGroup.finalize(messageTypes.length - 1, ranges);\n reasoningGroup.finalize(messageTypes.length - 1, ranges);\n\n return ranges;\n};\n\nconst useMessagePartsGroups = (): MessagePartRange[] => {\n const messageTypes = useAssistantState(\n useShallow((s) => s.message.parts.map((c: any) => c.type)),\n );\n\n return useMemo(() => {\n if (messageTypes.length === 0) {\n return [];\n }\n return groupMessageParts(messageTypes);\n }, [messageTypes]);\n};\n\nexport namespace MessagePrimitiveParts {\n export type Props = {\n /**\n * Component configuration for rendering different types of message content.\n *\n * You can provide custom components for each content type (text, image, file, etc.)\n * and configure tool rendering behavior. If not provided, default components will be used.\n */\n components?:\n | {\n /** Component for rendering empty messages */\n Empty?: EmptyMessagePartComponent | undefined;\n /** Component for rendering text content */\n Text?: TextMessagePartComponent | undefined;\n /** Component for rendering reasoning content (typically hidden) */\n Reasoning?: ReasoningMessagePartComponent | undefined;\n /** Component for rendering source content */\n Source?: SourceMessagePartComponent | undefined;\n /** Component for rendering image content */\n Image?: ImageMessagePartComponent | undefined;\n /** Component for rendering file content */\n File?: FileMessagePartComponent | undefined;\n /** Component for rendering audio content (experimental) */\n Unstable_Audio?: Unstable_AudioMessagePartComponent | undefined;\n /** Configuration for tool call rendering */\n tools?:\n | {\n /** Map of tool names to their specific components */\n by_name?:\n | Record<string, ToolCallMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unregistered tools */\n Fallback?: ComponentType<ToolCallMessagePartProps> | undefined;\n }\n | {\n /** Override component that handles all tool calls */\n Override: ComponentType<ToolCallMessagePartProps>;\n }\n | undefined;\n\n /**\n * Component for rendering grouped consecutive tool calls.\n *\n * When provided, this component will automatically wrap consecutive tool-call\n * message parts, allowing you to create collapsible sections, custom styling,\n * or other grouped presentations for multiple tool calls.\n *\n * The component receives:\n * - `startIndex`: The index of the first tool call in the group\n * - `endIndex`: The index of the last tool call in the group\n * - `children`: The rendered tool call components\n *\n * @example\n * ```tsx\n * // Collapsible tool group\n * ToolGroup: ({ startIndex, endIndex, children }) => (\n * <details className=\"tool-group\">\n * <summary>\n * {endIndex - startIndex + 1} tool calls\n * </summary>\n * <div className=\"tool-group-content\">\n * {children}\n * </div>\n * </details>\n * )\n * ```\n *\n * @example\n * ```tsx\n * // Custom styled tool group with header\n * ToolGroup: ({ startIndex, endIndex, children }) => (\n * <div className=\"border rounded-lg p-4 my-2\">\n * <div className=\"text-sm text-gray-600 mb-2\">\n * Tool execution #{startIndex + 1}-{endIndex + 1}\n * </div>\n * <div className=\"space-y-2\">\n * {children}\n * </div>\n * </div>\n * )\n * ```\n *\n * @param startIndex - Index of the first tool call in the group\n * @param endIndex - Index of the last tool call in the group\n * @param children - Rendered tool call components to display within the group\n *\n * @deprecated This feature is still experimental and subject to change.\n */\n ToolGroup?: ComponentType<\n PropsWithChildren<{ startIndex: number; endIndex: number }>\n >;\n\n /**\n * Component for rendering grouped reasoning parts.\n *\n * When provided, this component will automatically wrap reasoning message parts\n * (one or more consecutive) in a group container. Each reasoning part inside\n * renders its own text independently - no text merging occurs.\n *\n * The component receives:\n * - `startIndex`: The index of the first reasoning part in the group\n * - `endIndex`: The index of the last reasoning part in the group\n * - `children`: The rendered Reasoning components (one per part)\n *\n * @example\n * ```tsx\n * // Collapsible reasoning group\n * ReasoningGroup: ({ children }) => (\n * <details className=\"reasoning-group\">\n * <summary>Reasoning</summary>\n * <div className=\"reasoning-content\">\n * {children}\n * </div>\n * </details>\n * )\n * ```\n *\n * @param startIndex - Index of the first reasoning part in the group\n * @param endIndex - Index of the last reasoning part in the group\n * @param children - Rendered reasoning part components\n */\n ReasoningGroup?: ReasoningGroupComponent;\n }\n | undefined;\n };\n}\n\nconst ToolUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: ToolCallMessagePartComponent | undefined;\n} & ToolCallMessagePartProps) => {\n const Render = useAssistantState(({ toolUIs }) => {\n const Render =\n toolUIs.tools[props.toolName] ?? toolUIs.fallback ?? Fallback;\n if (Array.isArray(Render)) return Render[0] ?? Fallback;\n return Render;\n });\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst defaultComponents = {\n Text: () => (\n <p style={{ whiteSpace: \"pre-line\" }}>\n <MessagePartPrimitiveText />\n <MessagePartPrimitiveInProgress>\n <span style={{ fontFamily: \"revert\" }}>{\" \\u25CF\"}</span>\n </MessagePartPrimitiveInProgress>\n </p>\n ),\n Reasoning: () => null,\n Source: () => null,\n Image: () => <MessagePartPrimitiveImage />,\n File: () => null,\n Unstable_Audio: () => null,\n ToolGroup: ({ children }) => children,\n ReasoningGroup: ({ children }) => children,\n} satisfies MessagePrimitiveParts.Props[\"components\"];\n\ntype MessagePartComponentProps = {\n components: MessagePrimitiveParts.Props[\"components\"];\n};\n\nconst MessagePartComponent: FC<MessagePartComponentProps> = ({\n components: {\n Text = defaultComponents.Text,\n Reasoning = defaultComponents.Reasoning,\n Image = defaultComponents.Image,\n Source = defaultComponents.Source,\n File = defaultComponents.File,\n Unstable_Audio: Audio = defaultComponents.Unstable_Audio,\n tools = {},\n } = {},\n}) => {\n const api = useAssistantApi();\n const part = useAssistantState(({ part }) => part);\n\n const type = part.type;\n if (type === \"tool-call\") {\n const addResult = api.part().addToolResult;\n const resume = api.part().resumeToolCall;\n if (\"Override\" in tools)\n return <tools.Override {...part} addResult={addResult} resume={resume} />;\n const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;\n return (\n <ToolUIDisplay\n {...part}\n Fallback={Tool}\n addResult={addResult}\n resume={resume}\n />\n );\n }\n\n if (part.status?.type === \"requires-action\")\n throw new Error(\"Encountered unexpected requires-action status\");\n\n switch (type) {\n case \"text\":\n return <Text {...part} />;\n\n case \"reasoning\":\n return <Reasoning {...part} />;\n\n case \"source\":\n return <Source {...part} />;\n\n case \"image\":\n // eslint-disable-next-line jsx-a11y/alt-text\n return <Image {...part} />;\n\n case \"file\":\n return <File {...part} />;\n\n case \"audio\":\n return <Audio {...part} />;\n\n default:\n const unhandledType: never = type;\n throw new Error(`Unknown message part type: ${unhandledType}`);\n }\n};\n\nexport namespace MessagePrimitivePartByIndex {\n export type Props = {\n index: number;\n components: MessagePrimitiveParts.Props[\"components\"];\n };\n}\n\n/**\n * Renders a single message part at the specified index.\n *\n * This component provides direct access to render a specific message part\n * within the current message context, using the provided component configuration.\n *\n * @example\n * ```tsx\n * <MessagePrimitive.PartByIndex\n * index={0}\n * components={{\n * Text: MyTextComponent,\n * Image: MyImageComponent\n * }}\n * />\n * ```\n */\nexport const MessagePrimitivePartByIndex: FC<MessagePrimitivePartByIndex.Props> =\n memo(\n ({ index, components }) => {\n return (\n <PartByIndexProvider index={index}>\n <MessagePartComponent components={components} />\n </PartByIndexProvider>\n );\n },\n (prev, next) =>\n prev.index === next.index &&\n prev.components?.Text === next.components?.Text &&\n prev.components?.Reasoning === next.components?.Reasoning &&\n prev.components?.Source === next.components?.Source &&\n prev.components?.Image === next.components?.Image &&\n prev.components?.File === next.components?.File &&\n prev.components?.Unstable_Audio === next.components?.Unstable_Audio &&\n prev.components?.tools === next.components?.tools &&\n prev.components?.ToolGroup === next.components?.ToolGroup &&\n prev.components?.ReasoningGroup === next.components?.ReasoningGroup,\n );\n\nMessagePrimitivePartByIndex.displayName = \"MessagePrimitive.PartByIndex\";\n\nconst EmptyPartFallback: FC<{\n status: MessagePartStatus;\n component: TextMessagePartComponent;\n}> = ({ status, component: Component }) => {\n return (\n <TextMessagePartProvider text=\"\" isRunning={status.type === \"running\"}>\n <Component type=\"text\" text=\"\" status={status} />\n </TextMessagePartProvider>\n );\n};\n\nconst COMPLETE_STATUS: MessagePartStatus = Object.freeze({\n type: \"complete\",\n});\n\nconst EmptyPartsImpl: FC<MessagePartComponentProps> = ({ components }) => {\n const status = useAssistantState(\n (s) => (s.message.status ?? COMPLETE_STATUS) as MessagePartStatus,\n );\n\n if (components?.Empty) return <components.Empty status={status} />;\n\n return (\n <EmptyPartFallback\n status={status}\n component={components?.Text ?? defaultComponents.Text}\n />\n );\n};\n\nconst EmptyParts = memo(\n EmptyPartsImpl,\n (prev, next) =>\n prev.components?.Empty === next.components?.Empty &&\n prev.components?.Text === next.components?.Text,\n);\n\n/**\n * Renders the parts of a message with support for multiple content types.\n *\n * This component automatically handles different types of message content including\n * text, images, files, tool calls, and more. It provides a flexible component\n * system for customizing how each content type is rendered.\n *\n * @example\n * ```tsx\n * <MessagePrimitive.Parts\n * components={{\n * Text: ({ text }) => <p className=\"message-text\">{text}</p>,\n * Image: ({ image }) => <img src={image} alt=\"Message image\" />,\n * tools: {\n * by_name: {\n * calculator: CalculatorTool,\n * weather: WeatherTool,\n * },\n * Fallback: DefaultToolComponent\n * }\n * }}\n * />\n * ```\n */\nexport const MessagePrimitiveParts: FC<MessagePrimitiveParts.Props> = ({\n components,\n}) => {\n const contentLength = useAssistantState(\n ({ message }) => message.parts.length,\n );\n const messageRanges = useMessagePartsGroups();\n\n const partsElements = useMemo(() => {\n if (contentLength === 0) {\n return <EmptyParts components={components} />;\n }\n\n return messageRanges.map((range) => {\n if (range.type === \"single\") {\n return (\n <MessagePrimitivePartByIndex\n key={range.index}\n index={range.index}\n components={components}\n />\n );\n } else if (range.type === \"toolGroup\") {\n const ToolGroupComponent =\n components!.ToolGroup ?? defaultComponents.ToolGroup;\n return (\n <ToolGroupComponent\n key={`tool-${range.startIndex}`}\n startIndex={range.startIndex}\n endIndex={range.endIndex}\n >\n {Array.from(\n { length: range.endIndex - range.startIndex + 1 },\n (_, i) => (\n <MessagePrimitivePartByIndex\n key={i}\n index={range.startIndex + i}\n components={components}\n />\n ),\n )}\n </ToolGroupComponent>\n );\n } else {\n // reasoningGroup\n const ReasoningGroupComponent =\n components!.ReasoningGroup ?? defaultComponents.ReasoningGroup;\n return (\n <ReasoningGroupComponent\n key={`reasoning-${range.startIndex}`}\n startIndex={range.startIndex}\n endIndex={range.endIndex}\n >\n {Array.from(\n { length: range.endIndex - range.startIndex + 1 },\n (_, i) => (\n <MessagePrimitivePartByIndex\n key={i}\n index={range.startIndex + i}\n components={components}\n />\n ),\n )}\n </ReasoningGroupComponent>\n );\n }\n });\n }, [messageRanges, components, contentLength]);\n\n return <>{partsElements}</>;\n};\n\nMessagePrimitiveParts.displayName = \"MessagePrimitive.Parts\";\n"],"mappings":"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","names":["Render","part"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/primitives/reasoning/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/primitives/reasoning/index.ts"],"sourcesContent":["export { useScrollLock } from \"./useScrollLock\";\n"],"mappings":";AAAA,SAAS,qBAAqB;","names":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type RefObject } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Locks scroll position during collapsible/height animations and hides scrollbar.
|
|
4
|
+
*
|
|
5
|
+
* This utility prevents page jumps when content height changes during animations,
|
|
6
|
+
* providing a smooth user experience. It finds the nearest scrollable ancestor and
|
|
7
|
+
* temporarily locks its scroll position while the animation completes.
|
|
8
|
+
*
|
|
9
|
+
* - Prevents forced reflows: no layout reads, mutations scoped to scrollable parent only
|
|
10
|
+
* - Reactive: only intercepts scroll events when browser actually adjusts
|
|
11
|
+
* - Cleans up automatically after animation duration
|
|
12
|
+
*
|
|
13
|
+
* @param animatedElementRef - Ref to the animated element
|
|
14
|
+
* @param animationDuration - Lock duration in milliseconds
|
|
15
|
+
* @returns Function to activate the scroll lock
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* const collapsibleRef = useRef<HTMLDivElement>(null);
|
|
20
|
+
* const lockScroll = useScrollLock(collapsibleRef, 200);
|
|
21
|
+
*
|
|
22
|
+
* const handleCollapse = () => {
|
|
23
|
+
* lockScroll(); // Lock scroll before collapsing
|
|
24
|
+
* setIsOpen(false);
|
|
25
|
+
* };
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare const useScrollLock: <T extends HTMLElement = HTMLElement>(animatedElementRef: RefObject<T | null>, animationDuration: number) => () => void;
|
|
29
|
+
//# sourceMappingURL=useScrollLock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useScrollLock.d.ts","sourceRoot":"","sources":["../../../src/primitives/reasoning/useScrollLock.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAkC,MAAM,OAAO,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,SAAS,WAAW,GAAG,WAAW,EAC/D,oBAAoB,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,EACvC,mBAAmB,MAAM,eAqD1B,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/primitives/reasoning/useScrollLock.tsx
|
|
4
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
5
|
+
var useScrollLock = (animatedElementRef, animationDuration) => {
|
|
6
|
+
const scrollContainerRef = useRef(null);
|
|
7
|
+
const cleanupRef = useRef(null);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
return () => {
|
|
10
|
+
cleanupRef.current?.();
|
|
11
|
+
};
|
|
12
|
+
}, []);
|
|
13
|
+
const lockScroll = useCallback(() => {
|
|
14
|
+
cleanupRef.current?.();
|
|
15
|
+
(function findScrollableAncestor() {
|
|
16
|
+
if (scrollContainerRef.current || !animatedElementRef.current) return;
|
|
17
|
+
let el = animatedElementRef.current;
|
|
18
|
+
while (el) {
|
|
19
|
+
const { overflowY } = getComputedStyle(el);
|
|
20
|
+
if (overflowY === "scroll" || overflowY === "auto") {
|
|
21
|
+
scrollContainerRef.current = el;
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
el = el.parentElement;
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
const scrollContainer = scrollContainerRef.current;
|
|
28
|
+
if (!scrollContainer) return;
|
|
29
|
+
const scrollPosition = scrollContainer.scrollTop;
|
|
30
|
+
const scrollbarWidth = scrollContainer.style.scrollbarWidth;
|
|
31
|
+
scrollContainer.style.scrollbarWidth = "none";
|
|
32
|
+
const resetPosition = () => scrollContainer.scrollTop = scrollPosition;
|
|
33
|
+
scrollContainer.addEventListener("scroll", resetPosition);
|
|
34
|
+
const timeoutId = setTimeout(() => {
|
|
35
|
+
scrollContainer.removeEventListener("scroll", resetPosition);
|
|
36
|
+
scrollContainer.style.scrollbarWidth = scrollbarWidth;
|
|
37
|
+
cleanupRef.current = null;
|
|
38
|
+
}, animationDuration);
|
|
39
|
+
cleanupRef.current = () => {
|
|
40
|
+
clearTimeout(timeoutId);
|
|
41
|
+
scrollContainer.removeEventListener("scroll", resetPosition);
|
|
42
|
+
scrollContainer.style.scrollbarWidth = scrollbarWidth;
|
|
43
|
+
};
|
|
44
|
+
}, [animationDuration, animatedElementRef]);
|
|
45
|
+
return lockScroll;
|
|
46
|
+
};
|
|
47
|
+
export {
|
|
48
|
+
useScrollLock
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=useScrollLock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/primitives/reasoning/useScrollLock.tsx"],"sourcesContent":["\"use client\";\n\nimport { type RefObject, useCallback, useEffect, useRef } from \"react\";\n\n/**\n * Locks scroll position during collapsible/height animations and hides scrollbar.\n *\n * This utility prevents page jumps when content height changes during animations,\n * providing a smooth user experience. It finds the nearest scrollable ancestor and\n * temporarily locks its scroll position while the animation completes.\n *\n * - Prevents forced reflows: no layout reads, mutations scoped to scrollable parent only\n * - Reactive: only intercepts scroll events when browser actually adjusts\n * - Cleans up automatically after animation duration\n *\n * @param animatedElementRef - Ref to the animated element\n * @param animationDuration - Lock duration in milliseconds\n * @returns Function to activate the scroll lock\n *\n * @example\n * ```tsx\n * const collapsibleRef = useRef<HTMLDivElement>(null);\n * const lockScroll = useScrollLock(collapsibleRef, 200);\n *\n * const handleCollapse = () => {\n * lockScroll(); // Lock scroll before collapsing\n * setIsOpen(false);\n * };\n * ```\n */\nexport const useScrollLock = <T extends HTMLElement = HTMLElement>(\n animatedElementRef: RefObject<T | null>,\n animationDuration: number,\n) => {\n const scrollContainerRef = useRef<HTMLElement | null>(null);\n const cleanupRef = useRef<(() => void) | null>(null);\n\n useEffect(() => {\n return () => {\n cleanupRef.current?.();\n };\n }, []);\n\n const lockScroll = useCallback(() => {\n cleanupRef.current?.();\n\n (function findScrollableAncestor() {\n if (scrollContainerRef.current || !animatedElementRef.current) return;\n\n let el: HTMLElement | null = animatedElementRef.current;\n while (el) {\n const { overflowY } = getComputedStyle(el);\n if (overflowY === \"scroll\" || overflowY === \"auto\") {\n scrollContainerRef.current = el;\n break;\n }\n el = el.parentElement;\n }\n })();\n\n const scrollContainer = scrollContainerRef.current;\n if (!scrollContainer) return;\n\n const scrollPosition = scrollContainer.scrollTop;\n const scrollbarWidth = scrollContainer.style.scrollbarWidth;\n\n scrollContainer.style.scrollbarWidth = \"none\";\n\n const resetPosition = () => (scrollContainer.scrollTop = scrollPosition);\n scrollContainer.addEventListener(\"scroll\", resetPosition);\n\n const timeoutId = setTimeout(() => {\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n scrollContainer.style.scrollbarWidth = scrollbarWidth;\n cleanupRef.current = null;\n }, animationDuration);\n\n cleanupRef.current = () => {\n clearTimeout(timeoutId);\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n scrollContainer.style.scrollbarWidth = scrollbarWidth;\n };\n }, [animationDuration, animatedElementRef]);\n\n return lockScroll;\n};\n"],"mappings":";;;AAEA,SAAyB,aAAa,WAAW,cAAc;AA4BxD,IAAM,gBAAgB,CAC3B,oBACA,sBACG;AACH,QAAM,qBAAqB,OAA2B,IAAI;AAC1D,QAAM,aAAa,OAA4B,IAAI;AAEnD,YAAU,MAAM;AACd,WAAO,MAAM;AACX,iBAAW,UAAU;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa,YAAY,MAAM;AACnC,eAAW,UAAU;AAErB,KAAC,SAAS,yBAAyB;AACjC,UAAI,mBAAmB,WAAW,CAAC,mBAAmB,QAAS;AAE/D,UAAI,KAAyB,mBAAmB;AAChD,aAAO,IAAI;AACT,cAAM,EAAE,UAAU,IAAI,iBAAiB,EAAE;AACzC,YAAI,cAAc,YAAY,cAAc,QAAQ;AAClD,6BAAmB,UAAU;AAC7B;AAAA,QACF;AACA,aAAK,GAAG;AAAA,MACV;AAAA,IACF,GAAG;AAEH,UAAM,kBAAkB,mBAAmB;AAC3C,QAAI,CAAC,gBAAiB;AAEtB,UAAM,iBAAiB,gBAAgB;AACvC,UAAM,iBAAiB,gBAAgB,MAAM;AAE7C,oBAAgB,MAAM,iBAAiB;AAEvC,UAAM,gBAAgB,MAAO,gBAAgB,YAAY;AACzD,oBAAgB,iBAAiB,UAAU,aAAa;AAExD,UAAM,YAAY,WAAW,MAAM;AACjC,sBAAgB,oBAAoB,UAAU,aAAa;AAC3D,sBAAgB,MAAM,iBAAiB;AACvC,iBAAW,UAAU;AAAA,IACvB,GAAG,iBAAiB;AAEpB,eAAW,UAAU,MAAM;AACzB,mBAAa,SAAS;AACtB,sBAAgB,oBAAoB,UAAU,aAAa;AAC3D,sBAAgB,MAAM,iBAAiB;AAAA,IACzC;AAAA,EACF,GAAG,CAAC,mBAAmB,kBAAkB,CAAC;AAE1C,SAAO;AACT;","names":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComponentType } from "react";
|
|
1
|
+
import type { ComponentType, PropsWithChildren } from "react";
|
|
2
2
|
import type { MessagePartStatus, FileMessagePart, ImageMessagePart, ReasoningMessagePart, SourceMessagePart, TextMessagePart, ToolCallMessagePart, Unstable_AudioMessagePart } from "./AssistantTypes";
|
|
3
3
|
import { MessagePartState } from "../legacy-runtime/runtime/MessagePartRuntime";
|
|
4
4
|
import { ToolResponse } from "assistant-stream";
|
|
@@ -10,6 +10,11 @@ export type TextMessagePartProps = MessagePartState & TextMessagePart;
|
|
|
10
10
|
export type TextMessagePartComponent = ComponentType<TextMessagePartProps>;
|
|
11
11
|
export type ReasoningMessagePartProps = MessagePartState & ReasoningMessagePart;
|
|
12
12
|
export type ReasoningMessagePartComponent = ComponentType<ReasoningMessagePartProps>;
|
|
13
|
+
export type ReasoningGroupProps = PropsWithChildren<{
|
|
14
|
+
startIndex: number;
|
|
15
|
+
endIndex: number;
|
|
16
|
+
}>;
|
|
17
|
+
export type ReasoningGroupComponent = ComponentType<ReasoningGroupProps>;
|
|
13
18
|
export type SourceMessagePartProps = MessagePartState & SourceMessagePart;
|
|
14
19
|
export type SourceMessagePartComponent = ComponentType<SourceMessagePartProps>;
|
|
15
20
|
export type ImageMessagePartProps = MessagePartState & ImageMessagePart;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessagePartComponentTypes.d.ts","sourceRoot":"","sources":["../../src/types/MessagePartComponentTypes.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"MessagePartComponentTypes.d.ts","sourceRoot":"","sources":["../../src/types/MessagePartComponentTypes.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAC9D,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AACF,MAAM,MAAM,yBAAyB,GAAG,aAAa,CAAC,qBAAqB,CAAC,CAAC;AAE7E,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG,eAAe,CAAC;AACtE,MAAM,MAAM,wBAAwB,GAAG,aAAa,CAAC,oBAAoB,CAAC,CAAC;AAE3E,MAAM,MAAM,yBAAyB,GAAG,gBAAgB,GAAG,oBAAoB,CAAC;AAChF,MAAM,MAAM,6BAA6B,GACvC,aAAa,CAAC,yBAAyB,CAAC,CAAC;AAE3C,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,aAAa,CAAC,mBAAmB,CAAC,CAAC;AAEzE,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;AAC1E,MAAM,MAAM,0BAA0B,GAAG,aAAa,CAAC,sBAAsB,CAAC,CAAC;AAE/E,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;AACxE,MAAM,MAAM,yBAAyB,GAAG,aAAa,CAAC,qBAAqB,CAAC,CAAC;AAE7E,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG,eAAe,CAAC;AACtE,MAAM,MAAM,wBAAwB,GAAG,aAAa,CAAC,oBAAoB,CAAC,CAAC;AAE3E,MAAM,MAAM,8BAA8B,GAAG,gBAAgB,GAC3D,yBAAyB,CAAC;AAC5B,MAAM,MAAM,kCAAkC,GAC5C,aAAa,CAAC,8BAA8B,CAAC,CAAC;AAEhD,MAAM,MAAM,wBAAwB,CAClC,KAAK,GAAG,GAAG,EACX,OAAO,GAAG,OAAO,IACf,gBAAgB,GAClB,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG;IACpC,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC7D,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC,CAAC;AAEJ,MAAM,MAAM,4BAA4B,CACtC,KAAK,GAAG,GAAG,EACX,OAAO,GAAG,GAAG,IACX,aAAa,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { Attachment, PendingAttachment, CompleteAttachment, AttachmentStatus, } from "./AttachmentTypes";
|
|
2
2
|
export type { AppendMessage, TextMessagePart, ReasoningMessagePart, SourceMessagePart, ImageMessagePart, FileMessagePart, Unstable_AudioMessagePart, ToolCallMessagePart, MessageStatus, MessagePartStatus, ToolCallMessagePartStatus, ThreadUserMessagePart, ThreadAssistantMessagePart, ThreadSystemMessage, ThreadAssistantMessage, ThreadUserMessage, ThreadMessage, } from "./AssistantTypes";
|
|
3
|
-
export type { EmptyMessagePartComponent, EmptyMessagePartProps, TextMessagePartComponent, TextMessagePartProps, ReasoningMessagePartComponent, ReasoningMessagePartProps, SourceMessagePartComponent, SourceMessagePartProps, ImageMessagePartComponent, ImageMessagePartProps, FileMessagePartComponent, FileMessagePartProps, Unstable_AudioMessagePartComponent, Unstable_AudioMessagePartProps, ToolCallMessagePartComponent, ToolCallMessagePartProps, } from "./MessagePartComponentTypes";
|
|
3
|
+
export type { EmptyMessagePartComponent, EmptyMessagePartProps, TextMessagePartComponent, TextMessagePartProps, ReasoningMessagePartComponent, ReasoningMessagePartProps, SourceMessagePartComponent, SourceMessagePartProps, ImageMessagePartComponent, ImageMessagePartProps, FileMessagePartComponent, FileMessagePartProps, Unstable_AudioMessagePartComponent, Unstable_AudioMessagePartProps, ToolCallMessagePartComponent, ToolCallMessagePartProps, ReasoningGroupProps, ReasoningGroupComponent, } from "./MessagePartComponentTypes";
|
|
4
4
|
export type { ThreadListItemStatus } from "../legacy-runtime/runtime/ThreadListItemRuntime";
|
|
5
5
|
export type { Unsubscribe } from "./Unsubscribe";
|
|
6
6
|
export type { AssistantEventScope, AssistantEventSelector, AssistantEvent, AssistantEventMap, AssistantEventCallback, } from "./EventTypes";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,yBAAyB,EACzB,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,yBAAyB,EAGzB,qBAAqB,EACrB,0BAA0B,EAC1B,mBAAmB,EACnB,sBAAsB,EACtB,iBAAiB,EACjB,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,yBAAyB,EACzB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,EACzB,0BAA0B,EAC1B,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,kCAAkC,EAClC,8BAA8B,EAC9B,4BAA4B,EAC5B,wBAAwB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,yBAAyB,EACzB,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,yBAAyB,EAGzB,qBAAqB,EACrB,0BAA0B,EAC1B,mBAAmB,EACnB,sBAAsB,EACtB,iBAAiB,EACjB,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,yBAAyB,EACzB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,EACzB,0BAA0B,EAC1B,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,kCAAkC,EAClC,8BAA8B,EAC9B,4BAA4B,EAC5B,wBAAwB,EACxB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,6BAA6B,CAAC;AAGrC,YAAY,EAAE,oBAAoB,EAAE,MAAM,iDAAiD,CAAC;AAE5F,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,YAAY,EACV,mBAAmB,EACnB,sBAAsB,EACtB,cAAc,EACd,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,cAAc,CAAC"}
|
package/package.json
CHANGED
package/src/primitives/index.ts
CHANGED
|
@@ -16,3 +16,4 @@ export { useMessagePartSource } from "./messagePart/useMessagePartSource";
|
|
|
16
16
|
export { useMessagePartFile } from "./messagePart/useMessagePartFile";
|
|
17
17
|
export { useMessagePartImage } from "./messagePart/useMessagePartImage";
|
|
18
18
|
export { useThreadViewportAutoScroll } from "./thread/useThreadViewportAutoScroll";
|
|
19
|
+
export { useScrollLock } from "./reasoning";
|
|
@@ -25,6 +25,7 @@ import type {
|
|
|
25
25
|
ToolCallMessagePartProps,
|
|
26
26
|
FileMessagePartComponent,
|
|
27
27
|
ReasoningMessagePartComponent,
|
|
28
|
+
ReasoningGroupComponent,
|
|
28
29
|
} from "../../types/MessagePartComponentTypes";
|
|
29
30
|
import { MessagePartPrimitiveInProgress } from "../messagePart/MessagePartInProgress";
|
|
30
31
|
import { MessagePartStatus } from "../../types/AssistantTypes";
|
|
@@ -32,50 +33,75 @@ import { useShallow } from "zustand/shallow";
|
|
|
32
33
|
|
|
33
34
|
type MessagePartRange =
|
|
34
35
|
| { type: "single"; index: number }
|
|
35
|
-
| { type: "toolGroup"; startIndex: number; endIndex: number }
|
|
36
|
+
| { type: "toolGroup"; startIndex: number; endIndex: number }
|
|
37
|
+
| { type: "reasoningGroup"; startIndex: number; endIndex: number };
|
|
36
38
|
|
|
37
39
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
+
* Creates a group state manager for a specific part type.
|
|
41
|
+
* Returns functions to start, end, and finalize groups.
|
|
42
|
+
*/
|
|
43
|
+
const createGroupState = <T extends "toolGroup" | "reasoningGroup">(
|
|
44
|
+
groupType: T,
|
|
45
|
+
) => {
|
|
46
|
+
let start = -1;
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
startGroup: (index: number) => {
|
|
50
|
+
if (start === -1) {
|
|
51
|
+
start = index;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
endGroup: (endIndex: number, ranges: MessagePartRange[]) => {
|
|
55
|
+
if (start !== -1) {
|
|
56
|
+
ranges.push({
|
|
57
|
+
type: groupType,
|
|
58
|
+
startIndex: start,
|
|
59
|
+
endIndex,
|
|
60
|
+
} as MessagePartRange);
|
|
61
|
+
start = -1;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
finalize: (endIndex: number, ranges: MessagePartRange[]) => {
|
|
65
|
+
if (start !== -1) {
|
|
66
|
+
ranges.push({
|
|
67
|
+
type: groupType,
|
|
68
|
+
startIndex: start,
|
|
69
|
+
endIndex,
|
|
70
|
+
} as MessagePartRange);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Groups consecutive tool-call and reasoning message parts into ranges.
|
|
78
|
+
* Always groups tool calls and reasoning parts, even if there's only one.
|
|
40
79
|
*/
|
|
41
80
|
const groupMessageParts = (
|
|
42
81
|
messageTypes: readonly string[],
|
|
43
82
|
): MessagePartRange[] => {
|
|
44
83
|
const ranges: MessagePartRange[] = [];
|
|
45
|
-
|
|
84
|
+
const toolGroup = createGroupState("toolGroup");
|
|
85
|
+
const reasoningGroup = createGroupState("reasoningGroup");
|
|
46
86
|
|
|
47
87
|
for (let i = 0; i < messageTypes.length; i++) {
|
|
48
88
|
const type = messageTypes[i];
|
|
49
89
|
|
|
50
90
|
if (type === "tool-call") {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
91
|
+
reasoningGroup.endGroup(i - 1, ranges);
|
|
92
|
+
toolGroup.startGroup(i);
|
|
93
|
+
} else if (type === "reasoning") {
|
|
94
|
+
toolGroup.endGroup(i - 1, ranges);
|
|
95
|
+
reasoningGroup.startGroup(i);
|
|
55
96
|
} else {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
ranges.push({
|
|
59
|
-
type: "toolGroup",
|
|
60
|
-
startIndex: currentToolGroupStart,
|
|
61
|
-
endIndex: i - 1,
|
|
62
|
-
});
|
|
63
|
-
currentToolGroupStart = -1;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Add non-tool-call part individually
|
|
97
|
+
toolGroup.endGroup(i - 1, ranges);
|
|
98
|
+
reasoningGroup.endGroup(i - 1, ranges);
|
|
67
99
|
ranges.push({ type: "single", index: i });
|
|
68
100
|
}
|
|
69
101
|
}
|
|
70
102
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
ranges.push({
|
|
74
|
-
type: "toolGroup",
|
|
75
|
-
startIndex: currentToolGroupStart,
|
|
76
|
-
endIndex: messageTypes.length - 1,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
103
|
+
toolGroup.finalize(messageTypes.length - 1, ranges);
|
|
104
|
+
reasoningGroup.finalize(messageTypes.length - 1, ranges);
|
|
79
105
|
|
|
80
106
|
return ranges;
|
|
81
107
|
};
|
|
@@ -184,6 +210,37 @@ export namespace MessagePrimitiveParts {
|
|
|
184
210
|
ToolGroup?: ComponentType<
|
|
185
211
|
PropsWithChildren<{ startIndex: number; endIndex: number }>
|
|
186
212
|
>;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Component for rendering grouped reasoning parts.
|
|
216
|
+
*
|
|
217
|
+
* When provided, this component will automatically wrap reasoning message parts
|
|
218
|
+
* (one or more consecutive) in a group container. Each reasoning part inside
|
|
219
|
+
* renders its own text independently - no text merging occurs.
|
|
220
|
+
*
|
|
221
|
+
* The component receives:
|
|
222
|
+
* - `startIndex`: The index of the first reasoning part in the group
|
|
223
|
+
* - `endIndex`: The index of the last reasoning part in the group
|
|
224
|
+
* - `children`: The rendered Reasoning components (one per part)
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```tsx
|
|
228
|
+
* // Collapsible reasoning group
|
|
229
|
+
* ReasoningGroup: ({ children }) => (
|
|
230
|
+
* <details className="reasoning-group">
|
|
231
|
+
* <summary>Reasoning</summary>
|
|
232
|
+
* <div className="reasoning-content">
|
|
233
|
+
* {children}
|
|
234
|
+
* </div>
|
|
235
|
+
* </details>
|
|
236
|
+
* )
|
|
237
|
+
* ```
|
|
238
|
+
*
|
|
239
|
+
* @param startIndex - Index of the first reasoning part in the group
|
|
240
|
+
* @param endIndex - Index of the last reasoning part in the group
|
|
241
|
+
* @param children - Rendered reasoning part components
|
|
242
|
+
*/
|
|
243
|
+
ReasoningGroup?: ReasoningGroupComponent;
|
|
187
244
|
}
|
|
188
245
|
| undefined;
|
|
189
246
|
};
|
|
@@ -220,6 +277,7 @@ const defaultComponents = {
|
|
|
220
277
|
File: () => null,
|
|
221
278
|
Unstable_Audio: () => null,
|
|
222
279
|
ToolGroup: ({ children }) => children,
|
|
280
|
+
ReasoningGroup: ({ children }) => children,
|
|
223
281
|
} satisfies MessagePrimitiveParts.Props["components"];
|
|
224
282
|
|
|
225
283
|
type MessagePartComponentProps = {
|
|
@@ -328,7 +386,8 @@ export const MessagePrimitivePartByIndex: FC<MessagePrimitivePartByIndex.Props>
|
|
|
328
386
|
prev.components?.File === next.components?.File &&
|
|
329
387
|
prev.components?.Unstable_Audio === next.components?.Unstable_Audio &&
|
|
330
388
|
prev.components?.tools === next.components?.tools &&
|
|
331
|
-
prev.components?.ToolGroup === next.components?.ToolGroup
|
|
389
|
+
prev.components?.ToolGroup === next.components?.ToolGroup &&
|
|
390
|
+
prev.components?.ReasoningGroup === next.components?.ReasoningGroup,
|
|
332
391
|
);
|
|
333
392
|
|
|
334
393
|
MessagePrimitivePartByIndex.displayName = "MessagePrimitive.PartByIndex";
|
|
@@ -416,12 +475,12 @@ export const MessagePrimitiveParts: FC<MessagePrimitiveParts.Props> = ({
|
|
|
416
475
|
components={components}
|
|
417
476
|
/>
|
|
418
477
|
);
|
|
419
|
-
} else {
|
|
478
|
+
} else if (range.type === "toolGroup") {
|
|
420
479
|
const ToolGroupComponent =
|
|
421
480
|
components!.ToolGroup ?? defaultComponents.ToolGroup;
|
|
422
481
|
return (
|
|
423
482
|
<ToolGroupComponent
|
|
424
|
-
key={range.startIndex}
|
|
483
|
+
key={`tool-${range.startIndex}`}
|
|
425
484
|
startIndex={range.startIndex}
|
|
426
485
|
endIndex={range.endIndex}
|
|
427
486
|
>
|
|
@@ -437,6 +496,28 @@ export const MessagePrimitiveParts: FC<MessagePrimitiveParts.Props> = ({
|
|
|
437
496
|
)}
|
|
438
497
|
</ToolGroupComponent>
|
|
439
498
|
);
|
|
499
|
+
} else {
|
|
500
|
+
// reasoningGroup
|
|
501
|
+
const ReasoningGroupComponent =
|
|
502
|
+
components!.ReasoningGroup ?? defaultComponents.ReasoningGroup;
|
|
503
|
+
return (
|
|
504
|
+
<ReasoningGroupComponent
|
|
505
|
+
key={`reasoning-${range.startIndex}`}
|
|
506
|
+
startIndex={range.startIndex}
|
|
507
|
+
endIndex={range.endIndex}
|
|
508
|
+
>
|
|
509
|
+
{Array.from(
|
|
510
|
+
{ length: range.endIndex - range.startIndex + 1 },
|
|
511
|
+
(_, i) => (
|
|
512
|
+
<MessagePrimitivePartByIndex
|
|
513
|
+
key={i}
|
|
514
|
+
index={range.startIndex + i}
|
|
515
|
+
components={components}
|
|
516
|
+
/>
|
|
517
|
+
),
|
|
518
|
+
)}
|
|
519
|
+
</ReasoningGroupComponent>
|
|
520
|
+
);
|
|
440
521
|
}
|
|
441
522
|
});
|
|
442
523
|
}, [messageRanges, components, contentLength]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useScrollLock } from "./useScrollLock";
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { type RefObject, useCallback, useEffect, useRef } from "react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Locks scroll position during collapsible/height animations and hides scrollbar.
|
|
7
|
+
*
|
|
8
|
+
* This utility prevents page jumps when content height changes during animations,
|
|
9
|
+
* providing a smooth user experience. It finds the nearest scrollable ancestor and
|
|
10
|
+
* temporarily locks its scroll position while the animation completes.
|
|
11
|
+
*
|
|
12
|
+
* - Prevents forced reflows: no layout reads, mutations scoped to scrollable parent only
|
|
13
|
+
* - Reactive: only intercepts scroll events when browser actually adjusts
|
|
14
|
+
* - Cleans up automatically after animation duration
|
|
15
|
+
*
|
|
16
|
+
* @param animatedElementRef - Ref to the animated element
|
|
17
|
+
* @param animationDuration - Lock duration in milliseconds
|
|
18
|
+
* @returns Function to activate the scroll lock
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* const collapsibleRef = useRef<HTMLDivElement>(null);
|
|
23
|
+
* const lockScroll = useScrollLock(collapsibleRef, 200);
|
|
24
|
+
*
|
|
25
|
+
* const handleCollapse = () => {
|
|
26
|
+
* lockScroll(); // Lock scroll before collapsing
|
|
27
|
+
* setIsOpen(false);
|
|
28
|
+
* };
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export const useScrollLock = <T extends HTMLElement = HTMLElement>(
|
|
32
|
+
animatedElementRef: RefObject<T | null>,
|
|
33
|
+
animationDuration: number,
|
|
34
|
+
) => {
|
|
35
|
+
const scrollContainerRef = useRef<HTMLElement | null>(null);
|
|
36
|
+
const cleanupRef = useRef<(() => void) | null>(null);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
return () => {
|
|
40
|
+
cleanupRef.current?.();
|
|
41
|
+
};
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
const lockScroll = useCallback(() => {
|
|
45
|
+
cleanupRef.current?.();
|
|
46
|
+
|
|
47
|
+
(function findScrollableAncestor() {
|
|
48
|
+
if (scrollContainerRef.current || !animatedElementRef.current) return;
|
|
49
|
+
|
|
50
|
+
let el: HTMLElement | null = animatedElementRef.current;
|
|
51
|
+
while (el) {
|
|
52
|
+
const { overflowY } = getComputedStyle(el);
|
|
53
|
+
if (overflowY === "scroll" || overflowY === "auto") {
|
|
54
|
+
scrollContainerRef.current = el;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
el = el.parentElement;
|
|
58
|
+
}
|
|
59
|
+
})();
|
|
60
|
+
|
|
61
|
+
const scrollContainer = scrollContainerRef.current;
|
|
62
|
+
if (!scrollContainer) return;
|
|
63
|
+
|
|
64
|
+
const scrollPosition = scrollContainer.scrollTop;
|
|
65
|
+
const scrollbarWidth = scrollContainer.style.scrollbarWidth;
|
|
66
|
+
|
|
67
|
+
scrollContainer.style.scrollbarWidth = "none";
|
|
68
|
+
|
|
69
|
+
const resetPosition = () => (scrollContainer.scrollTop = scrollPosition);
|
|
70
|
+
scrollContainer.addEventListener("scroll", resetPosition);
|
|
71
|
+
|
|
72
|
+
const timeoutId = setTimeout(() => {
|
|
73
|
+
scrollContainer.removeEventListener("scroll", resetPosition);
|
|
74
|
+
scrollContainer.style.scrollbarWidth = scrollbarWidth;
|
|
75
|
+
cleanupRef.current = null;
|
|
76
|
+
}, animationDuration);
|
|
77
|
+
|
|
78
|
+
cleanupRef.current = () => {
|
|
79
|
+
clearTimeout(timeoutId);
|
|
80
|
+
scrollContainer.removeEventListener("scroll", resetPosition);
|
|
81
|
+
scrollContainer.style.scrollbarWidth = scrollbarWidth;
|
|
82
|
+
};
|
|
83
|
+
}, [animationDuration, animatedElementRef]);
|
|
84
|
+
|
|
85
|
+
return lockScroll;
|
|
86
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComponentType } from "react";
|
|
1
|
+
import type { ComponentType, PropsWithChildren } from "react";
|
|
2
2
|
import type {
|
|
3
3
|
MessagePartStatus,
|
|
4
4
|
FileMessagePart,
|
|
@@ -24,6 +24,12 @@ export type ReasoningMessagePartProps = MessagePartState & ReasoningMessagePart;
|
|
|
24
24
|
export type ReasoningMessagePartComponent =
|
|
25
25
|
ComponentType<ReasoningMessagePartProps>;
|
|
26
26
|
|
|
27
|
+
export type ReasoningGroupProps = PropsWithChildren<{
|
|
28
|
+
startIndex: number;
|
|
29
|
+
endIndex: number;
|
|
30
|
+
}>;
|
|
31
|
+
export type ReasoningGroupComponent = ComponentType<ReasoningGroupProps>;
|
|
32
|
+
|
|
27
33
|
export type SourceMessagePartProps = MessagePartState & SourceMessagePart;
|
|
28
34
|
export type SourceMessagePartComponent = ComponentType<SourceMessagePartProps>;
|
|
29
35
|
|
package/src/types/index.ts
CHANGED