Skip to content

Commit 666c54d

Browse files
committed
fix dialog animations, mobile input ux, and tool descriptions
1 parent 4d3ba8d commit 666c54d

File tree

6 files changed

+101
-74
lines changed

6 files changed

+101
-74
lines changed

app/(chat)/page.tsx

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import { cookies } from "next/headers";
2-
import { redirect } from "next/navigation";
32
import { Suspense } from "react";
43
import { Chat } from "@/components/chat";
54
import { DataStreamHandler } from "@/components/data-stream-handler";
65
import { DEFAULT_CHAT_MODEL } from "@/lib/ai/models";
76
import { generateUUID } from "@/lib/utils";
8-
import { auth } from "../(auth)/auth";
97

108
export default function Page() {
119
return (
@@ -16,16 +14,9 @@ export default function Page() {
1614
}
1715

1816
async function NewChatPage() {
19-
const session = await auth();
20-
21-
if (!session) {
22-
redirect("/api/auth/guest");
23-
}
24-
25-
const id = generateUUID();
26-
2717
const cookieStore = await cookies();
2818
const modelIdFromCookie = cookieStore.get("chat-model");
19+
const id = generateUUID();
2920

3021
if (!modelIdFromCookie) {
3122
return (

components/multimodal-input.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
type SetStateAction,
1212
useCallback,
1313
useEffect,
14-
useMemo,
1514
useRef,
1615
useState,
1716
} from "react";
@@ -100,6 +99,17 @@ function PureMultimodalInput({
10099
}
101100
}, [adjustHeight]);
102101

102+
const hasAutoFocused = useRef(false);
103+
useEffect(() => {
104+
if (!hasAutoFocused.current && width) {
105+
const timer = setTimeout(() => {
106+
textareaRef.current?.focus();
107+
hasAutoFocused.current = true;
108+
}, 100);
109+
return () => clearTimeout(timer);
110+
}
111+
}, [width]);
112+
103113
const resetHeight = useCallback(() => {
104114
if (textareaRef.current) {
105115
textareaRef.current.style.height = "44px";
@@ -352,8 +362,7 @@ function PureMultimodalInput({
352362
)}
353363
<div className="flex flex-row items-start gap-1 sm:gap-2">
354364
<PromptInputTextarea
355-
autoFocus
356-
className="grow resize-none border-0! border-none! bg-transparent p-2 text-sm outline-none ring-0 [-ms-overflow-style:none] [scrollbar-width:none] placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 [&::-webkit-scrollbar]:hidden"
365+
className="grow resize-none border-0! border-none! bg-transparent p-2 text-base outline-none ring-0 [-ms-overflow-style:none] [scrollbar-width:none] placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 [&::-webkit-scrollbar]:hidden"
357366
data-testid="multimodal-input"
358367
disableAutoResize={true}
359368
maxHeight={200}

components/ui/alert-dialog.tsx

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
1-
"use client"
1+
"use client";
22

3-
import * as React from "react"
4-
import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
3+
import { AlertDialog as AlertDialogPrimitive } from "radix-ui";
4+
import * as React from "react";
5+
import { buttonVariants } from "@/components/ui/button";
6+
import { cn } from "@/lib/utils";
57

6-
import { cn } from "@/lib/utils"
7-
import { buttonVariants } from "@/components/ui/button"
8+
const AlertDialog = AlertDialogPrimitive.Root;
89

9-
const AlertDialog = AlertDialogPrimitive.Root
10+
const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
1011

11-
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
12-
13-
const AlertDialogPortal = AlertDialogPrimitive.Portal
12+
const AlertDialogPortal = AlertDialogPrimitive.Portal;
1413

1514
const AlertDialogOverlay = React.forwardRef<
1615
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
1716
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
1817
>(({ className, ...props }, ref) => (
1918
<AlertDialogPrimitive.Overlay
2019
className={cn(
21-
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
20+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80 data-[state=closed]:animate-out data-[state=open]:animate-in",
2221
className
2322
)}
2423
{...props}
2524
ref={ref}
2625
/>
27-
))
28-
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
26+
));
27+
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
2928

3029
const AlertDialogContent = React.forwardRef<
3130
React.ElementRef<typeof AlertDialogPrimitive.Content>,
@@ -34,16 +33,16 @@ const AlertDialogContent = React.forwardRef<
3433
<AlertDialogPortal>
3534
<AlertDialogOverlay />
3635
<AlertDialogPrimitive.Content
37-
ref={ref}
3836
className={cn(
39-
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
37+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:rounded-lg",
4038
className
4139
)}
40+
ref={ref}
4241
{...props}
4342
/>
4443
</AlertDialogPortal>
45-
))
46-
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
44+
));
45+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
4746

