components/followup-suggestions.tsx
"use client";
import { useChatStoreApi } from "@ai-sdk-tools/store";
import { useMessageIds } from "@/lib/stores/hooks-base";
import {
useMessagePartByPartIdx,
useMessagePartTypesById,
} from "@/lib/stores/hooks-message-parts";
import type { ChatMessage } from "@/lib/ai/types";
import { generateUUID } from "@/lib/utils";
export function FollowUpSuggestionsParts({ messageId }: { messageId: string }) {
const types = useMessagePartTypesById(messageId);
const ids = useMessageIds();
const isLastMessage = ids.at(-1) === messageId;
// Only show on the last message
if (!isLastMessage) {
return null;
}
const partIdx = types.indexOf("data-followupSuggestions");
if (partIdx === -1) {
return null;
}
return <FollowUpSuggestionsPart messageId={messageId} partIdx={partIdx} />;
}
function FollowUpSuggestionsPart({
messageId,
partIdx,
}: {
messageId: string;
partIdx: number;
}) {
const part = useMessagePartByPartIdx(
messageId,
partIdx,
"data-followupSuggestions"
);
return <FollowUpSuggestions suggestions={part.data.suggestions} />;
}
export function FollowUpSuggestions({
suggestions,
}: {
suggestions: string[];
}) {
const storeApi = useChatStoreApi();
const handleClick = (suggestion: string) => {
const sendMessage = storeApi.getState().sendMessage;
const parentMessageId = storeApi.getState().getLastMessageId();
const message: ChatMessage = {
id: generateUUID(),
role: "user",
parts: [{ type: "text", text: suggestion }],
metadata: {
createdAt: new Date(),
parentMessageId,
selectedModel: "your-default-model", // Get from context
},
};
sendMessage(message);
};
if (!suggestions?.length) return null;
return (
<div className="mt-2 flex flex-col gap-2">
<div className="text-xs text-muted-foreground">Related</div>
<div className="flex flex-wrap gap-1.5">
{suggestions.map((s) => (
<button
key={s}
onClick={() => handleClick(s)}
className="rounded-md border px-2 py-1 text-sm hover:bg-muted"
>
{s}
</button>
))}
</div>
</div>
);
}