@assistant-ui/react 0.14.6 → 0.14.7
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSmooth.d.ts","sourceRoot":"","sources":["../../../src/utils/smooth/useSmooth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AA+D5B,eAAO,MAAM,SAAS,GACpB,OAAO,gBAAgB,GAAG,CAAC,eAAe,GAAG,oBAAoB,CAAC,EAClE,SAAQ,OAAe,KACtB,gBAAgB,GAAG,CAAC,eAAe,GAAG,oBAAoB,
|
|
1
|
+
{"version":3,"file":"useSmooth.d.ts","sourceRoot":"","sources":["../../../src/utils/smooth/useSmooth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AA+D5B,eAAO,MAAM,SAAS,GACpB,OAAO,gBAAgB,GAAG,CAAC,eAAe,GAAG,oBAAoB,CAAC,EAClE,SAAQ,OAAe,KACtB,gBAAgB,GAAG,CAAC,eAAe,GAAG,oBAAoB,CAmG5D,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { useAuiState } from "@assistant-ui/store";
|
|
3
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
4
4
|
import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
|
|
5
5
|
import { useSmoothStatusStore } from "./SmoothContext.js";
|
|
6
6
|
import { writableStore } from "../../context/ReadonlyStore.js";
|
|
@@ -55,9 +55,23 @@ const SMOOTH_STATUS = Object.freeze({
|
|
|
55
55
|
});
|
|
56
56
|
export const useSmooth = (state, smooth = false) => {
|
|
57
57
|
const { text } = state;
|
|
58
|
-
const id = useAuiState((s) => s.message.id);
|
|
59
|
-
const idRef = useRef(id);
|
|
60
58
|
const [displayedText, setDisplayedText] = useState(state.status.type === "running" ? "" : text);
|
|
59
|
+
// Render-phase resync on part flip or text discontinuity, so the
|
|
60
|
+
// first paint after a thread switch never shows the previous
|
|
61
|
+
// part's text (#4051). `displayedText` is already a prefix of
|
|
62
|
+
// `text` during normal streaming, so use it as the previous-text
|
|
63
|
+
// reference instead of carrying separate state — avoids the
|
|
64
|
+
// double render per streaming token. Read part identity through
|
|
65
|
+
// `useAuiState` so we actually subscribe to its changes instead
|
|
66
|
+
// of relying on a render-time proxy reference that may be stable
|
|
67
|
+
// across thread swaps.
|
|
68
|
+
const aui = useAui();
|
|
69
|
+
const part = useAuiState(() => aui.part());
|
|
70
|
+
const [prevPart, setPrevPart] = useState(part);
|
|
71
|
+
if (part !== prevPart || !text.startsWith(displayedText)) {
|
|
72
|
+
setPrevPart(part);
|
|
73
|
+
setDisplayedText(state.status.type === "running" ? "" : text);
|
|
74
|
+
}
|
|
61
75
|
const smoothStatusStore = useSmoothStatusStore({ optional: true });
|
|
62
76
|
const setText = useCallbackRef((text) => {
|
|
63
77
|
setDisplayedText(text);
|
|
@@ -78,23 +92,26 @@ export const useSmooth = (state, smooth = false) => {
|
|
|
78
92
|
}
|
|
79
93
|
}, [smoothStatusStore, smooth, text, displayedText, state.status]);
|
|
80
94
|
const [animatorRef] = useState(new TextStreamAnimator(displayedText, setText));
|
|
95
|
+
const animatorPartRef = useRef(part);
|
|
81
96
|
useEffect(() => {
|
|
82
97
|
if (!smooth) {
|
|
83
98
|
animatorRef.stop();
|
|
84
99
|
return;
|
|
85
100
|
}
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
// Discontinuity: part flipped, or new text breaks continuation
|
|
102
|
+
// of the animator's current target. Either case requires
|
|
103
|
+
// resetting the cursor — without the part check, a new part
|
|
104
|
+
// whose text happens to share a prefix with the previous target
|
|
105
|
+
// would keep the stale cursor and flicker.
|
|
106
|
+
const partChanged = animatorPartRef.current !== part;
|
|
107
|
+
animatorPartRef.current = part;
|
|
108
|
+
if (partChanged || !text.startsWith(animatorRef.targetText)) {
|
|
88
109
|
if (state.status.type === "running") {
|
|
89
|
-
// New streaming message → animate from empty string
|
|
90
|
-
setText("");
|
|
91
110
|
animatorRef.currentText = "";
|
|
92
111
|
animatorRef.targetText = text;
|
|
93
112
|
animatorRef.start();
|
|
94
113
|
}
|
|
95
114
|
else {
|
|
96
|
-
// Completed message → display immediately
|
|
97
|
-
setText(text);
|
|
98
115
|
animatorRef.currentText = text;
|
|
99
116
|
animatorRef.targetText = text;
|
|
100
117
|
animatorRef.stop();
|
|
@@ -103,7 +120,7 @@ export const useSmooth = (state, smooth = false) => {
|
|
|
103
120
|
}
|
|
104
121
|
animatorRef.targetText = text;
|
|
105
122
|
animatorRef.start();
|
|
106
|
-
}, [
|
|
123
|
+
}, [animatorRef, smooth, text, state.status.type, part]);
|
|
107
124
|
useEffect(() => {
|
|
108
125
|
return () => {
|
|
109
126
|
animatorRef.stop();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSmooth.js","sourceRoot":"","sources":["../../../src/utils/smooth/useSmooth.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"useSmooth.js","sourceRoot":"","sources":["../../../src/utils/smooth/useSmooth.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.7",
|
|
4
4
|
"description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"radix-ui",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
],
|
|
49
49
|
"sideEffects": false,
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@assistant-ui/core": "^0.2.
|
|
51
|
+
"@assistant-ui/core": "^0.2.4",
|
|
52
52
|
"@assistant-ui/store": "^0.2.11",
|
|
53
53
|
"@assistant-ui/tap": "^0.5.11",
|
|
54
54
|
"@radix-ui/primitive": "^1.1.3",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
-
import { useAuiState } from "@assistant-ui/store";
|
|
4
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
5
5
|
import type {
|
|
6
6
|
MessagePartStatus,
|
|
7
7
|
ReasoningMessagePart,
|
|
@@ -75,13 +75,28 @@ export const useSmooth = (
|
|
|
75
75
|
smooth: boolean = false,
|
|
76
76
|
): MessagePartState & (TextMessagePart | ReasoningMessagePart) => {
|
|
77
77
|
const { text } = state;
|
|
78
|
-
const id = useAuiState((s) => s.message.id);
|
|
79
78
|
|
|
80
|
-
const idRef = useRef(id);
|
|
81
79
|
const [displayedText, setDisplayedText] = useState(
|
|
82
80
|
state.status.type === "running" ? "" : text,
|
|
83
81
|
);
|
|
84
82
|
|
|
83
|
+
// Render-phase resync on part flip or text discontinuity, so the
|
|
84
|
+
// first paint after a thread switch never shows the previous
|
|
85
|
+
// part's text (#4051). `displayedText` is already a prefix of
|
|
86
|
+
// `text` during normal streaming, so use it as the previous-text
|
|
87
|
+
// reference instead of carrying separate state — avoids the
|
|
88
|
+
// double render per streaming token. Read part identity through
|
|
89
|
+
// `useAuiState` so we actually subscribe to its changes instead
|
|
90
|
+
// of relying on a render-time proxy reference that may be stable
|
|
91
|
+
// across thread swaps.
|
|
92
|
+
const aui = useAui();
|
|
93
|
+
const part = useAuiState(() => aui.part());
|
|
94
|
+
const [prevPart, setPrevPart] = useState(part);
|
|
95
|
+
if (part !== prevPart || !text.startsWith(displayedText)) {
|
|
96
|
+
setPrevPart(part);
|
|
97
|
+
setDisplayedText(state.status.type === "running" ? "" : text);
|
|
98
|
+
}
|
|
99
|
+
|
|
85
100
|
const smoothStatusStore = useSmoothStatusStore({ optional: true });
|
|
86
101
|
const setText = useCallbackRef((text: string) => {
|
|
87
102
|
setDisplayedText(text);
|
|
@@ -109,35 +124,36 @@ export const useSmooth = (
|
|
|
109
124
|
new TextStreamAnimator(displayedText, setText),
|
|
110
125
|
);
|
|
111
126
|
|
|
127
|
+
const animatorPartRef = useRef(part);
|
|
112
128
|
useEffect(() => {
|
|
113
129
|
if (!smooth) {
|
|
114
130
|
animatorRef.stop();
|
|
115
131
|
return;
|
|
116
132
|
}
|
|
117
133
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
134
|
+
// Discontinuity: part flipped, or new text breaks continuation
|
|
135
|
+
// of the animator's current target. Either case requires
|
|
136
|
+
// resetting the cursor — without the part check, a new part
|
|
137
|
+
// whose text happens to share a prefix with the previous target
|
|
138
|
+
// would keep the stale cursor and flicker.
|
|
139
|
+
const partChanged = animatorPartRef.current !== part;
|
|
140
|
+
animatorPartRef.current = part;
|
|
141
|
+
if (partChanged || !text.startsWith(animatorRef.targetText)) {
|
|
121
142
|
if (state.status.type === "running") {
|
|
122
|
-
// New streaming message → animate from empty string
|
|
123
|
-
setText("");
|
|
124
143
|
animatorRef.currentText = "";
|
|
125
144
|
animatorRef.targetText = text;
|
|
126
145
|
animatorRef.start();
|
|
127
146
|
} else {
|
|
128
|
-
// Completed message → display immediately
|
|
129
|
-
setText(text);
|
|
130
147
|
animatorRef.currentText = text;
|
|
131
148
|
animatorRef.targetText = text;
|
|
132
149
|
animatorRef.stop();
|
|
133
150
|
}
|
|
134
|
-
|
|
135
151
|
return;
|
|
136
152
|
}
|
|
137
153
|
|
|
138
154
|
animatorRef.targetText = text;
|
|
139
155
|
animatorRef.start();
|
|
140
|
-
}, [
|
|
156
|
+
}, [animatorRef, smooth, text, state.status.type, part]);
|
|
141
157
|
|
|
142
158
|
useEffect(() => {
|
|
143
159
|
return () => {
|