fix: rename Claw mode and type responses
This commit is contained in:
parent
d5fed4f2da
commit
fa10115043
@ -12345,11 +12345,11 @@ export function AdminSettingsClient() {
|
|||||||
<div className="flex items-center justify-between gap-4">
|
<div className="flex items-center justify-between gap-4">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<Label htmlFor="openClawChatEnabled">
|
<Label htmlFor="openClawChatEnabled">
|
||||||
无限画布 Clawd
|
无限画布 Claw
|
||||||
</Label>
|
</Label>
|
||||||
<div className="text-muted-foreground text-xs">
|
<div className="text-muted-foreground text-xs">
|
||||||
开启并配置接口后,右侧对话框模式菜单显示
|
开启并配置接口后,右侧对话框模式菜单显示
|
||||||
Clawd;Token 仅保存在服务端,不会下发到前端。
|
Claw;Token 仅保存在服务端,不会下发到前端。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
|
|||||||
@ -33,7 +33,7 @@ type ParsedClawdChatRequest = z.infer<typeof requestSchema> & {
|
|||||||
|
|
||||||
function buildChatTitle(message: string) {
|
function buildChatTitle(message: string) {
|
||||||
const normalized = message.replace(/\s+/g, " ").trim();
|
const normalized = message.replace(/\s+/g, " ").trim();
|
||||||
if (!normalized) return "Clawd 对话";
|
if (!normalized) return "Claw 对话";
|
||||||
return normalized.length > 80 ? `${normalized.slice(0, 80)}...` : normalized;
|
return normalized.length > 80 ? `${normalized.slice(0, 80)}...` : normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,11 +144,11 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
const config = await readOpenClawConfig();
|
const config = await readOpenClawConfig();
|
||||||
if (!config.enabled) {
|
if (!config.enabled) {
|
||||||
return NextResponse.json({ error: "Clawd 未启用" }, { status: 403 });
|
return NextResponse.json({ error: "Claw 未启用" }, { status: 403 });
|
||||||
}
|
}
|
||||||
if (!config.baseUrl || !config.token) {
|
if (!config.baseUrl || !config.token) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: "Clawd 接口地址或 Token 未配置" },
|
{ error: "Claw 接口地址或 Token 未配置" },
|
||||||
{ status: 500 }
|
{ status: 500 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -311,8 +311,10 @@ interface CanvasChatPanelProps {
|
|||||||
|
|
||||||
type CanvasComposerMode = "agent" | "image" | "video" | "detail" | "clawd";
|
type CanvasComposerMode = "agent" | "image" | "video" | "detail" | "clawd";
|
||||||
|
|
||||||
|
const CLAW_MODE_LABEL = "Claw";
|
||||||
|
|
||||||
function getCanvasComposerModeSessionLabel(mode: CanvasComposerMode) {
|
function getCanvasComposerModeSessionLabel(mode: CanvasComposerMode) {
|
||||||
if (mode === "clawd") return "Clawd";
|
if (mode === "clawd") return CLAW_MODE_LABEL;
|
||||||
if (mode === "video") return "视频";
|
if (mode === "video") return "视频";
|
||||||
if (mode === "detail") return "AI详情";
|
if (mode === "detail") return "AI详情";
|
||||||
if (mode === "image") return "图像";
|
if (mode === "image") return "图像";
|
||||||
@ -952,16 +954,125 @@ type CanvasTypewriterTextProps = {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
renderMarkdown?: boolean;
|
renderMarkdown?: boolean;
|
||||||
|
preserveMarkdownTables?: boolean;
|
||||||
onActiveChange?: (typingKey: string, active: boolean) => void;
|
onActiveChange?: (typingKey: string, active: boolean) => void;
|
||||||
onComplete?: (typingKey: string) => void;
|
onComplete?: (typingKey: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type MarkdownTableBlockRange = {
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isLikelyMarkdownTableRow(line: string) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (!trimmed || !trimmed.includes("|")) return false;
|
||||||
|
|
||||||
|
const cells = trimmed
|
||||||
|
.replace(/^\|/, "")
|
||||||
|
.replace(/\|$/, "")
|
||||||
|
.split("|");
|
||||||
|
return cells.length >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMarkdownTableSeparatorRow(line: string) {
|
||||||
|
if (!isLikelyMarkdownTableRow(line)) return false;
|
||||||
|
|
||||||
|
const cells = line
|
||||||
|
.trim()
|
||||||
|
.replace(/^\|/, "")
|
||||||
|
.replace(/\|$/, "")
|
||||||
|
.split("|")
|
||||||
|
.map((cell) => cell.trim().replace(/\s+/g, ""));
|
||||||
|
|
||||||
|
return cells.length >= 2 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMarkdownTableBlockRanges(content: string): MarkdownTableBlockRange[] {
|
||||||
|
if (!content.includes("|")) return [];
|
||||||
|
|
||||||
|
const rawLines = content.split("\n");
|
||||||
|
const lines = rawLines.map((line, index) => {
|
||||||
|
const hasNewline = index < rawLines.length - 1;
|
||||||
|
const text = hasNewline ? `${line}\n` : line;
|
||||||
|
return {
|
||||||
|
line: line.replace(/\r$/, ""),
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
const indexedLines = lines.map((line) => {
|
||||||
|
const start = offset;
|
||||||
|
offset += line.text.length;
|
||||||
|
return {
|
||||||
|
...line,
|
||||||
|
start,
|
||||||
|
end: offset,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const ranges: MarkdownTableBlockRange[] = [];
|
||||||
|
for (let index = 0; index < indexedLines.length - 1; index += 1) {
|
||||||
|
const header = indexedLines[index];
|
||||||
|
const separator = indexedLines[index + 1];
|
||||||
|
if (
|
||||||
|
!header ||
|
||||||
|
!separator ||
|
||||||
|
!isLikelyMarkdownTableRow(header.line) ||
|
||||||
|
!isMarkdownTableSeparatorRow(separator.line)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let endIndex = index + 2;
|
||||||
|
while (
|
||||||
|
endIndex < indexedLines.length &&
|
||||||
|
isLikelyMarkdownTableRow(indexedLines[endIndex]?.line || "")
|
||||||
|
) {
|
||||||
|
endIndex += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ranges.push({
|
||||||
|
start: header.start,
|
||||||
|
end: indexedLines[endIndex - 1]?.end || separator.end,
|
||||||
|
});
|
||||||
|
index = endIndex - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ranges;
|
||||||
|
}
|
||||||
|
|
||||||
|
function advanceTypewriterCursor(
|
||||||
|
contentLength: number,
|
||||||
|
targetCursor: number,
|
||||||
|
tableRanges: MarkdownTableBlockRange[]
|
||||||
|
) {
|
||||||
|
const clampedCursor = Math.min(contentLength, targetCursor);
|
||||||
|
for (const range of tableRanges) {
|
||||||
|
if (clampedCursor >= range.start && clampedCursor < range.end) {
|
||||||
|
return Math.min(contentLength, range.end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clampedCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CanvasTypewriterCursor() {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className="ml-0.5 inline-block h-[1.05em] w-[2px] animate-pulse rounded-full bg-current align-[-0.16em]"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function CanvasTypewriterText({
|
function CanvasTypewriterText({
|
||||||
text,
|
text,
|
||||||
typingKey,
|
typingKey,
|
||||||
enabled,
|
enabled,
|
||||||
className,
|
className,
|
||||||
renderMarkdown = false,
|
renderMarkdown = false,
|
||||||
|
preserveMarkdownTables = false,
|
||||||
onActiveChange,
|
onActiveChange,
|
||||||
onComplete,
|
onComplete,
|
||||||
}: CanvasTypewriterTextProps) {
|
}: CanvasTypewriterTextProps) {
|
||||||
@ -980,12 +1091,15 @@ function CanvasTypewriterText({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let cursor = 0;
|
let cursor = 0;
|
||||||
|
const tableRanges = preserveMarkdownTables
|
||||||
|
? getMarkdownTableBlockRanges(content)
|
||||||
|
: [];
|
||||||
activeRef.current = true;
|
activeRef.current = true;
|
||||||
onActiveChange?.(typingKey, true);
|
onActiveChange?.(typingKey, true);
|
||||||
setDisplayText("");
|
setDisplayText("");
|
||||||
|
|
||||||
const timer = window.setInterval(() => {
|
const timer = window.setInterval(() => {
|
||||||
cursor += 1;
|
cursor = advanceTypewriterCursor(content.length, cursor + 1, tableRanges);
|
||||||
if (cursor >= content.length) {
|
if (cursor >= content.length) {
|
||||||
setDisplayText(content);
|
setDisplayText(content);
|
||||||
window.clearInterval(timer);
|
window.clearInterval(timer);
|
||||||
@ -1006,13 +1120,25 @@ function CanvasTypewriterText({
|
|||||||
onActiveChange?.(typingKey, false);
|
onActiveChange?.(typingKey, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [content, enabled, onActiveChange, onComplete, typingKey]);
|
}, [content, enabled, onActiveChange, onComplete, preserveMarkdownTables, typingKey]);
|
||||||
|
|
||||||
|
const showCursor = enabled && Boolean(content) && displayText.length < content.length;
|
||||||
|
|
||||||
if (renderMarkdown) {
|
if (renderMarkdown) {
|
||||||
return <Response className={className}>{displayText}</Response>;
|
return (
|
||||||
|
<div className="w-full min-w-0">
|
||||||
|
<Response className={className}>{displayText}</Response>
|
||||||
|
{showCursor ? <CanvasTypewriterCursor /> : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className={className}>{displayText}</div>;
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
{displayText}
|
||||||
|
{showCursor ? <CanvasTypewriterCursor /> : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function CanvasAssistantTypingPlaceholder({ hint }: { hint?: string | null }) {
|
function CanvasAssistantTypingPlaceholder({ hint }: { hint?: string | null }) {
|
||||||
@ -1037,7 +1163,7 @@ function CanvasAssistantTypingPlaceholder({ hint }: { hint?: string | null }) {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div
|
<div
|
||||||
aria-label="Clawd 正在处理"
|
aria-label={`${CLAW_MODE_LABEL} 正在处理`}
|
||||||
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"
|
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"
|
||||||
>
|
>
|
||||||
{[0, 1, 2].map((index) => (
|
{[0, 1, 2].map((index) => (
|
||||||
@ -1553,6 +1679,7 @@ export function CanvasChatPanel({
|
|||||||
const [analysisCollapseState, setAnalysisCollapseState] = useState<Record<string, boolean>>({});
|
const [analysisCollapseState, setAnalysisCollapseState] = useState<Record<string, boolean>>({});
|
||||||
const [typedAssistantSections, setTypedAssistantSections] = useState<Record<string, boolean>>({});
|
const [typedAssistantSections, setTypedAssistantSections] = useState<Record<string, boolean>>({});
|
||||||
const [activeTypingSections, setActiveTypingSections] = useState<Record<string, boolean>>({});
|
const [activeTypingSections, setActiveTypingSections] = useState<Record<string, boolean>>({});
|
||||||
|
const [clawdTypewriterMessageIds, setClawdTypewriterMessageIds] = useState<Record<string, boolean>>({});
|
||||||
const [insufficientPointsState, setInsufficientPointsState] = useState<{
|
const [insufficientPointsState, setInsufficientPointsState] = useState<{
|
||||||
balance: number;
|
balance: number;
|
||||||
expectedCost: number;
|
expectedCost: number;
|
||||||
@ -3633,7 +3760,7 @@ export function CanvasChatPanel({
|
|||||||
);
|
);
|
||||||
const activeModeLabel =
|
const activeModeLabel =
|
||||||
activeComposerMode === "clawd"
|
activeComposerMode === "clawd"
|
||||||
? "Clawd"
|
? CLAW_MODE_LABEL
|
||||||
: activeComposerMode === "video"
|
: activeComposerMode === "video"
|
||||||
? "视频"
|
? "视频"
|
||||||
: activeComposerMode === "detail"
|
: activeComposerMode === "detail"
|
||||||
@ -3978,7 +4105,7 @@ export function CanvasChatPanel({
|
|||||||
hiddenPromptRef.current = null;
|
hiddenPromptRef.current = null;
|
||||||
autoContextRef.current = null;
|
autoContextRef.current = null;
|
||||||
if (mode === "clawd" && !isClawdModeEnabled) {
|
if (mode === "clawd" && !isClawdModeEnabled) {
|
||||||
toast.error("Clawd 未开启");
|
toast.error(`${CLAW_MODE_LABEL} 未开启`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mode === "agent") {
|
if (mode === "agent") {
|
||||||
@ -8991,7 +9118,7 @@ export function CanvasChatPanel({
|
|||||||
});
|
});
|
||||||
const responseData = await response.json().catch(() => null);
|
const responseData = await response.json().catch(() => null);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(responseData?.error || "Clawd 请求失败");
|
throw new Error(responseData?.error || `${CLAW_MODE_LABEL} 请求失败`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const returnedMessages = Array.isArray(responseData?.messages)
|
const returnedMessages = Array.isArray(responseData?.messages)
|
||||||
@ -8999,6 +9126,19 @@ export function CanvasChatPanel({
|
|||||||
: [];
|
: [];
|
||||||
if (returnedMessages.length > 0) {
|
if (returnedMessages.length > 0) {
|
||||||
const returnedIds = new Set(returnedMessages.map((message) => message.id));
|
const returnedIds = new Set(returnedMessages.map((message) => message.id));
|
||||||
|
const returnedAssistantIds = returnedMessages
|
||||||
|
.filter((message) => message.role === "assistant")
|
||||||
|
.map((message) => message.id)
|
||||||
|
.filter(Boolean);
|
||||||
|
if (returnedAssistantIds.length > 0) {
|
||||||
|
setClawdTypewriterMessageIds((previousIds) => {
|
||||||
|
const nextIds = { ...previousIds };
|
||||||
|
returnedAssistantIds.forEach((messageId) => {
|
||||||
|
nextIds[messageId] = true;
|
||||||
|
});
|
||||||
|
return nextIds;
|
||||||
|
});
|
||||||
|
}
|
||||||
setMessages((previousMessages) =>
|
setMessages((previousMessages) =>
|
||||||
sortCanvasMessagesByCreatedAt([
|
sortCanvasMessagesByCreatedAt([
|
||||||
...previousMessages.filter(
|
...previousMessages.filter(
|
||||||
@ -9014,11 +9154,11 @@ export function CanvasChatPanel({
|
|||||||
}
|
}
|
||||||
hasHydratedHistoryRef.current = true;
|
hasHydratedHistoryRef.current = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[CanvasChat] Clawd request failed:", error);
|
console.error("[CanvasChat] Claw request failed:", error);
|
||||||
setMessages((previousMessages) =>
|
setMessages((previousMessages) =>
|
||||||
previousMessages.filter((message) => message.id !== userMessageId)
|
previousMessages.filter((message) => message.id !== userMessageId)
|
||||||
);
|
);
|
||||||
toast.error(error instanceof Error ? error.message : "Clawd 请求失败");
|
toast.error(error instanceof Error ? error.message : `${CLAW_MODE_LABEL} 请求失败`);
|
||||||
setClawdTableFiles(tableFilesSnapshot);
|
setClawdTableFiles(tableFilesSnapshot);
|
||||||
setAttachments(attachmentsSnapshot);
|
setAttachments(attachmentsSnapshot);
|
||||||
const currentEditorText = (editableDiv?.textContent || "")
|
const currentEditorText = (editableDiv?.textContent || "")
|
||||||
@ -9956,7 +10096,7 @@ export function CanvasChatPanel({
|
|||||||
const hasEnoughAgentUploads = !hasAgentUploadSlots || attachments.length >= agentRequiredUploadCount;
|
const hasEnoughAgentUploads = !hasAgentUploadSlots || attachments.length >= agentRequiredUploadCount;
|
||||||
const composerPlaceholder = isWorkspacePresentation
|
const composerPlaceholder = isWorkspacePresentation
|
||||||
? isClawdMode
|
? isClawdMode
|
||||||
? "输入要交给 Clawd 的问题,可上传图片或表格"
|
? `输入要交给 ${CLAW_MODE_LABEL} 的问题,可上传图片或表格`
|
||||||
: isDetailGenerationMode
|
: isDetailGenerationMode
|
||||||
? "可选补充:风格、情绪氛围、禁用元素、期望生成张数(如:生成8张)"
|
? "可选补充:风格、情绪氛围、禁用元素、期望生成张数(如:生成8张)"
|
||||||
: isVideoWorkspace
|
: isVideoWorkspace
|
||||||
@ -9967,7 +10107,7 @@ export function CanvasChatPanel({
|
|||||||
? "可选补充本次生图要求,例如:高级感、暖灰色、近景特写、生成3张。"
|
? "可选补充本次生图要求,例如:高级感、暖灰色、近景特写、生成3张。"
|
||||||
: "可选补充本次生成要求,例如:更高级、更生活化、暖调灯光。"
|
: "可选补充本次生成要求,例如:更高级、更生活化、暖调灯光。"
|
||||||
: isClawdMode
|
: isClawdMode
|
||||||
? "输入要交给 Clawd 的问题,可上传图片或表格"
|
? `输入要交给 ${CLAW_MODE_LABEL} 的问题,可上传图片或表格`
|
||||||
: isDetailGenerationMode
|
: isDetailGenerationMode
|
||||||
? "可选补充:风格、情绪氛围、禁用元素、期望生成张数(如:生成8张)"
|
? "可选补充:风格、情绪氛围、禁用元素、期望生成张数(如:生成8张)"
|
||||||
: isVideoWorkspace
|
: isVideoWorkspace
|
||||||
@ -10289,7 +10429,7 @@ export function CanvasChatPanel({
|
|||||||
!shouldShowImageLoading;
|
!shouldShowImageLoading;
|
||||||
const workspaceEmptyTitle =
|
const workspaceEmptyTitle =
|
||||||
isClawdMode
|
isClawdMode
|
||||||
? "开始 Clawd 对话"
|
? `开始 ${CLAW_MODE_LABEL} 对话`
|
||||||
: activeComposerMode === "agent" && !isPresetAgentSelection
|
: activeComposerMode === "agent" && !isPresetAgentSelection
|
||||||
? "开始 Auto Agent 对话"
|
? "开始 Auto Agent 对话"
|
||||||
: hasAgentUploadSlots && attachments.length < agentRequiredUploadCount
|
: hasAgentUploadSlots && attachments.length < agentRequiredUploadCount
|
||||||
@ -10961,6 +11101,10 @@ export function CanvasChatPanel({
|
|||||||
isLoading &&
|
isLoading &&
|
||||||
!hasAssistantMedia &&
|
!hasAssistantMedia &&
|
||||||
index === renderableVisibleMessages.length - 1;
|
index === renderableVisibleMessages.length - 1;
|
||||||
|
const shouldTypeClawAssistantMessage =
|
||||||
|
message.role === "assistant" &&
|
||||||
|
!hasAssistantMedia &&
|
||||||
|
Boolean(clawdTypewriterMessageIds[message.id]);
|
||||||
|
|
||||||
const messageTimestamp = getMessageTimestamp(message);
|
const messageTimestamp = getMessageTimestamp(message);
|
||||||
const prevMessageTimestamp =
|
const prevMessageTimestamp =
|
||||||
@ -11137,16 +11281,18 @@ export function CanvasChatPanel({
|
|||||||
const analysisTypingKey = `${message.id}-${i}-analysis-typing`;
|
const analysisTypingKey = `${message.id}-${i}-analysis-typing`;
|
||||||
const plainTextTypingKey = `${message.id}-${i}-plain-typing`;
|
const plainTextTypingKey = `${message.id}-${i}-plain-typing`;
|
||||||
const shouldAnimateAnalysis =
|
const shouldAnimateAnalysis =
|
||||||
shouldTypeAssistantMessage &&
|
|
||||||
Boolean(analysisSection?.analysis) &&
|
Boolean(analysisSection?.analysis) &&
|
||||||
!hasCanvasRenderableMarkdown(analysisSection?.analysis || "") &&
|
!typedAssistantSections[analysisTypingKey] &&
|
||||||
!typedAssistantSections[analysisTypingKey];
|
((shouldTypeAssistantMessage &&
|
||||||
|
!hasCanvasRenderableMarkdown(analysisSection?.analysis || "")) ||
|
||||||
|
shouldTypeClawAssistantMessage);
|
||||||
const shouldAnimatePlainText =
|
const shouldAnimatePlainText =
|
||||||
shouldTypeAssistantMessage &&
|
|
||||||
!analysisSection &&
|
!analysisSection &&
|
||||||
Boolean(plainText) &&
|
Boolean(plainText) &&
|
||||||
!hasCanvasRenderableMarkdown(plainText) &&
|
!typedAssistantSections[plainTextTypingKey] &&
|
||||||
!typedAssistantSections[plainTextTypingKey];
|
((shouldTypeAssistantMessage &&
|
||||||
|
!hasCanvasRenderableMarkdown(plainText)) ||
|
||||||
|
shouldTypeClawAssistantMessage);
|
||||||
const shouldShowDoneSummary =
|
const shouldShowDoneSummary =
|
||||||
!isDetailGenerationMode &&
|
!isDetailGenerationMode &&
|
||||||
imageUrls.length > 0 &&
|
imageUrls.length > 0 &&
|
||||||
@ -11216,6 +11362,7 @@ export function CanvasChatPanel({
|
|||||||
enabled={shouldAnimateAnalysis}
|
enabled={shouldAnimateAnalysis}
|
||||||
className="break-words [overflow-wrap:anywhere]"
|
className="break-words [overflow-wrap:anywhere]"
|
||||||
renderMarkdown
|
renderMarkdown
|
||||||
|
preserveMarkdownTables={shouldTypeClawAssistantMessage}
|
||||||
onActiveChange={handleTypewriterActiveChange}
|
onActiveChange={handleTypewriterActiveChange}
|
||||||
onComplete={handleTypewriterComplete}
|
onComplete={handleTypewriterComplete}
|
||||||
/>
|
/>
|
||||||
@ -11232,6 +11379,7 @@ export function CanvasChatPanel({
|
|||||||
enabled={shouldAnimatePlainText}
|
enabled={shouldAnimatePlainText}
|
||||||
className="break-words [overflow-wrap:anywhere]"
|
className="break-words [overflow-wrap:anywhere]"
|
||||||
renderMarkdown
|
renderMarkdown
|
||||||
|
preserveMarkdownTables={shouldTypeClawAssistantMessage}
|
||||||
onActiveChange={handleTypewriterActiveChange}
|
onActiveChange={handleTypewriterActiveChange}
|
||||||
onComplete={handleTypewriterComplete}
|
onComplete={handleTypewriterComplete}
|
||||||
/>
|
/>
|
||||||
@ -13023,7 +13171,7 @@ export function CanvasChatPanel({
|
|||||||
className={getComposerModeMenuItemClass("clawd")}
|
className={getComposerModeMenuItemClass("clawd")}
|
||||||
>
|
>
|
||||||
<MessageCircle className="h-5 w-5" />
|
<MessageCircle className="h-5 w-5" />
|
||||||
<span className="flex-1">Clawd</span>
|
<span className="flex-1">{CLAW_MODE_LABEL}</span>
|
||||||
{activeComposerMode === "clawd" ? (
|
{activeComposerMode === "clawd" ? (
|
||||||
<Check className={cn("h-4 w-4", isVideoWorkspace ? "text-primary" : "text-[#1f2937]")} />
|
<Check className={cn("h-4 w-4", isVideoWorkspace ? "text-primary" : "text-[#1f2937]")} />
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@ -336,7 +336,7 @@ export async function spreadsheetFileToOpenClawMarkdown(file: File) {
|
|||||||
|
|
||||||
const extension = getSpreadsheetExtension(file.name);
|
const extension = getSpreadsheetExtension(file.name);
|
||||||
if (!SUPPORTED_EXTENSIONS.has(extension)) {
|
if (!SUPPORTED_EXTENSIONS.has(extension)) {
|
||||||
throw new Error("Clawd 目前支持 .xlsx、.csv、.tsv 表格文件");
|
throw new Error("Claw 目前支持 .xlsx、.csv、.tsv 表格文件");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extension === "csv") {
|
if (extension === "csv") {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user