@steerable/agent-ui 0.6.57 → 0.6.58
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.
|
@@ -30,6 +30,8 @@ export function useChatList(options) {
|
|
|
30
30
|
const [error, setError] = useState(null);
|
|
31
31
|
const [selectedAgentId, setSelectedAgentId] = useState(null);
|
|
32
32
|
const pageRef = useRef(1);
|
|
33
|
+
const hasMoreRef = useRef(false);
|
|
34
|
+
const loadingMoreRef = useRef(false);
|
|
33
35
|
const mountedRef = useRef(true);
|
|
34
36
|
useEffect(() => {
|
|
35
37
|
mountedRef.current = true;
|
|
@@ -45,6 +47,7 @@ export function useChatList(options) {
|
|
|
45
47
|
return;
|
|
46
48
|
setChats(res.chats);
|
|
47
49
|
setHasMore(res.hasMore);
|
|
50
|
+
hasMoreRef.current = res.hasMore;
|
|
48
51
|
pageRef.current = 1;
|
|
49
52
|
setError(null);
|
|
50
53
|
}
|
|
@@ -69,16 +72,27 @@ export function useChatList(options) {
|
|
|
69
72
|
}
|
|
70
73
|
}, []);
|
|
71
74
|
const loadMoreChats = useCallback(async () => {
|
|
72
|
-
if (!
|
|
75
|
+
if (!hasMoreRef.current || loadingMoreRef.current)
|
|
73
76
|
return;
|
|
77
|
+
loadingMoreRef.current = true;
|
|
74
78
|
setLoadingMore(true);
|
|
75
79
|
try {
|
|
76
80
|
const next = pageRef.current + 1;
|
|
77
81
|
const res = await transportRef.current.listChats({ page: next, pageSize });
|
|
78
82
|
if (!mountedRef.current)
|
|
79
83
|
return;
|
|
80
|
-
setChats((prev) =>
|
|
84
|
+
setChats((prev) => {
|
|
85
|
+
const ids = new Set(prev.map(transportRef.current.getChatId));
|
|
86
|
+
return prev.concat(res.chats.filter((chat) => {
|
|
87
|
+
const id = transportRef.current.getChatId(chat);
|
|
88
|
+
if (ids.has(id))
|
|
89
|
+
return false;
|
|
90
|
+
ids.add(id);
|
|
91
|
+
return true;
|
|
92
|
+
}));
|
|
93
|
+
});
|
|
81
94
|
setHasMore(res.hasMore);
|
|
95
|
+
hasMoreRef.current = res.hasMore;
|
|
82
96
|
pageRef.current = next;
|
|
83
97
|
}
|
|
84
98
|
catch (err) {
|
|
@@ -86,10 +100,11 @@ export function useChatList(options) {
|
|
|
86
100
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
87
101
|
}
|
|
88
102
|
finally {
|
|
103
|
+
loadingMoreRef.current = false;
|
|
89
104
|
if (mountedRef.current)
|
|
90
105
|
setLoadingMore(false);
|
|
91
106
|
}
|
|
92
|
-
}, [
|
|
107
|
+
}, [pageSize]);
|
|
93
108
|
const createChat = useCallback(async (input) => {
|
|
94
109
|
const t = transportRef.current;
|
|
95
110
|
if (!t.createChat)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steerable/agent-ui",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.58",
|
|
4
4
|
"description": "Steerable framework Tier 4 — headless React hooks (useAgentSession, useChatStream, useToolCallStatus) and Tailwind-themed components (ChatPanel, MessageList, OrchestrationPlanCard, ToolCallRenderer, SSEStreamView) for embedding Steerable agents in any React app.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://steerableframework.com/storybook/",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"README.md"
|
|
58
58
|
],
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@steerable/agent-protocol": "0.6.
|
|
60
|
+
"@steerable/agent-protocol": "0.6.58"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"react": ">=18.0.0",
|
|
@@ -83,6 +83,22 @@ describe('useChatList', () => {
|
|
|
83
83
|
expect(result.current.hasMoreChats).toBe(false);
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
+
it('coalesces concurrent loadMoreChats calls for the same page', async () => {
|
|
87
|
+
const { transport, listChats } = makeTransport();
|
|
88
|
+
const { result } = renderHook(() => useChatList({ transport, pageSize: 50 }));
|
|
89
|
+
|
|
90
|
+
await waitFor(() => expect(result.current.chats.length).toBe(2));
|
|
91
|
+
|
|
92
|
+
await act(async () => {
|
|
93
|
+
const first = result.current.loadMoreChats();
|
|
94
|
+
const second = result.current.loadMoreChats();
|
|
95
|
+
await Promise.all([first, second]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
expect(listChats).toHaveBeenCalledTimes(2);
|
|
99
|
+
expect(result.current.chats.map((c) => c.id)).toEqual(['c1', 'c2', 'c3']);
|
|
100
|
+
});
|
|
101
|
+
|
|
86
102
|
it('patchChatTitle updates without re-fetching', async () => {
|
|
87
103
|
const { transport, listChats } = makeTransport();
|
|
88
104
|
const { result } = renderHook(() => useChatList({ transport, pageSize: 50 }));
|
package/src/state/useChatList.ts
CHANGED
|
@@ -88,6 +88,8 @@ export function useChatList<TChat, TAgent>(
|
|
|
88
88
|
const [error, setError] = useState<Error | null>(null);
|
|
89
89
|
const [selectedAgentId, setSelectedAgentId] = useState<string | null>(null);
|
|
90
90
|
const pageRef = useRef(1);
|
|
91
|
+
const hasMoreRef = useRef(false);
|
|
92
|
+
const loadingMoreRef = useRef(false);
|
|
91
93
|
const mountedRef = useRef(true);
|
|
92
94
|
|
|
93
95
|
useEffect(() => {
|
|
@@ -104,6 +106,7 @@ export function useChatList<TChat, TAgent>(
|
|
|
104
106
|
if (!mountedRef.current) return;
|
|
105
107
|
setChats(res.chats);
|
|
106
108
|
setHasMore(res.hasMore);
|
|
109
|
+
hasMoreRef.current = res.hasMore;
|
|
107
110
|
pageRef.current = 1;
|
|
108
111
|
setError(null);
|
|
109
112
|
} catch (err) {
|
|
@@ -123,21 +126,34 @@ export function useChatList<TChat, TAgent>(
|
|
|
123
126
|
}, []);
|
|
124
127
|
|
|
125
128
|
const loadMoreChats = useCallback(async () => {
|
|
126
|
-
if (!
|
|
129
|
+
if (!hasMoreRef.current || loadingMoreRef.current) return;
|
|
130
|
+
loadingMoreRef.current = true;
|
|
127
131
|
setLoadingMore(true);
|
|
128
132
|
try {
|
|
129
133
|
const next = pageRef.current + 1;
|
|
130
134
|
const res = await transportRef.current.listChats({ page: next, pageSize });
|
|
131
135
|
if (!mountedRef.current) return;
|
|
132
|
-
setChats((prev) =>
|
|
136
|
+
setChats((prev) => {
|
|
137
|
+
const ids = new Set(prev.map(transportRef.current.getChatId));
|
|
138
|
+
return prev.concat(
|
|
139
|
+
res.chats.filter((chat) => {
|
|
140
|
+
const id = transportRef.current.getChatId(chat);
|
|
141
|
+
if (ids.has(id)) return false;
|
|
142
|
+
ids.add(id);
|
|
143
|
+
return true;
|
|
144
|
+
}),
|
|
145
|
+
);
|
|
146
|
+
});
|
|
133
147
|
setHasMore(res.hasMore);
|
|
148
|
+
hasMoreRef.current = res.hasMore;
|
|
134
149
|
pageRef.current = next;
|
|
135
150
|
} catch (err) {
|
|
136
151
|
if (mountedRef.current) setError(err instanceof Error ? err : new Error(String(err)));
|
|
137
152
|
} finally {
|
|
153
|
+
loadingMoreRef.current = false;
|
|
138
154
|
if (mountedRef.current) setLoadingMore(false);
|
|
139
155
|
}
|
|
140
|
-
}, [
|
|
156
|
+
}, [pageSize]);
|
|
141
157
|
|
|
142
158
|
const createChat = useCallback(
|
|
143
159
|
async (input?: { agentId?: string; projectId?: string }) => {
|