Run biome check and add ChatError component
This commit is contained in:
parent
6a5041be43
commit
08f45451bb
13 changed files with 228 additions and 136 deletions
|
|
@ -1,6 +1,5 @@
|
|||
import { Chat } from "@/components/chat";
|
||||
import { Container } from "@/components/container";
|
||||
import { Prose } from "@/components/prose";
|
||||
|
||||
export default function ChatPage() {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@
|
|||
"rules": {
|
||||
"recommended": true,
|
||||
"suspicious": {
|
||||
"noUnknownAtRules": "off"
|
||||
"noUnknownAtRules": "off",
|
||||
"noArrayIndexKey": "off"
|
||||
},
|
||||
"complexity": {
|
||||
"noStaticOnlyClass": "off"
|
||||
|
|
|
|||
14
components/chat/chat-error.tsx
Normal file
14
components/chat/chat-error.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { AlertTriangleIcon } from "lucide-react";
|
||||
import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
|
||||
|
||||
export function ChatError({ error }: { error?: Error }) {
|
||||
if (!error) return null;
|
||||
|
||||
return (
|
||||
<Alert variant="destructive">
|
||||
<AlertTriangleIcon />
|
||||
<AlertTitle>{error.name}</AlertTitle>
|
||||
<AlertDescription>{error.message}</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState, type SubmitEvent } from "react";
|
||||
import { ArrowUpIcon } from "lucide-react";
|
||||
import { type SubmitEvent, useState } from "react";
|
||||
import { Button } from "../ui/button";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import { ArrowUpIcon, SendIcon } from "lucide-react";
|
||||
|
||||
interface ChatFormProps {
|
||||
onMessage: (message: { text: string }) => void;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { UIMessage } from "ai";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { UIMessage } from "ai";
|
||||
|
||||
interface ChatMessagesProps {
|
||||
messages: UIMessage[];
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
"use client";
|
||||
|
||||
import { ChatMessages } from "./chat-messages";
|
||||
import { ChatForm } from "./chat-form";
|
||||
import { DefaultChatTransport } from "ai";
|
||||
import { useChat } from "@ai-sdk/react";
|
||||
import { DefaultChatTransport } from "ai";
|
||||
import { ChatError } from "./chat-error";
|
||||
import { ChatForm } from "./chat-form";
|
||||
import { ChatMessages } from "./chat-messages";
|
||||
|
||||
export function Chat() {
|
||||
const { messages, sendMessage, status, error } = useChat({
|
||||
|
|
@ -13,7 +14,8 @@ export function Chat() {
|
|||
});
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl flex flex-col gap-2">
|
||||
<div className="max-w-2xl flex flex-col gap-4">
|
||||
<ChatError error={error} />
|
||||
<ChatMessages messages={messages} />
|
||||
<ChatForm
|
||||
onMessage={sendMessage}
|
||||
|
|
|
|||
76
components/ui/alert.tsx
Normal file
76
components/ui/alert.tsx
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import type * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const alertVariants = cva(
|
||||
"group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-card text-card-foreground",
|
||||
destructive:
|
||||
"bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
function Alert({
|
||||
className,
|
||||
variant,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert"
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-title"
|
||||
className={cn(
|
||||
"font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-description"
|
||||
className={cn(
|
||||
"text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-action"
|
||||
className={cn("absolute top-2 right-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription, AlertAction };
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { Slot } from "radix-ui"
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { Slot } from "radix-ui";
|
||||
import type * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
|
|
@ -38,8 +38,8 @@ const buttonVariants = cva(
|
|||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
function Button({
|
||||
className,
|
||||
|
|
@ -49,9 +49,9 @@ function Button({
|
|||
...props
|
||||
}: React.ComponentProps<"button"> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
asChild?: boolean
|
||||
asChild?: boolean;
|
||||
}) {
|
||||
const Comp = asChild ? Slot.Root : "button"
|
||||
const Comp = asChild ? Slot.Root : "button";
|
||||
|
||||
return (
|
||||
<Comp
|
||||
|
|
@ -61,7 +61,7 @@ function Button({
|
|||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { Button, buttonVariants }
|
||||
export { Button, buttonVariants };
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react"
|
||||
import type * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
||||
return (
|
||||
|
|
@ -8,11 +8,11 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
|||
data-slot="textarea"
|
||||
className={cn(
|
||||
"flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { Textarea }
|
||||
export { Textarea };
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Elysia from "elysia";
|
||||
import { health } from "./modules/health";
|
||||
import { chat } from "./modules/chat";
|
||||
import { health } from "./modules/health";
|
||||
|
||||
export const app = new Elysia({ prefix: "/api" }).use(health).use(chat);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { openai } from "@ai-sdk/openai";
|
||||
import { convertToModelMessages, streamText } from "ai";
|
||||
import type { ChatModel } from "./model";
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
|
||||
export abstract class Chat {
|
||||
static async streamText(chat: ChatModel["chat"]) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue