fix: refine claw markdown composer display

This commit is contained in:
134 2026-07-02 21:59:12 +08:00
parent ed63eb77b7
commit 71fd765ebc
2 changed files with 128 additions and 8 deletions

View File

@ -2,7 +2,7 @@
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
import { useEffect, useRef, useState, useCallback, useMemo, type ClipboardEvent as ReactClipboardEvent } from "react";
import { useEffect, useRef, useState, useCallback, useMemo, type ClipboardEvent as ReactClipboardEvent, type MouseEvent as ReactMouseEvent } from "react";
import { useSession } from "next-auth/react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { createPortal } from "react-dom";
@ -1222,6 +1222,86 @@ function extractOpenClawActionsFromMarkdown(content: string) {
};
}
function splitOpenClawMarkdownTableCells(line: string) {
return String(line || "")
.trim()
.replace(/^\|/, "")
.replace(/\|$/, "")
.split(/(?<!\\)\|/)
.map((cell) =>
cell
.replace(/\\\|/g, "|")
.replace(/\*\*(.*?)\*\*/g, "$1")
.replace(/`([^`]+)`/g, "$1")
.replace(/\s+/g, "")
.trim()
);
}
function isOpenClawRoutingDiagnosticTable(tableLines: string[]) {
if (tableLines.length < 3) return false;
const headerCells = splitOpenClawMarkdownTableCells(tableLines[0] || "");
const hasDiagnosticHeader =
headerCells.some((cell) => cell.includes("已尝试")) &&
headerCells.some((cell) => cell.includes("结果"));
if (!hasDiagnosticHeader) return false;
const bodyText = tableLines
.slice(2)
.map((line) => splitOpenClawMarkdownTableCells(line).join(" "))
.join(" ");
return (
bodyText.includes("标准品类识别") ||
bodyText.includes("市场分析准备") ||
bodyText.includes("建议示例")
);
}
function stripOpenClawLowValueDiagnosticTables(content: string) {
const normalized = String(content || "").replace(/\r\n/g, "\n");
if (!normalized.includes("|")) return normalized.trim();
const lines = normalized.split("\n");
const keptLines: string[] = [];
let inCodeFence = false;
for (let index = 0; index < lines.length; index += 1) {
const line = lines[index] || "";
if (/^\s*```/.test(line)) {
inCodeFence = !inCodeFence;
keptLines.push(line);
continue;
}
if (
!inCodeFence &&
index < lines.length - 1 &&
isLikelyMarkdownTableRow(line) &&
isMarkdownTableSeparatorRow(lines[index + 1] || "")
) {
let endIndex = index + 2;
while (
endIndex < lines.length &&
isLikelyMarkdownTableRow(lines[endIndex] || "")
) {
endIndex += 1;
}
const tableLines = lines.slice(index, endIndex);
if (isOpenClawRoutingDiagnosticTable(tableLines)) {
index = endIndex - 1;
continue;
}
}
keptLines.push(line);
}
return keptLines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
}
type MarkdownTableBlockRange = {
start: number;
end: number;
@ -11568,6 +11648,35 @@ export function CanvasChatPanel({
(selectedSkillTag
? `请输入「${selectedSkillTag.name}」相关需求`
: "请输入你的设计需求");
const clawdEditablePresetPlaceholder =
isClawdMode && activeClawdSkillTag
? activeClawdSkillTag.placeholder?.trim() || ""
: "";
const handleClawdPresetPlaceholderDoubleClick = useCallback(
(event: ReactMouseEvent<HTMLDivElement>) => {
if (isSubmitting || !isClawdMode || !clawdEditablePresetPlaceholder) return;
const editor = event.currentTarget;
if (input.trim() || getComposerPlainTextFromElement(editor)) return;
event.preventDefault();
event.stopPropagation();
composerUserEditedAtRef.current = Date.now();
setInput(clawdEditablePresetPlaceholder);
editor.textContent = clawdEditablePresetPlaceholder;
editor.focus();
const selection = window.getSelection();
if (!selection) return;
const range = document.createRange();
range.selectNodeContents(editor);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
},
[clawdEditablePresetPlaceholder, input, isClawdMode, isSubmitting]
);
const detailProductCount = DETAIL_PRODUCT_SLOT_INDEXES.filter((index) => Boolean(detailSlots[index]?.url)).length;
const detailModelReady = DETAIL_MODEL_SLOT_INDEX >= 0 ? Boolean(detailSlots[DETAIL_MODEL_SLOT_INDEX]?.url) : false;
const detailBundleCount = DETAIL_BUNDLE_SLOT_INDEXES.filter((index) => Boolean(detailSlots[index]?.url)).length;
@ -12805,7 +12914,11 @@ export function CanvasChatPanel({
const openClawActionsResult = isClawAssistantMessage
? extractOpenClawActionsFromMarkdown(textWithoutImages)
: { markdown: textWithoutImages, blocks: [] };
const textWithoutActions = openClawActionsResult.markdown;
const textWithoutActions = isClawAssistantMessage
? stripOpenClawLowValueDiagnosticTables(
openClawActionsResult.markdown
)
: openClawActionsResult.markdown;
const openClawActionBlocks = openClawActionsResult.blocks;
const analysisSection = extractCanvasImageAnalysisSection(textWithoutActions);
const plainText = normalizeCanvasAgentText(textWithoutActions);
@ -14457,6 +14570,7 @@ export function CanvasChatPanel({
: "text-[#1f2328] empty:before:text-[#a8abb2] dark:text-zinc-100 dark:empty:before:text-zinc-500"
)}
data-placeholder={composerPlaceholder}
onDoubleClick={handleClawdPresetPlaceholderDoubleClick}
onInput={(e) => {
// 从 contenteditable 中提取纯文本(排除附件标签文本)
const target = e.target as HTMLDivElement;

View File

@ -571,10 +571,10 @@ function DownloadableMarkdownTable({
<table
ref={tableRef}
className={cn(
"w-max min-w-full table-auto border-collapse text-sm",
"[&_td]:min-w-[12rem] [&_td]:align-top [&_td]:whitespace-nowrap",
"[&_td:first-child]:min-w-[22rem] [&_th:first-child]:min-w-[22rem]",
"[&_th]:min-w-[12rem] [&_th]:whitespace-nowrap",
"min-w-full table-auto border-collapse text-sm",
"[&_td]:min-w-[10rem] [&_td]:max-w-[24rem] [&_td]:align-top [&_td]:whitespace-normal [&_td]:break-words [&_td]:[overflow-wrap:anywhere] [&_td]:[word-break:break-word]",
"[&_td:first-child]:min-w-[12rem] [&_td:first-child]:max-w-[18rem] [&_th:first-child]:min-w-[12rem] [&_th:first-child]:max-w-[18rem]",
"[&_th]:min-w-[10rem] [&_th]:max-w-[22rem] [&_th]:whitespace-normal [&_th]:break-words [&_th]:[overflow-wrap:anywhere] [&_th]:[word-break:break-word]",
className
)}
{...props}
@ -593,7 +593,10 @@ function MarkdownTableCell({
}: MarkdownTableCellProps) {
return (
<td
className={cn("min-w-[12rem] align-top !whitespace-nowrap", className)}
className={cn(
"min-w-[10rem] max-w-[24rem] align-top !whitespace-normal break-words [overflow-wrap:anywhere] [word-break:break-word]",
className
)}
{...props}
/>
);
@ -606,7 +609,10 @@ function MarkdownTableHeaderCell({
}: MarkdownTableHeaderCellProps) {
return (
<th
className={cn("min-w-[12rem] !whitespace-nowrap", className)}
className={cn(
"min-w-[10rem] max-w-[22rem] !whitespace-normal break-words [overflow-wrap:anywhere] [word-break:break-word]",
className
)}
{...props}
/>
);