Unverified 提交 5e71af36 authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Show info toast when switching to agent mode (#2160)

<!-- CURSOR_SUMMARY --> > [!NOTE] > Adds an informational toast to guide users when enabling the new agent mode. > > - New `LocalAgentNewChatToast` with dismiss and "Never show again" actions (auto-dismiss after 8s) > - `ChatModeSelector`: when switching to `local-agent` on `/chat` with existing messages, show the toast using `sonner`; uses TanStack Router for route detection and Jotai chat state > - `UserSettingsSchema`: adds `hideLocalAgentNewChatToast` to persist user preference > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit b07beac9d13b87b2e08ea1a9a376994148beb6c9. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Shows a friendly info toast when switching to Agent mode on the chat page, encouraging a fresh chat for better results. Includes dismiss and “never show again” options that respect user preferences. - **New Features** - Added LocalAgentNewChatToast, shown when selecting local-agent on /chat with existing messages. - “Never show again” persists via hideLocalAgentNewChatToast in UserSettingsSchema. - Uses TanStack Router to detect the chat route; toast auto-dismisses after 8s. <sup>Written for commit b07beac9d13b87b2e08ea1a9a376994148beb6c9. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. -->
上级 d40aa21e
...@@ -15,6 +15,11 @@ import type { ChatMode } from "@/lib/schemas"; ...@@ -15,6 +15,11 @@ import type { ChatMode } from "@/lib/schemas";
import { isDyadProEnabled } from "@/lib/schemas"; import { isDyadProEnabled } from "@/lib/schemas";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { detectIsMac } from "@/hooks/useChatModeToggle"; import { detectIsMac } from "@/hooks/useChatModeToggle";
import { useRouterState } from "@tanstack/react-router";
import { toast } from "sonner";
import { LocalAgentNewChatToast } from "./LocalAgentNewChatToast";
import { useAtomValue } from "jotai";
import { chatMessagesByIdAtom } from "@/atoms/chatAtoms";
function ExperimentalBadge() { function ExperimentalBadge() {
return ( return (
...@@ -26,12 +31,44 @@ function ExperimentalBadge() { ...@@ -26,12 +31,44 @@ function ExperimentalBadge() {
export function ChatModeSelector() { export function ChatModeSelector() {
const { settings, updateSettings } = useSettings(); const { settings, updateSettings } = useSettings();
const routerState = useRouterState();
const isChatRoute = routerState.location.pathname === "/chat";
const messagesById = useAtomValue(chatMessagesByIdAtom);
const chatId = routerState.location.search.id as number | undefined;
const currentChatMessages = chatId ? (messagesById.get(chatId) ?? []) : [];
const selectedMode = settings?.selectedChatMode || "build"; const selectedMode = settings?.selectedChatMode || "build";
const isProEnabled = settings ? isDyadProEnabled(settings) : false; const isProEnabled = settings ? isDyadProEnabled(settings) : false;
const handleModeChange = (value: string) => { const handleModeChange = (value: string) => {
updateSettings({ selectedChatMode: value as ChatMode }); const newMode = value as ChatMode;
updateSettings({ selectedChatMode: newMode });
// We want to show a toast when user is switching to the new agent mode
// because they might weird results mixing Build and Agent mode in the same chat.
//
// Only show toast if:
// - User is switching to the new agent mode
// - User is on the chat (not home page) with existing messages
// - User has not explicitly disabled the toast
if (
newMode === "local-agent" &&
isChatRoute &&
currentChatMessages.length > 0 &&
!settings?.hideLocalAgentNewChatToast
) {
toast.custom(
(t) => (
<LocalAgentNewChatToast
toastId={t}
onNeverShowAgain={() => {
updateSettings({ hideLocalAgentNewChatToast: true });
}}
/>
),
{ duration: 8000 },
);
}
}; };
const getModeDisplayName = (mode: ChatMode) => { const getModeDisplayName = (mode: ChatMode) => {
......
import { toast } from "sonner";
import { X, Sparkles } from "lucide-react";
import { Button } from "./ui/button";
interface LocalAgentNewChatToastProps {
toastId: string | number;
onNeverShowAgain: () => void;
}
export function LocalAgentNewChatToast({
toastId,
onNeverShowAgain,
}: LocalAgentNewChatToastProps) {
const handleClose = () => {
toast.dismiss(toastId);
};
const handleNeverShowAgain = () => {
onNeverShowAgain();
toast.dismiss(toastId);
};
return (
<div className="relative bg-blue-50/95 dark:bg-slate-800/95 backdrop-blur-sm border border-blue-200 dark:border-slate-600 rounded-xl shadow-lg min-w-[380px] max-w-[450px] overflow-hidden">
<div className="p-4">
<div className="flex items-start">
<div className="flex-1">
<div className="flex items-center mb-3">
<div className="flex-shrink-0">
<div className="w-6 h-6 bg-gradient-to-br from-blue-500 to-blue-600 dark:from-blue-400 dark:to-blue-500 rounded-full flex items-center justify-center shadow-sm">
<Sparkles className="w-3.5 h-3.5 text-white" />
</div>
</div>
<h3 className="ml-3 text-sm font-semibold text-blue-900 dark:text-blue-100">
Agent Mode Activated
</h3>
{/* Close button */}
<button
type="button"
onClick={handleClose}
className="ml-auto flex-shrink-0 p-1.5 text-blue-500 dark:text-slate-400 hover:text-blue-700 dark:hover:text-slate-200 transition-colors duration-200 rounded-md hover:bg-blue-100/50 dark:hover:bg-slate-700/50"
aria-label="Close"
>
<X className="w-4 h-4" />
</button>
</div>
{/* Message */}
<div className="mb-4">
<p className="text-[14px] text-blue-800 dark:text-slate-200 leading-relaxed">
<strong>Tip: Create a new chat</strong> to give the agent a
clean context for better results.
</p>
</div>
{/* Action buttons */}
<div className="flex items-center justify-end gap-2">
<Button
onClick={handleNeverShowAgain}
size="sm"
variant="ghost"
className="text-blue-600 dark:text-slate-400 hover:text-blue-800 dark:hover:text-slate-200 hover:bg-blue-100/50 dark:hover:bg-slate-700/50"
>
Never show again
</Button>
</div>
</div>
</div>
</div>
</div>
);
}
...@@ -249,6 +249,21 @@ export type AgentToolConsent = z.infer<typeof AgentToolConsentSchema>; ...@@ -249,6 +249,21 @@ export type AgentToolConsent = z.infer<typeof AgentToolConsentSchema>;
* Zod schema for user settings * Zod schema for user settings
*/ */
export const UserSettingsSchema = z.object({ export const UserSettingsSchema = z.object({
////////////////////////////////
// E2E TESTING ONLY.
////////////////////////////////
isTestMode: z.boolean().optional(),
////////////////////////////////
// DEPRECATED.
////////////////////////////////
enableProSaverMode: z.boolean().optional(),
dyadProBudget: DyadProBudgetSchema.optional(),
runtimeMode: RuntimeModeSchema.optional(),
////////////////////////////////
// ACTIVE FIELDS.
////////////////////////////////
selectedModel: LargeLanguageModelSchema, selectedModel: LargeLanguageModelSchema,
providerSettings: z.record(z.string(), ProviderSettingSchema), providerSettings: z.record(z.string(), ProviderSettingSchema),
agentToolConsents: z.record(z.string(), AgentToolConsentSchema).optional(), agentToolConsents: z.record(z.string(), AgentToolConsentSchema).optional(),
...@@ -294,18 +309,7 @@ export const UserSettingsSchema = z.object({ ...@@ -294,18 +309,7 @@ export const UserSettingsSchema = z.object({
systemCpuPercent: z.number().optional(), systemCpuPercent: z.number().optional(),
}) })
.optional(), .optional(),
hideLocalAgentNewChatToast: z.boolean().optional(),
////////////////////////////////
// E2E TESTING ONLY.
////////////////////////////////
isTestMode: z.boolean().optional(),
////////////////////////////////
// DEPRECATED.
////////////////////////////////
enableProSaverMode: z.boolean().optional(),
dyadProBudget: DyadProBudgetSchema.optional(),
runtimeMode: RuntimeModeSchema.optional(),
}); });
/** /**
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论