fix: improve Clawd pending and markdown tables

This commit is contained in:
134 2026-06-23 22:59:03 +08:00
parent cd0037dc47
commit 67dbeeab1a
2 changed files with 45 additions and 8 deletions

View File

@ -898,10 +898,30 @@ function CanvasTypewriterText({
return <div className={className}>{displayText}</div>; return <div className={className}>{displayText}</div>;
} }
function CanvasAssistantTypingPlaceholder() {
return (
<div className="flex w-full justify-start">
<div
aria-label="Clawd 正在处理"
className="inline-flex h-10 items-center gap-1.5 rounded-2xl bg-[#f4f5f7] px-4 text-[#9aa3b2] dark:bg-white/8 dark:text-zinc-400"
>
{[0, 1, 2].map((index) => (
<span
key={index}
className="h-2 w-2 rounded-full bg-current opacity-70 animate-pulse"
style={{ animationDelay: `${index * 140}ms` }}
/>
))}
</div>
</div>
);
}
function normalizeCanvasAgentText(value: string): string { function normalizeCanvasAgentText(value: string): string {
return String(value || "") return String(value || "")
.replace(/\r\n/g, "\n") .replace(/\r\n/g, "\n")
.replace(/\n{3,}/g, "\n\n") .replace(/[ \t\u00a0]+\n/g, "\n")
.replace(/\n[ \t\u00a0]*\n(?:[ \t\u00a0]*\n)+/g, "\n\n")
.trim(); .trim();
} }
@ -914,6 +934,7 @@ function hasCanvasRenderableMarkdown(value: string): boolean {
/\*\*[^*]+\*\*/.test(text) || /\*\*[^*]+\*\*/.test(text) ||
/(^|\n)\s*[-*+]\s+/.test(text) || /(^|\n)\s*[-*+]\s+/.test(text) ||
/(^|\n)\s*\d+\.\s+/.test(text) || /(^|\n)\s*\d+\.\s+/.test(text) ||
/(^|\n)\s*\|.+\|\s*\n\s*\|[-:\s|]+\|/.test(text) ||
/\[[^\]]+\]\([^)]+\)/.test(text) /\[[^\]]+\]\([^)]+\)/.test(text)
); );
} }
@ -5849,9 +5870,9 @@ export function CanvasChatPanel({
// 滚动到底部 // 滚动到底部
useEffect(() => { useEffect(() => {
if (messages.length === 0) return; if (messages.length === 0 && !isClawdSubmitting) return;
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]); }, [isClawdSubmitting, messages]);
// 标记是否已经为当前画布设置过标题(避免重复调用) // 标记是否已经为当前画布设置过标题(避免重复调用)
const hasSetCanvasTitle = useRef(false); const hasSetCanvasTitle = useRef(false);
@ -10785,7 +10806,7 @@ export function CanvasChatPanel({
text={analysisSection.analysis} text={analysisSection.analysis}
typingKey={analysisTypingKey} typingKey={analysisTypingKey}
enabled={shouldAnimateAnalysis} enabled={shouldAnimateAnalysis}
className="whitespace-pre-wrap break-words [overflow-wrap:anywhere]" className="break-words [overflow-wrap:anywhere]"
renderMarkdown renderMarkdown
onActiveChange={handleTypewriterActiveChange} onActiveChange={handleTypewriterActiveChange}
onComplete={handleTypewriterComplete} onComplete={handleTypewriterComplete}
@ -10967,6 +10988,10 @@ export function CanvasChatPanel({
/> />
)} )}
{isClawdMode && isClawdSubmitting ? (
<CanvasAssistantTypingPlaceholder />
) : null}
{isLoading && !shouldRenderImageLoading && ( {isLoading && !shouldRenderImageLoading && (
<div className="flex w-full justify-start"> <div className="flex w-full justify-start">
<div className="flex items-center gap-2 text-[#8e97a5]"> <div className="flex items-center gap-2 text-[#8e97a5]">

View File

@ -80,6 +80,14 @@ function downloadTextFile(filename: string, content: string, type: string) {
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
function normalizeMarkdownContent(value: string) {
return String(value || "")
.replace(/\r\n/g, "\n")
.replace(/[ \t\u00a0]+\n/g, "\n")
.replace(/\n[ \t\u00a0]*\n(?:[ \t\u00a0]*\n)+/g, "\n\n")
.trim();
}
function splitMarkdownImages(content: string) { function splitMarkdownImages(content: string) {
const segments: Array< const segments: Array<
| { type: "text"; value: string } | { type: "text"; value: string }
@ -275,7 +283,7 @@ function DownloadableMarkdownTable({
}; };
return ( return (
<div className="my-4 max-w-full overflow-hidden rounded-xl border border-border bg-background/70"> <div className="my-3 max-w-full overflow-hidden rounded-xl border border-border bg-background/70 whitespace-normal">
<div className="flex items-center justify-end border-b border-border/70 bg-muted/40 px-2 py-1.5"> <div className="flex items-center justify-end border-b border-border/70 bg-muted/40 px-2 py-1.5">
<button <button
type="button" type="button"
@ -320,8 +328,9 @@ export const Response = ({
...props ...props
}: ResponseProps) => { }: ResponseProps) => {
const streamdownClassName = cn( const streamdownClassName = cn(
"w-full min-w-0 [&>*:first-child]:mt-0 [&>*:last-child]:mb-0 [&_code]:whitespace-pre-wrap [&_code]:break-words [&_pre]:max-w-full [&_pre]:overflow-x-auto", "w-full min-w-0 [&>*:first-child]:mt-0 [&>*:last-child]:mb-0 [&_code]:break-words [&_pre]:max-w-full [&_pre]:overflow-x-auto",
className className,
"whitespace-normal [&_code]:whitespace-pre-wrap"
); );
const processingKey = `${isProcessingEnhance}-${isProcessingRmbg}-${isProcessingExtend}-${isProcessingAngleEdit}-${isProcessingWatermarkRemove}`; const processingKey = `${isProcessingEnhance}-${isProcessingRmbg}-${isProcessingExtend}-${isProcessingAngleEdit}-${isProcessingWatermarkRemove}`;
@ -425,7 +434,10 @@ export const Response = ({
); );
}; };
const rawText = typeof props.children === "string" ? props.children : null; const rawText =
typeof props.children === "string"
? normalizeMarkdownContent(props.children)
: null;
if (rawText) { if (rawText) {
const analysisSection = collapsibleImageAnalysis const analysisSection = collapsibleImageAnalysis
? extractImageAnalysisSection(rawText) ? extractImageAnalysisSection(rawText)