fix: unify Clawd attachment picker
This commit is contained in:
parent
16f734e9c5
commit
cd8f23e4b3
@ -22,6 +22,7 @@ import { generateUUID } from "@/lib/utils";
|
||||
const requestSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
message: z.string().trim().max(8000).optional().default(""),
|
||||
displayMessage: z.string().trim().max(8000).optional(),
|
||||
userMessageId: z.string().uuid().optional(),
|
||||
metadata: z.record(z.unknown()).optional(),
|
||||
});
|
||||
@ -80,6 +81,7 @@ async function parseClawdChatRequest(
|
||||
const parsed = requestSchema.safeParse({
|
||||
id: formData.get("id"),
|
||||
message: formData.get("message") || "",
|
||||
displayMessage: formData.get("displayMessage") || undefined,
|
||||
userMessageId: formData.get("userMessageId") || undefined,
|
||||
metadata,
|
||||
});
|
||||
@ -176,7 +178,7 @@ export async function POST(request: Request) {
|
||||
}
|
||||
|
||||
const displayMessage = buildOpenClawDisplayMessage({
|
||||
message: parsed.message,
|
||||
message: parsed.displayMessage || parsed.message,
|
||||
files: spreadsheetMarkdownResult?.files || [],
|
||||
});
|
||||
const upstreamMessage = buildOpenClawMessageWithSpreadsheetMarkdown({
|
||||
|
||||
@ -25,7 +25,7 @@ import {
|
||||
type UiSurfaceSnapshot,
|
||||
} from "@/lib/ui-surface-protocol";
|
||||
import useSWR, { useSWRConfig } from "swr";
|
||||
import { Loader2, ImagePlus, X, Plus, History, LayoutGrid, ArrowRightToLine, RefreshCw, Globe, ArrowUp, Video, Sparkles, Check, ChevronDown, ChevronUp, AlertCircle, CheckCircle2, Brain, ThumbsUp, ThumbsDown, Eye, Trash2, Download, MessageCircle, Clapperboard, Bot, FileSpreadsheet } from "lucide-react";
|
||||
import { Loader2, ImagePlus, X, Plus, History, LayoutGrid, ArrowRightToLine, RefreshCw, Globe, ArrowUp, Video, Sparkles, Check, ChevronDown, ChevronUp, AlertCircle, CheckCircle2, Brain, ThumbsUp, ThumbsDown, Eye, Trash2, Download, MessageCircle, Clapperboard, Bot, FileSpreadsheet, Paperclip } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Collapsible,
|
||||
@ -361,6 +361,43 @@ function buildClawdTableDisplayText(message: string, files: File[]) {
|
||||
return `${prompt}\n\n已上传表格:\n${lines.join("\n")}`;
|
||||
}
|
||||
|
||||
function buildClawdImageDisplayText(attachments: PickedObjectAttachment[]) {
|
||||
if (attachments.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const lines = attachments.map(
|
||||
(attachment, index) =>
|
||||
`- ${normalizeChatAttachmentName(attachment.name) || `图片${index + 1}`}`
|
||||
);
|
||||
return `已添加图片:\n${lines.join("\n")}`;
|
||||
}
|
||||
|
||||
function buildClawdImageReferenceText(attachments: PickedObjectAttachment[]) {
|
||||
if (attachments.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const lines = attachments.map((attachment, index) => {
|
||||
const name = normalizeChatAttachmentName(attachment.name) || `图片${index + 1}`;
|
||||
return `- ${name}: ${attachment.url}`;
|
||||
});
|
||||
return `用户还添加了以下图片作为参考,请按图片链接理解:\n${lines.join("\n")}`;
|
||||
}
|
||||
|
||||
function getClawdDefaultPrompt(hasTables: boolean, hasImages: boolean) {
|
||||
if (hasTables && hasImages) {
|
||||
return "请分析上传的表格和图片内容";
|
||||
}
|
||||
if (hasTables) {
|
||||
return "请分析上传的表格内容";
|
||||
}
|
||||
if (hasImages) {
|
||||
return "请分析上传的图片内容";
|
||||
}
|
||||
return "请分析";
|
||||
}
|
||||
|
||||
type VideoStoryboardStreamData =
|
||||
| {
|
||||
stage: "detecting";
|
||||
@ -952,6 +989,18 @@ function CanvasTypewriterText({
|
||||
function CanvasAssistantTypingPlaceholder() {
|
||||
return (
|
||||
<div className="flex w-full justify-start">
|
||||
<style>{`
|
||||
@keyframes clawdTypingDotBounce {
|
||||
0%, 80%, 100% {
|
||||
transform: translateY(0) scale(0.92);
|
||||
opacity: 0.5;
|
||||
}
|
||||
40% {
|
||||
transform: translateY(-5px) scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
<div
|
||||
aria-label="Clawd 正在处理"
|
||||
className="inline-flex h-7 items-center gap-1 rounded-[14px] bg-[#f4f5f7] px-3 text-[#9aa3b2] dark:bg-white/8 dark:text-zinc-400"
|
||||
@ -959,10 +1008,10 @@ function CanvasAssistantTypingPlaceholder() {
|
||||
{[0, 1, 2].map((index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="h-1.5 w-1.5 animate-bounce rounded-full bg-current opacity-75"
|
||||
className="h-1.5 w-1.5 rounded-full bg-current"
|
||||
style={{
|
||||
animationDelay: `${index * 120}ms`,
|
||||
animationDuration: "0.9s",
|
||||
animation: "clawdTypingDotBounce 0.9s ease-in-out infinite",
|
||||
animationDelay: `${index * 140}ms`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
@ -1384,7 +1433,6 @@ export function CanvasChatPanel({
|
||||
const [messageViewportKey, setMessageViewportKey] = useState(0);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const clawdTableInputRef = useRef<HTMLInputElement>(null);
|
||||
const videoSlotInputRefs = useRef<Array<HTMLInputElement | null>>([]);
|
||||
const agentSlotInputRefs = useRef<Array<HTMLInputElement | null>>([]);
|
||||
const detailSlotInputRefs = useRef<Array<HTMLInputElement | null>>([]);
|
||||
@ -8794,20 +8842,30 @@ export function CanvasChatPanel({
|
||||
.trim();
|
||||
const tableFilesSnapshot = [...clawdTableFiles];
|
||||
const hasTableFiles = tableFilesSnapshot.length > 0;
|
||||
const attachmentsSnapshot = [...attachments];
|
||||
const imageAttachmentsSnapshot = attachmentsSnapshot
|
||||
.map((attachment) => sanitizeChatAttachment(attachment))
|
||||
.filter((attachment): attachment is PickedObjectAttachment => Boolean(attachment));
|
||||
const hasImageAttachments = imageAttachmentsSnapshot.length > 0;
|
||||
|
||||
if (!textContent && !hasTableFiles) {
|
||||
return;
|
||||
}
|
||||
if (attachments.length > 0) {
|
||||
toast.error("Clawd 当前仅支持文本和表格文件");
|
||||
if (!textContent && !hasTableFiles && !hasImageAttachments) {
|
||||
return;
|
||||
}
|
||||
|
||||
const requestMessage = textContent || "请分析上传的表格内容";
|
||||
const optimisticMessageText = buildClawdTableDisplayText(
|
||||
requestMessage,
|
||||
tableFilesSnapshot
|
||||
const baseRequestMessage =
|
||||
textContent || getClawdDefaultPrompt(hasTableFiles, hasImageAttachments);
|
||||
const imageReferenceText = buildClawdImageReferenceText(
|
||||
imageAttachmentsSnapshot
|
||||
);
|
||||
const requestMessage = [baseRequestMessage, imageReferenceText]
|
||||
.filter(Boolean)
|
||||
.join("\n\n");
|
||||
const optimisticMessageText = [
|
||||
buildClawdTableDisplayText(baseRequestMessage, tableFilesSnapshot),
|
||||
buildClawdImageDisplayText(imageAttachmentsSnapshot),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n\n");
|
||||
const sendSignature = JSON.stringify({
|
||||
chatId: activeChatId,
|
||||
mode: "clawd",
|
||||
@ -8815,6 +8873,10 @@ export function CanvasChatPanel({
|
||||
tableFiles: tableFilesSnapshot
|
||||
.map((file) => `${file.name}:${file.size}:${file.lastModified}`)
|
||||
.sort(),
|
||||
imageAttachments: imageAttachmentsSnapshot
|
||||
.map((attachment) => normalizeMediaUrl(attachment.url))
|
||||
.filter(Boolean)
|
||||
.sort(),
|
||||
});
|
||||
|
||||
if (shouldBlockDuplicateSend(activeChatId, sendSignature)) {
|
||||
@ -8865,11 +8927,13 @@ export function CanvasChatPanel({
|
||||
formData.append("id", rawChatId);
|
||||
formData.append("userMessageId", userMessageId);
|
||||
formData.append("message", requestMessage);
|
||||
formData.append("displayMessage", optimisticMessageText);
|
||||
formData.append(
|
||||
"metadata",
|
||||
JSON.stringify({
|
||||
composerMode: "clawd",
|
||||
activeChatId,
|
||||
imageAttachmentCount: imageAttachmentsSnapshot.length || undefined,
|
||||
})
|
||||
);
|
||||
tableFilesSnapshot.forEach((file) => {
|
||||
@ -8911,6 +8975,7 @@ export function CanvasChatPanel({
|
||||
);
|
||||
toast.error(error instanceof Error ? error.message : "Clawd 请求失败");
|
||||
setClawdTableFiles(tableFilesSnapshot);
|
||||
setAttachments(attachmentsSnapshot);
|
||||
const currentEditorText = (editableDiv?.textContent || "")
|
||||
.replace(/\u200b/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
@ -8919,6 +8984,16 @@ export function CanvasChatPanel({
|
||||
setInput(textContent);
|
||||
if (editableDiv) {
|
||||
editableDiv.textContent = textContent;
|
||||
window.setTimeout(() => {
|
||||
attachmentsSnapshot.forEach((attachment, index) => {
|
||||
if (!attachment.url) return;
|
||||
insertImageTagInEditor(
|
||||
attachment.url,
|
||||
normalizeChatAttachmentName(attachment.name),
|
||||
index
|
||||
);
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@ -9227,6 +9302,7 @@ export function CanvasChatPanel({
|
||||
selectedAgentPreset,
|
||||
selectedModelId,
|
||||
sendMessage,
|
||||
insertImageTagInEditor,
|
||||
clearSelection,
|
||||
clearLatestGeneratedImageHighlights,
|
||||
buildAutoContext,
|
||||
@ -9526,6 +9602,30 @@ export function CanvasChatPanel({
|
||||
const handleFileSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = e.target.files;
|
||||
if (!files) return;
|
||||
let selectedFiles = Array.from(files);
|
||||
|
||||
if (isClawdMode) {
|
||||
const tableFiles = selectedFiles.filter((file) => isClawdTableFile(file));
|
||||
const imageFiles = selectedFiles.filter((file) =>
|
||||
file.type.startsWith("image/")
|
||||
);
|
||||
const unsupportedFiles = selectedFiles.filter(
|
||||
(file) => !isClawdTableFile(file) && !file.type.startsWith("image/")
|
||||
);
|
||||
|
||||
unsupportedFiles.forEach((file) => {
|
||||
toast.error(`${file.name} 格式不支持,请上传图片或 .xlsx/.csv/.tsv`);
|
||||
});
|
||||
if (tableFiles.length > 0) {
|
||||
addClawdTableFiles(tableFiles);
|
||||
}
|
||||
|
||||
selectedFiles = imageFiles;
|
||||
if (selectedFiles.length === 0) {
|
||||
e.target.value = "";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let firstImageUrl: string | null = null;
|
||||
// 记录本次上传开始时的节点数量,用于计算新图片位置
|
||||
@ -9537,7 +9637,7 @@ export function CanvasChatPanel({
|
||||
const currentSlots = Array.from({ length: slotCount }, (_, index) => attachments[index] ?? null);
|
||||
let handledCount = 0;
|
||||
|
||||
for (const file of Array.from(files)) {
|
||||
for (const file of selectedFiles) {
|
||||
const targetSlotIndex = currentSlots.findIndex((slot) => !slot);
|
||||
if (targetSlotIndex < 0) break;
|
||||
|
||||
@ -9575,7 +9675,7 @@ export function CanvasChatPanel({
|
||||
: `已添加 ${handledCount} 张图片到风格化槽位`
|
||||
);
|
||||
}
|
||||
if (handledCount < files.length) {
|
||||
if (handledCount < selectedFiles.length) {
|
||||
toast.info("风格化需要的图片槽位已满,剩余图片未添加");
|
||||
}
|
||||
|
||||
@ -9584,7 +9684,7 @@ export function CanvasChatPanel({
|
||||
return;
|
||||
}
|
||||
|
||||
for (const file of Array.from(files)) {
|
||||
for (const file of selectedFiles) {
|
||||
if (!file.type.startsWith('image/')) {
|
||||
toast.error('请选择图片文件');
|
||||
continue;
|
||||
@ -9678,62 +9778,57 @@ export function CanvasChatPanel({
|
||||
e.target.value = '';
|
||||
};
|
||||
|
||||
const handleClawdTableFileSelect = useCallback(
|
||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const selectedFiles = Array.from(event.currentTarget.files || []);
|
||||
event.currentTarget.value = "";
|
||||
if (selectedFiles.length === 0) return;
|
||||
function addClawdTableFiles(selectedFiles: File[]) {
|
||||
if (selectedFiles.length === 0) return;
|
||||
|
||||
const remainingSlots = CLAWD_TABLE_MAX_FILES - clawdTableFiles.length;
|
||||
if (remainingSlots <= 0) {
|
||||
toast.error(`一次最多上传 ${CLAWD_TABLE_MAX_FILES} 个表格`);
|
||||
return;
|
||||
const remainingSlots = CLAWD_TABLE_MAX_FILES - clawdTableFiles.length;
|
||||
if (remainingSlots <= 0) {
|
||||
toast.error(`一次最多上传 ${CLAWD_TABLE_MAX_FILES} 个表格`);
|
||||
return;
|
||||
}
|
||||
|
||||
const acceptedFiles: File[] = [];
|
||||
for (const file of selectedFiles) {
|
||||
if (!isClawdTableFile(file)) {
|
||||
toast.error(`${file.name} 格式不支持,请上传 .xlsx、.csv 或 .tsv`);
|
||||
continue;
|
||||
}
|
||||
if (file.size <= 0) {
|
||||
toast.error(`${file.name} 是空文件`);
|
||||
continue;
|
||||
}
|
||||
if (file.size > CLAWD_TABLE_MAX_BYTES) {
|
||||
toast.error(`${file.name} 超过 10MB,请先拆分或压缩`);
|
||||
continue;
|
||||
}
|
||||
acceptedFiles.push(file);
|
||||
}
|
||||
|
||||
const acceptedFiles: File[] = [];
|
||||
for (const file of selectedFiles) {
|
||||
if (!isClawdTableFile(file)) {
|
||||
toast.error(`${file.name} 格式不支持,请上传 .xlsx、.csv 或 .tsv`);
|
||||
if (acceptedFiles.length === 0) return;
|
||||
|
||||
setClawdTableFiles((currentFiles) => {
|
||||
const existingSignatures = new Set(
|
||||
currentFiles.map(
|
||||
(file) => `${file.name}:${file.size}:${file.lastModified}`
|
||||
)
|
||||
);
|
||||
const nextFiles = [...currentFiles];
|
||||
for (const file of acceptedFiles) {
|
||||
if (nextFiles.length >= CLAWD_TABLE_MAX_FILES) break;
|
||||
const signature = `${file.name}:${file.size}:${file.lastModified}`;
|
||||
if (existingSignatures.has(signature)) {
|
||||
continue;
|
||||
}
|
||||
if (file.size <= 0) {
|
||||
toast.error(`${file.name} 是空文件`);
|
||||
continue;
|
||||
}
|
||||
if (file.size > CLAWD_TABLE_MAX_BYTES) {
|
||||
toast.error(`${file.name} 超过 10MB,请先拆分或压缩`);
|
||||
continue;
|
||||
}
|
||||
acceptedFiles.push(file);
|
||||
existingSignatures.add(signature);
|
||||
nextFiles.push(file);
|
||||
}
|
||||
return nextFiles;
|
||||
});
|
||||
|
||||
if (acceptedFiles.length === 0) return;
|
||||
|
||||
setClawdTableFiles((currentFiles) => {
|
||||
const existingSignatures = new Set(
|
||||
currentFiles.map(
|
||||
(file) => `${file.name}:${file.size}:${file.lastModified}`
|
||||
)
|
||||
);
|
||||
const nextFiles = [...currentFiles];
|
||||
for (const file of acceptedFiles) {
|
||||
if (nextFiles.length >= CLAWD_TABLE_MAX_FILES) break;
|
||||
const signature = `${file.name}:${file.size}:${file.lastModified}`;
|
||||
if (existingSignatures.has(signature)) {
|
||||
continue;
|
||||
}
|
||||
existingSignatures.add(signature);
|
||||
nextFiles.push(file);
|
||||
}
|
||||
return nextFiles;
|
||||
});
|
||||
|
||||
if (acceptedFiles.length > remainingSlots) {
|
||||
toast.info(`一次最多上传 ${CLAWD_TABLE_MAX_FILES} 个表格,已自动保留前 ${remainingSlots} 个`);
|
||||
}
|
||||
},
|
||||
[clawdTableFiles.length]
|
||||
);
|
||||
if (acceptedFiles.length > remainingSlots) {
|
||||
toast.info(`一次最多上传 ${CLAWD_TABLE_MAX_FILES} 个表格,已自动保留前 ${remainingSlots} 个`);
|
||||
}
|
||||
}
|
||||
|
||||
const removeClawdTableFile = useCallback((index: number) => {
|
||||
setClawdTableFiles((currentFiles) =>
|
||||
@ -9815,7 +9910,7 @@ export function CanvasChatPanel({
|
||||
const hasEnoughAgentUploads = !hasAgentUploadSlots || attachments.length >= agentRequiredUploadCount;
|
||||
const composerPlaceholder = isWorkspacePresentation
|
||||
? isClawdMode
|
||||
? "输入要交给 Clawd 的问题,可上传表格"
|
||||
? "输入要交给 Clawd 的问题,可上传图片或表格"
|
||||
: isDetailGenerationMode
|
||||
? "可选补充:风格、情绪氛围、禁用元素、期望生成张数(如:生成8张)"
|
||||
: isVideoWorkspace
|
||||
@ -9826,7 +9921,7 @@ export function CanvasChatPanel({
|
||||
? "可选补充本次生图要求,例如:高级感、暖灰色、近景特写、生成3张。"
|
||||
: "可选补充本次生成要求,例如:更高级、更生活化、暖调灯光。"
|
||||
: isClawdMode
|
||||
? "输入要交给 Clawd 的问题,可上传表格"
|
||||
? "输入要交给 Clawd 的问题,可上传图片或表格"
|
||||
: isDetailGenerationMode
|
||||
? "可选补充:风格、情绪氛围、禁用元素、期望生成张数(如:生成8张)"
|
||||
: isVideoWorkspace
|
||||
@ -9851,7 +9946,7 @@ export function CanvasChatPanel({
|
||||
? Math.min(100, Math.round((detailProgress.current / detailProgress.total) * 100))
|
||||
: 0;
|
||||
const canSend = isClawdMode
|
||||
? Boolean(input.trim() || clawdTableFiles.length > 0) && !isClawdSubmitting
|
||||
? Boolean(input.trim() || attachments.length > 0 || clawdTableFiles.length > 0) && !isClawdSubmitting
|
||||
: isDetailGenerationMode
|
||||
? detailValidationIssues.length === 0 && !isDetailGenerating
|
||||
: hasInputOrAttachments &&
|
||||
@ -12521,6 +12616,8 @@ export function CanvasChatPanel({
|
||||
? "min-h-[38px] text-[19px] leading-7 sm:text-[22px] sm:leading-8"
|
||||
: activeComposerMode === "image"
|
||||
? "min-h-[44px] text-[16px] leading-7 sm:text-[16px] sm:leading-7"
|
||||
: isClawdMode
|
||||
? "min-h-[64px] text-[12px] leading-5 sm:text-[12px] sm:leading-5"
|
||||
: "min-h-[64px] text-[16px] leading-7 sm:text-[17px] sm:leading-7",
|
||||
isVideoWorkspace
|
||||
? "text-foreground empty:before:text-muted-foreground/70"
|
||||
@ -12736,19 +12833,11 @@ export function CanvasChatPanel({
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
accept={isClawdMode ? `image/*,${CLAWD_TABLE_ACCEPT}` : "image/*"}
|
||||
multiple
|
||||
className="hidden"
|
||||
onChange={handleFileSelect}
|
||||
/>
|
||||
<input
|
||||
ref={clawdTableInputRef}
|
||||
type="file"
|
||||
accept={CLAWD_TABLE_ACCEPT}
|
||||
multiple
|
||||
className="hidden"
|
||||
onChange={handleClawdTableFileSelect}
|
||||
/>
|
||||
{videoFrameSlots.map((_, index) => (
|
||||
<input
|
||||
key={`canvas-video-slot-input-${index}`}
|
||||
@ -12777,7 +12866,7 @@ export function CanvasChatPanel({
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{!isWorkspacePresentation && !isDetailGenerationMode && activeComposerMode !== "image" && activeComposerMode !== "clawd" && (!isVideoModel || showVideoAttachmentButton) && (
|
||||
{!isWorkspacePresentation && !isDetailGenerationMode && activeComposerMode !== "image" && (isClawdMode || !isVideoModel || showVideoAttachmentButton) && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
@ -12789,30 +12878,19 @@ export function CanvasChatPanel({
|
||||
)}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={isSubmitting}
|
||||
title={getToolbarItem("chatAttach").label}
|
||||
title={isClawdMode ? "上传附件" : getToolbarItem("chatAttach").label}
|
||||
>
|
||||
<CanvasToolbarIcon
|
||||
className="h-4.5 w-4.5"
|
||||
fallback={Plus}
|
||||
item={getToolbarItem("chatAttach")}
|
||||
/>
|
||||
{isClawdMode ? (
|
||||
<Paperclip className="h-4.5 w-4.5" />
|
||||
) : (
|
||||
<CanvasToolbarIcon
|
||||
className="h-4.5 w-4.5"
|
||||
fallback={Plus}
|
||||
item={getToolbarItem("chatAttach")}
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
{!isWorkspacePresentation && !isDetailGenerationMode && isClawdMode ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 shrink-0 rounded-full border border-transparent bg-[#eeeeef] text-[#20242a] transition-colors hover:bg-[#e7e7e8] hover:text-[#111418] disabled:opacity-50 dark:bg-white/10 dark:text-zinc-100 dark:hover:bg-white/14 dark:hover:text-white"
|
||||
onClick={() => clawdTableInputRef.current?.click()}
|
||||
disabled={
|
||||
isSubmitting ||
|
||||
clawdTableFiles.length >= CLAWD_TABLE_MAX_FILES
|
||||
}
|
||||
title="上传表格"
|
||||
>
|
||||
<FileSpreadsheet className="h-4.5 w-4.5" />
|
||||
</Button>
|
||||
) : null}
|
||||
{!isWorkspacePresentation ? (
|
||||
<DropdownMenu
|
||||
open={isComposerModeMenuOpen}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user