Files
chatgpt-api-web/src/components/ui/chat/chat-bubble.tsx
黑墨水鱼 79d5ded088
All checks were successful
Build static content / build (push) Successful in 8m5s
Update pages.yml
Update pages.yml

Update pages.yml

Update pages.yml

Update pages.yml

Update pages.yml

fix message bubble overflow on small screen

refactor ListAPI component to simplify click handler for template selection

chat store title

fix: adjust MessageBubble component to allow full-width rendering on medium screens

feat: enhance ConversationTitle component with full-width styling and click handler for title retrieval

feat: add abort signal support for fetch and stream response handling in Chat component

feat: add usage tracking and timestamps to ChatStoreMessage structure

pwa

feat: update theme colors to black in manifest and Vite config

display standlone

feat: add smooth scrolling to messages in Chatbox component

feat: add handleNewChatStore function to App context and integrate in Chatbox for new chat functionality

feat: refactor MessageBubble component to use ChatBubble and improve structure

refactor(MessageBubble): move TTSPlay component into message area and reorganize action buttons

ui(navbar): improve cost breakdown clarity and add accumulated cost tracking

Revert "feat: refactor MessageBubble component to use ChatBubble and improve structure"

This reverts commit d16984c7da896ee0d047dca0be3f4ad1703a5d2c.

display string mesasge trimed

fix typo

fix scroll after send

fix(MessageBubble): trim whitespace from reasoning content display

feat(sidebar): optimize mobile performance with CSS transitions

- Refactored mobile sidebar implementation to use direct CSS transforms instead of Sheet component
- Added static overlay mask with opacity transition for mobile experience
- Implemented custom close button with X icon to replace Sheet's default
- Improved z-index handling for sidebar elements (chat-bubble z-index reduced to 30)
- Preserved DOM structure during sidebar toggle to prevent unnecessary remounting
- Unified PC/mobile behavior using CSS animation rather than dynamic mounting
- Removed dependency on radix-ui Dialog components for mobile sidebar

fix scroll

fix sidebar style on mobile

apply default render to markdown

fix(ChatMessageList): set width to 100vw for full viewport coverage

fix small overflow

fix: overflow on PC

break model name anywhere

fix language
2025-02-07 18:56:13 +08:00

201 lines
4.5 KiB
TypeScript

import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
import MessageLoading from "./message-loading";
import { Button, ButtonProps } from "../button";
// ChatBubble
const chatBubbleVariant = cva("flex gap-2 items-end relative group", {
variants: {
variant: {
received: "self-start",
sent: "self-end flex-row-reverse",
},
layout: {
default: "",
ai: "max-w-full w-full items-center",
},
},
defaultVariants: {
variant: "received",
layout: "default",
},
});
interface ChatBubbleProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof chatBubbleVariant> {}
const ChatBubble = React.forwardRef<HTMLDivElement, ChatBubbleProps>(
({ className, variant, layout, children, ...props }, ref) => (
<div
className={cn(
chatBubbleVariant({ variant, layout, className }),
"relative group"
)}
ref={ref}
{...props}
>
{React.Children.map(children, (child) =>
React.isValidElement(child) && typeof child.type !== "string"
? React.cloneElement(child, {
variant,
layout,
} as React.ComponentProps<typeof child.type>)
: child
)}
</div>
)
);
ChatBubble.displayName = "ChatBubble";
// ChatBubbleAvatar
interface ChatBubbleAvatarProps {
src?: string;
fallback?: string;
className?: string;
}
const ChatBubbleAvatar: React.FC<ChatBubbleAvatarProps> = ({
src,
fallback,
className,
}) => (
<Avatar className={className}>
<AvatarImage src={src} alt="Avatar" />
<AvatarFallback>{fallback}</AvatarFallback>
</Avatar>
);
// ChatBubbleMessage
const chatBubbleMessageVariants = cva("p-4", {
variants: {
variant: {
received:
"bg-secondary text-secondary-foreground rounded-r-lg rounded-tl-lg",
sent: "bg-primary text-primary-foreground rounded-l-lg rounded-tr-lg",
},
layout: {
default: "",
ai: "border-t w-full rounded-none bg-transparent",
},
},
defaultVariants: {
variant: "received",
layout: "default",
},
});
interface ChatBubbleMessageProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof chatBubbleMessageVariants> {
isLoading?: boolean;
}
const ChatBubbleMessage = React.forwardRef<
HTMLDivElement,
ChatBubbleMessageProps
>(
(
{ className, variant, layout, isLoading = false, children, ...props },
ref
) => (
<div
className={cn(
chatBubbleMessageVariants({ variant, layout, className }),
"break-words max-w-full whitespace-pre-wrap"
)}
ref={ref}
{...props}
>
{isLoading ? (
<>
{children}
<div className="flex items-center space-x-2">
<MessageLoading />
</div>
</>
) : (
children
)}
</div>
)
);
ChatBubbleMessage.displayName = "ChatBubbleMessage";
// ChatBubbleTimestamp
interface ChatBubbleTimestampProps
extends React.HTMLAttributes<HTMLDivElement> {
timestamp: string;
}
const ChatBubbleTimestamp: React.FC<ChatBubbleTimestampProps> = ({
timestamp,
className,
...props
}) => (
<div className={cn("text-xs mt-2 text-right", className)} {...props}>
{timestamp}
</div>
);
// ChatBubbleAction
type ChatBubbleActionProps = ButtonProps & {
icon: React.ReactNode;
};
const ChatBubbleAction: React.FC<ChatBubbleActionProps> = ({
icon,
onClick,
className,
variant = "ghost",
size = "icon",
...props
}) => (
<Button
variant={variant}
size={size}
className={className}
onClick={onClick}
{...props}
>
{icon}
</Button>
);
interface ChatBubbleActionWrapperProps
extends React.HTMLAttributes<HTMLDivElement> {
variant?: "sent" | "received";
className?: string;
}
const ChatBubbleActionWrapper = React.forwardRef<
HTMLDivElement,
ChatBubbleActionWrapperProps
>(({ variant, className, children, ...props }, ref) => (
<div
ref={ref}
className={cn(
"absolute z-30 translate-y-full flex opacity-0 group-hover:opacity-100 transition-opacity duration-200",
variant === "sent" ? "flex-row-reverse" : "",
className
)}
{...props}
>
{children}
</div>
));
ChatBubbleActionWrapper.displayName = "ChatBubbleActionWrapper";
export {
ChatBubble,
ChatBubbleAvatar,
ChatBubbleMessage,
ChatBubbleTimestamp,
chatBubbleVariant,
chatBubbleMessageVariants,
ChatBubbleAction,
ChatBubbleActionWrapper,
};