feat(chat): add composer action menu

This commit is contained in:
2026-07-21 10:58:40 +08:00
parent 17e5656f53
commit 7789673fff
11 changed files with 632 additions and 62 deletions
+33 -15
View File
@@ -1,29 +1,43 @@
"use client";
import { ArrowUp } from "lucide-react";
import { ArrowUp, Plus, X } from "lucide-react";
export interface ChatSendButtonProps {
disabled: boolean;
hasContent: boolean;
isMenuOpen: boolean;
menuId: string;
onClick: () => void;
onMenuToggle: () => void;
onPointerDownSend: () => void;
}
export function ChatSendButton({
disabled,
hasContent,
isMenuOpen,
menuId,
onClick,
onMenuToggle,
onPointerDownSend,
}: ChatSendButtonProps) {
const isActive = hasContent && !disabled;
const isSendMode = hasContent;
const label = isSendMode
? "Send message"
: isMenuOpen
? "Close chat actions"
: "Open chat actions";
return (
<button
type="button"
data-analytics-ignore
data-analytics-ignore={isSendMode ? true : undefined}
data-analytics-key={isSendMode ? undefined : "chat.toggle_actions"}
className={[
"flex aspect-square size-(--chat-send-button-size,40px) shrink-0 cursor-pointer items-center justify-center rounded-full border-0 bg-(--color-button-gradient-end,#fc69df) text-white transition-[background,transform] duration-200 disabled:cursor-not-allowed disabled:opacity-40 focus-visible:bg-[linear-gradient(to_right,var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))]",
isActive
? "bg-[linear-gradient(to_right,var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))]"
: "bg-[#f8a8ce] text-[rgba(255,255,255,0.88)] shadow-none",
"flex aspect-square size-(--chat-send-button-size,42px) shrink-0 cursor-pointer items-center justify-center rounded-full border transition-[background,color,transform,box-shadow] duration-200 disabled:cursor-not-allowed disabled:opacity-40 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#f657a0]",
isSendMode
? "border-transparent bg-[linear-gradient(to_right,var(--color-button-gradient-start,#ff67e0),var(--color-button-gradient-end,#ff52a2))] text-white shadow-[0_8px_20px_rgba(246,87,160,0.3)]"
: isMenuOpen
? "border-transparent bg-[#38262d] text-white shadow-[0_8px_18px_rgba(56,38,45,0.18)]"
: "border-[rgba(104,67,80,0.09)] bg-[#f8f1f4] text-[#76505f] shadow-none",
]
.filter(Boolean)
.join(" ")}
@@ -32,17 +46,21 @@ export function ChatSendButton({
if (event.pointerType === "mouse") return;
if (disabled) return;
event.preventDefault();
if (!hasContent) return;
if (!isSendMode) return;
onPointerDownSend();
}}
onClick={onClick}
aria-label="Send message"
onClick={isSendMode ? onClick : onMenuToggle}
aria-label={label}
aria-expanded={isSendMode ? undefined : isMenuOpen}
aria-controls={isSendMode ? undefined : menuId}
>
<ArrowUp
className="size-(--icon-size-xl,24px) text-(length:--icon-size-xl,24px) leading-none"
size={24}
aria-hidden="true"
/>
{isSendMode ? (
<ArrowUp size={23} strokeWidth={2.4} aria-hidden="true" />
) : isMenuOpen ? (
<X size={22} strokeWidth={2.2} aria-hidden="true" />
) : (
<Plus size={23} strokeWidth={2.2} aria-hidden="true" />
)}
</button>
);
}