4847
const AlertDialogHeader = ({
4948
className,
@@ -56,8 +55,8 @@ const AlertDialogHeader = ({
5655
)}
5756
{...props}
5857
/>
59-
)
60-
AlertDialogHeader.displayName = "AlertDialogHeader"
58+
);
59+
AlertDialogHeader.displayName = "AlertDialogHeader";
6160

6261
const AlertDialogFooter = ({
6362
className,
@@ -70,61 +69,61 @@ const AlertDialogFooter = ({
7069
)}
7170
{...props}
7271
/>
73-
)
74-
AlertDialogFooter.displayName = "AlertDialogFooter"
72+
);
73+
AlertDialogFooter.displayName = "AlertDialogFooter";
7574

7675
const AlertDialogTitle = React.forwardRef<
7776
React.ElementRef<typeof AlertDialogPrimitive.Title>,
7877
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
7978
>(({ className, ...props }, ref) => (
8079
<AlertDialogPrimitive.Title
80+
className={cn("font-semibold text-lg", className)}
8181
ref={ref}
82-
className={cn("text-lg font-semibold", className)}
8382
{...props}
8483
/>
85-
))
86-
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
84+
));
85+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
8786

8887
const AlertDialogDescription = React.forwardRef<
8988
React.ElementRef<typeof AlertDialogPrimitive.Description>,
9089
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
9190
>(({ className, ...props }, ref) => (
9291
<AlertDialogPrimitive.Description
92+
className={cn("text-muted-foreground text-sm", className)}
9393
ref={ref}
94-
className={cn("text-sm text-muted-foreground", className)}
9594
{...props}
9695
/>
97-
))
96+
));
9897
AlertDialogDescription.displayName =
99-
AlertDialogPrimitive.Description.displayName
98+
AlertDialogPrimitive.Description.displayName;
10099

101100
const AlertDialogAction = React.forwardRef<
102101
React.ElementRef<typeof AlertDialogPrimitive.Action>,
103102
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
104103
>(({ className, ...props }, ref) => (
105104
<AlertDialogPrimitive.Action
106-
ref={ref}
107105
className={cn(buttonVariants(), className)}
106+
ref={ref}
108107
{...props}
109108
/>
110-
))
111-
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
109+
));
110+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
112111

113112
const AlertDialogCancel = React.forwardRef<
114113
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
115114
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
116115
>(({ className, ...props }, ref) => (
117116
<AlertDialogPrimitive.Cancel
118-
ref={ref}
119117
className={cn(
120118
buttonVariants({ variant: "outline" }),
121119
"mt-2 sm:mt-0",
122120
className
123121
)}
122+
ref={ref}
124123
{...props}
125124
/>
126-
))
127-
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
125+
));
126+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
128127

129128
export {
130129
AlertDialog,
@@ -138,4 +137,4 @@ export {
138137
AlertDialogDescription,
139138
AlertDialogAction,
140139
AlertDialogCancel,
141-
}
140+
};

components/ui/dialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const DialogContent = React.forwardRef<
3838
<DialogPrimitive.Content
3939
ref={ref}
4040
className={cn(
41-
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
41+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 sm:rounded-lg",
4242
className
4343
)}
4444
{...props}

