From 22de9232982d4f8bc06e54d5fe637fe9e35647ac Mon Sep 17 00:00:00 2001
From: josh <144584931+dancer@users.noreply.github.com>
Date: Sat, 20 Dec 2025 00:23:15 +0000
Subject: [PATCH] fix dialog animations, mobile input ux, and tool descriptions
(#1362)
---
app/(chat)/page.tsx | 11 +----
components/multimodal-input.tsx | 15 ++++--
components/ui/alert-dialog.tsx | 65 +++++++++++++-------------
components/ui/dialog.tsx | 2 +-
lib/ai/prompts.ts | 10 +++-
lib/ai/tools/request-suggestions.ts | 72 +++++++++++++++++++----------
6 files changed, 101 insertions(+), 74 deletions(-)
diff --git a/app/(chat)/page.tsx b/app/(chat)/page.tsx
index abb7e5f..332ed4b 100644
--- a/app/(chat)/page.tsx
+++ b/app/(chat)/page.tsx
@@ -1,11 +1,9 @@
import { cookies } from "next/headers";
-import { redirect } from "next/navigation";
import { Suspense } from "react";
import { Chat } from "@/components/chat";
import { DataStreamHandler } from "@/components/data-stream-handler";
import { DEFAULT_CHAT_MODEL } from "@/lib/ai/models";
import { generateUUID } from "@/lib/utils";
-import { auth } from "../(auth)/auth";
export default function Page() {
return (
@@ -16,16 +14,9 @@ export default function Page() {
}
async function NewChatPage() {
- const session = await auth();
-
- if (!session) {
- redirect("/api/auth/guest");
- }
-
- const id = generateUUID();
-
const cookieStore = await cookies();
const modelIdFromCookie = cookieStore.get("chat-model");
+ const id = generateUUID();
if (!modelIdFromCookie) {
return (
diff --git a/components/multimodal-input.tsx b/components/multimodal-input.tsx
index 7a93b3b..9768241 100644
--- a/components/multimodal-input.tsx
+++ b/components/multimodal-input.tsx
@@ -11,7 +11,6 @@ import {
type SetStateAction,
useCallback,
useEffect,
- useMemo,
useRef,
useState,
} from "react";
@@ -100,6 +99,17 @@ function PureMultimodalInput({
}
}, [adjustHeight]);
+ const hasAutoFocused = useRef(false);
+ useEffect(() => {
+ if (!hasAutoFocused.current && width) {
+ const timer = setTimeout(() => {
+ textareaRef.current?.focus();
+ hasAutoFocused.current = true;
+ }, 100);
+ return () => clearTimeout(timer);
+ }
+ }, [width]);
+
const resetHeight = useCallback(() => {
if (textareaRef.current) {
textareaRef.current.style.height = "44px";
@@ -352,8 +362,7 @@ function PureMultimodalInput({
)}
,
@@ -18,14 +17,14 @@ const AlertDialogOverlay = React.forwardRef<
>(({ className, ...props }, ref) => (
-))
-AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
+));
+AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
const AlertDialogContent = React.forwardRef<
React.ElementRef,
@@ -34,16 +33,16 @@ const AlertDialogContent = React.forwardRef<
-))
-AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
+));
+AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
const AlertDialogHeader = ({
className,
@@ -56,8 +55,8 @@ const AlertDialogHeader = ({
)}
{...props}
/>
-)
-AlertDialogHeader.displayName = "AlertDialogHeader"
+);
+AlertDialogHeader.displayName = "AlertDialogHeader";
const AlertDialogFooter = ({
className,
@@ -70,61 +69,61 @@ const AlertDialogFooter = ({
)}
{...props}
/>
-)
-AlertDialogFooter.displayName = "AlertDialogFooter"
+);
+AlertDialogFooter.displayName = "AlertDialogFooter";
const AlertDialogTitle = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
-))
-AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
+));
+AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
const AlertDialogDescription = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
-))
+));
AlertDialogDescription.displayName =
- AlertDialogPrimitive.Description.displayName
+ AlertDialogPrimitive.Description.displayName;
const AlertDialogAction = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
-))
-AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
+));
+AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
const AlertDialogCancel = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
-))
-AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
+));
+AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
export {
AlertDialog,
@@ -138,4 +137,4 @@ export {
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
-}
+};
diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx
index f38593b..7bab9f0 100644
--- a/components/ui/dialog.tsx
+++ b/components/ui/dialog.tsx
@@ -38,7 +38,7 @@ const DialogContent = React.forwardRef<
tool({
- description: "Request suggestions for a document",
+ description:
+ "Request writing suggestions for an existing document artifact. Only use this when the user explicitly asks to improve or get suggestions for a document they have already created. Never use for general questions.",
inputSchema: z.object({
documentId: z
.string()
- .describe("The ID of the document to request edits"),
+ .describe(
+ "The UUID of an existing document artifact that was previously created with createDocument"
+ ),
}),
execute: async ({ documentId }) => {
const document = await getDocumentById({ id: documentId });
@@ -37,37 +40,56 @@ export const requestSuggestions = ({
"userId" | "createdAt" | "documentCreatedAt"
>[] = [];
- const { elementStream } = streamObject({
+ const { partialOutputStream } = streamText({
model: getArtifactModel(),
system:
"You are a help writing assistant. Given a piece of writing, please offer suggestions to improve the piece of writing and describe the change. It is very important for the edits to contain full sentences instead of just words. Max 5 suggestions.",
prompt: document.content,
- output: "array",
- schema: z.object({
- originalSentence: z.string().describe("The original sentence"),
- suggestedSentence: z.string().describe("The suggested sentence"),
- description: z.string().describe("The description of the suggestion"),
+ output: Output.array({
+ element: z.object({
+ originalSentence: z.string().describe("The original sentence"),
+ suggestedSentence: z.string().describe("The suggested sentence"),
+ description: z
+ .string()
+ .describe("The description of the suggestion"),
+ }),
}),
});
- for await (const element of elementStream) {
- // @ts-expect-error todo: fix type
- const suggestion: Suggestion = {
- originalText: element.originalSentence,
- suggestedText: element.suggestedSentence,
- description: element.description,
- id: generateUUID(),
- documentId,
- isResolved: false,
- };
+ let processedCount = 0;
+ for await (const partialOutput of partialOutputStream) {
+ if (!partialOutput) {
+ continue;
+ }
- dataStream.write({
- type: "data-suggestion",
- data: suggestion,
- transient: true,
- });
+ for (let i = processedCount; i < partialOutput.length; i++) {
+ const element = partialOutput[i];
+ if (
+ !element?.originalSentence ||
+ !element?.suggestedSentence ||
+ !element?.description
+ ) {
+ continue;
+ }
- suggestions.push(suggestion);
+ const suggestion = {
+ originalText: element.originalSentence,
+ suggestedText: element.suggestedSentence,
+ description: element.description,
+ id: generateUUID(),
+ documentId,
+ isResolved: false,
+ };
+
+ dataStream.write({
+ type: "data-suggestion",
+ data: suggestion as Suggestion,
+ transient: true,
+ });
+
+ suggestions.push(suggestion);
+ processedCount++;
+ }
}
if (session.user?.id) {