fix(room): clear AI stream state on room switch and hide cursor for saved chunks
- Clear activeAiStream, streamingChunks, and timers when room changes - Add showCursor prop to OrderedStreamChunks — only show cursor during active streaming, not for saved content
This commit is contained in:
parent
ab1ef0d1a7
commit
2bd40aee1b
@ -52,9 +52,12 @@ function parseSavedChunks(raw: string | null | undefined): Array<{ type: string;
|
||||
function OrderedStreamChunks({
|
||||
chunks,
|
||||
onMentionClick,
|
||||
showCursor = true,
|
||||
}: {
|
||||
chunks: Array<{ type: string; content: string }>;
|
||||
onMentionClick?: (type: string, id: string, label: string) => void;
|
||||
/** Show blinking cursor — only during active streaming */
|
||||
showCursor?: boolean;
|
||||
}) {
|
||||
// Group consecutive same-type chunks (tool_call hidden)
|
||||
const groups: Array<{ type: 'thinking' | 'answer'; content: string }> = [];
|
||||
@ -78,8 +81,8 @@ function OrderedStreamChunks({
|
||||
<MessageContent key={i} content={group.content} onMentionClick={onMentionClick} />
|
||||
),
|
||||
)}
|
||||
{/* Streaming cursor */}
|
||||
<span className="discord-streaming-cursor" />
|
||||
{/* Streaming cursor — only shown during active streaming */}
|
||||
{showCursor && <span className="discord-streaming-cursor" />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -415,10 +418,11 @@ export const MessageBubble = memo(function MessageBubble({
|
||||
onMentionClick={handleMentionClick}
|
||||
/>
|
||||
) : parseSavedChunks(message.thinking_content) ? (
|
||||
/* Saved ordered chunks — render in original order */
|
||||
/* Saved ordered chunks — render in original order (no cursor) */
|
||||
<OrderedStreamChunks
|
||||
chunks={parseSavedChunks(message.thinking_content)!}
|
||||
onMentionClick={handleMentionClick}
|
||||
showCursor={false}
|
||||
/>
|
||||
) : (
|
||||
/* Legacy: aggregated thinking at top, content at bottom */
|
||||
|
||||
@ -278,6 +278,13 @@ export function RoomProvider({
|
||||
setMessages([]);
|
||||
setIsHistoryLoaded(false);
|
||||
setNextCursor(null);
|
||||
// Clear AI stream state — prevents ghost "thinking..." indicator on room switch
|
||||
setActiveAiStream(null);
|
||||
setStreamingChunks(new Map());
|
||||
streamingChunksRef.current.clear();
|
||||
// Clear all streaming timers
|
||||
streamingTimersRef.current.forEach((timer) => clearTimeout(timer));
|
||||
streamingTimersRef.current.clear();
|
||||
|
||||
// Merge any buffered messages for the new room (Bug 3 fix)
|
||||
if (activeRoomId) {
|
||||
@ -1310,6 +1317,7 @@ export function RoomProvider({
|
||||
}, [projectName]);
|
||||
|
||||
// Fetch room AI configs for @ai: mention suggestions
|
||||
// Retry up to 3 times if any config has missing modelName
|
||||
const fetchRoomAiConfigs = useCallback(async () => {
|
||||
const client = wsClientRef.current;
|
||||
if (!activeRoomId || !client) {
|
||||
@ -1318,13 +1326,28 @@ export function RoomProvider({
|
||||
}
|
||||
setAiConfigsLoading(true);
|
||||
try {
|
||||
const configs = await client.aiList(activeRoomId);
|
||||
setRoomAiConfigs(
|
||||
configs.map((cfg) => ({
|
||||
const fetchOnce = async (): Promise<RoomAiConfig[]> => {
|
||||
const configs = await client.aiList(activeRoomId);
|
||||
return configs.map((cfg) => ({
|
||||
model: cfg.model,
|
||||
modelName: cfg.model_name,
|
||||
})),
|
||||
);
|
||||
}));
|
||||
};
|
||||
|
||||
let configs = await fetchOnce();
|
||||
let retries = 0;
|
||||
const maxRetries = 3;
|
||||
|
||||
while (
|
||||
configs.some((c) => !c.modelName) &&
|
||||
retries < maxRetries
|
||||
) {
|
||||
retries++;
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
configs = await fetchOnce();
|
||||
}
|
||||
|
||||
setRoomAiConfigs(configs);
|
||||
} catch {
|
||||
setRoomAiConfigs([]);
|
||||
} finally {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user