feat(ui): support markdown table rendering in evaluation field values
ReactTableTooltip now detects and renders markdown tables (| delimited) in addition to existing tab-delimited tables. Table content is rendered directly as an HTML table instead of showing raw markdown text. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -341,9 +341,12 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
const [renderedContent, setRenderedContent] = useState<React.ReactNode>(null);
|
||||
const textRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const isTableLike = content.includes('\t') && content.includes('\n');
|
||||
|
||||
|
||||
const isTabTable = content.includes('\t') && content.includes('\n');
|
||||
// 检测 markdown 表格:至少有 | 分隔符和分隔行 |---|
|
||||
const isMdTable = content.includes('|') && /\|[-\s:]+\|/.test(content);
|
||||
const isTableLike = isTabTable || isMdTable;
|
||||
|
||||
useEffect(() => {
|
||||
const checkTextOverflow = () => {
|
||||
const element = textRef.current;
|
||||
@@ -352,33 +355,94 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
|
||||
setShowTooltip(isTableLike || element.scrollHeight > element.clientHeight);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 预渲染内容并缓存
|
||||
if (isTableLike) {
|
||||
if (isMdTable) {
|
||||
setRenderedContent(renderMarkdownTable(content));
|
||||
} else if (isTabTable) {
|
||||
setRenderedContent(renderReactTable(content));
|
||||
} else {
|
||||
setRenderedContent(content);
|
||||
}
|
||||
|
||||
|
||||
requestAnimationFrame(checkTextOverflow);
|
||||
window.addEventListener('resize', checkTextOverflow);
|
||||
return () => {
|
||||
window.removeEventListener('resize', checkTextOverflow);
|
||||
};
|
||||
}, [content, isTableLike]);
|
||||
|
||||
// 解析表格数据
|
||||
|
||||
// 解析表格数据(tab分隔)
|
||||
const parseTableData = (text: string) => {
|
||||
const rows = text.split('\n').map(row => row.split('\t'));
|
||||
return rows;
|
||||
};
|
||||
|
||||
// 渲染React表格
|
||||
|
||||
// 解析 markdown 表格
|
||||
const parseMarkdownTable = (text: string): string[][] => {
|
||||
const lines = text.split('\n').filter(l => l.trim());
|
||||
const rows: string[][] = [];
|
||||
for (const line of lines) {
|
||||
// 跳过分隔行 |---|---|
|
||||
if (/^\s*\|[-\s:]+\|/.test(line)) continue;
|
||||
const cells = line.split('|')
|
||||
.map(c => c.trim())
|
||||
.filter((_, i, arr) => i > 0 && i < arr.length); // 去掉首尾空元素
|
||||
if (cells.length > 0) rows.push(cells);
|
||||
}
|
||||
return rows;
|
||||
};
|
||||
|
||||
// 渲染 markdown 表格
|
||||
const renderMarkdownTable = (text: string) => {
|
||||
try {
|
||||
const tableData = parseMarkdownTable(text);
|
||||
if (tableData.length === 0) return content;
|
||||
|
||||
return (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full border-collapse border border-gray-300">
|
||||
<thead>
|
||||
<tr>
|
||||
{tableData[0].map((cell, cellIndex) => (
|
||||
<th
|
||||
key={`header-${cellIndex}`}
|
||||
className="px-2 py-1 border border-gray-300 bg-gray-100 font-medium text-xs text-left whitespace-nowrap"
|
||||
>
|
||||
{cell || ' '}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{tableData.slice(1).map((row, rowIndex) => (
|
||||
<tr key={`row-${rowIndex}`}>
|
||||
{row.map((cell, cellIndex) => (
|
||||
<td
|
||||
key={`cell-${rowIndex}-${cellIndex}`}
|
||||
className="px-2 py-1 border border-gray-300 text-xs text-left"
|
||||
>
|
||||
{cell || ' '}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Markdown表格渲染错误:', error);
|
||||
return <div>{content}</div>;
|
||||
}
|
||||
};
|
||||
|
||||
// 渲染React表格(tab分隔)
|
||||
const renderReactTable = (text: string) => {
|
||||
try {
|
||||
const tableData = parseTableData(text);
|
||||
const hasHeader = tableData.length > 0;
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<table className="min-w-full border-collapse border border-gray-300">
|
||||
@@ -386,7 +450,7 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
|
||||
<thead>
|
||||
<tr>
|
||||
{tableData[0].map((cell, cellIndex) => (
|
||||
<th
|
||||
<th
|
||||
key={`header-${cellIndex}`}
|
||||
className="px-2 py-1 border border-gray-300 bg-gray-100 font-medium text-xs text-left"
|
||||
>
|
||||
@@ -400,7 +464,7 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
|
||||
{tableData.slice(1).map((row, rowIndex) => (
|
||||
<tr key={`row-${rowIndex}`}>
|
||||
{row.map((cell, cellIndex) => (
|
||||
<td
|
||||
<td
|
||||
key={`cell-${rowIndex}-${cellIndex}`}
|
||||
className="px-2 py-1 border border-gray-300 text-xs text-left"
|
||||
>
|
||||
@@ -421,10 +485,19 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
|
||||
|
||||
|
||||
|
||||
// 表格内容直接渲染表格组件,非表格内容保持原有行为
|
||||
if (isTableLike) {
|
||||
return (
|
||||
<div className="text-xs p-1 rounded cursor-text w-full text-left">
|
||||
{renderedContent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="text-xs p-1 rounded cursor-text w-full text-left">
|
||||
{showTooltip ? (
|
||||
<Tooltip
|
||||
<Tooltip
|
||||
content={renderedContent}
|
||||
placement="top"
|
||||
theme="light"
|
||||
|
||||
Reference in New Issue
Block a user