lib/ai/prompts.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,16 @@ This is a guide for using artifacts tools: \`createDocument\` and \`updateDocume
3030
- Immediately after creating a document
3131
3232
Do not update document right after creating it. Wait for user feedback or request to update it.
33+
34+
**Using \`requestSuggestions\`:**
35+
- ONLY use when the user explicitly asks for suggestions on an existing document
36+
- Requires a valid document ID from a previously created document
37+
- Never use for general questions or information requests
3338
`;
3439

35-
export const regularPrompt =
36-
"You are a friendly assistant! Keep your responses concise and helpful.";
40+
export const regularPrompt = `You are a friendly assistant! Keep your responses concise and helpful.
41+
42+
When asked to write, create, or help with something, just do it directly. Don't ask clarifying questions unless absolutely necessary - make reasonable assumptions and proceed with the task.`;
3743

3844
export type RequestHints = {
3945
latitude: Geo["latitude"];

lib/ai/tools/request-suggestions.ts

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { streamObject, tool, type UIMessageStreamWriter } from "ai";
1+
import { Output, streamText, tool, type UIMessageStreamWriter } from "ai";
22
import type { Session } from "next-auth";
33
import { z } from "zod";
44
import { getDocumentById, saveSuggestions } from "@/lib/db/queries";
@@ -17,11 +17,14 @@ export const requestSuggestions = ({
1717
dataStream,
1818
}: RequestSuggestionsProps) =>
1919
tool({
20-
description: "Request suggestions for a document",
20+
description:
21+
"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.",
2122
inputSchema: z.object({
2223
documentId: z
2324
.string()
24-
.describe("The ID of the document to request edits"),
25+
.describe(
26+
"The UUID of an existing document artifact that was previously created with createDocument"
27+
),
2528
}),
2629
execute: async ({ documentId }) => {
2730
const document = await getDocumentById({ id: documentId });
@@ -37,37 +40,56 @@ export const requestSuggestions = ({
3740
"userId" | "createdAt" | "documentCreatedAt"
3841
>[] = [];
3942

40-
const { elementStream } = streamObject({
43+
const { partialOutputStream } = streamText({
4144
model: getArtifactModel(),
4245
system:
4346
"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.",
4447
prompt: document.content,
45-
output: "array",
46-
schema: z.object({
47-
originalSentence: z.string().describe("The original sentence"),
48-
suggestedSentence: z.string().describe("The suggested sentence"),
49-
description: z.string().describe("The description of the suggestion"),
48+
output: Output.array({
49+
element: z.object({
50+
originalSentence: z.string().describe("The original sentence"),
51+
suggestedSentence: z.string().describe("The suggested sentence"),
52+
description: z
53+
.string()
54+
.describe("The description of the suggestion"),
55+
}),
5056
}),
5157
});
5258

53-
for await (const element of elementStream) {
54-
// @ts-expect-error todo: fix type
55-
const suggestion: Suggestion = {
56-
originalText: element.originalSentence,
57-
suggestedText: element.suggestedSentence,
58-
description: element.description,
59-
id: generateUUID(),
60-
documentId,
61-
isResolved: false,
62-
};
59+
let processedCount = 0;
60+
for await (const partialOutput of partialOutputStream) {
61+
if (!partialOutput) {
62+
continue;
63+
}
6364

64-
dataStream.write({
65-
type: "data-suggestion",
66-
data: suggestion,
67-
transient: true,
68-
});
65+
for (let i = processedCount; i < partialOutput.length; i++) {
66+
const element = partialOutput[i];
67+
if (
68+
!element?.originalSentence ||
69+
!element?.suggestedSentence ||
70+
!element?.description
71+
) {
72+
continue;
73+
}
74+
75+
const suggestion = {
76+
originalText: element.originalSentence,
77+
suggestedText: element.suggestedSentence,
78+
description: element.description,
79+
id: generateUUID(),
80+
documentId,
81+
isResolved: false,
82+
};
83+
84+
dataStream.write({
85+
type: "data-suggestion",
86+
data: suggestion as Suggestion,
87+
transient: true,
88+
});
6989

70-
suggestions.push(suggestion);
90+
suggestions.push(suggestion);
91+
processedCount++;
92+
}
7193
}
7294

7395
if (session.user?.id) {

0 commit comments

Comments
 (0)