fix(desktop): preserve Mermaid shapes and summary truncation

Keep auto-quoting slash labels for generated Mermaid paths while preserving slash-delimited flowchart shapes.
Restore truncation on activity summary values so the compact summary layout and tests agree.

Tested: cd desktop && bun run test -- src/pages/ActivitySettings.test.tsx src/components/chat/MermaidRenderer.test.tsx src/components/chat/MermaidRenderer.integration.test.tsx
Tested: bun run check:desktop
Confidence: high
Scope-risk: narrow
This commit is contained in:
程序员阿江(Relakkes) 2026-07-09 20:52:05 +08:00
parent 62be2e6fc3
commit 840390de50
3 changed files with 46 additions and 3 deletions

View File

@ -78,6 +78,40 @@ describe('MermaidRenderer', () => {
expect(arrow?.getAttribute('style') ?? '').not.toContain('stroke:')
})
it('quotes generated slash labels that Mermaid would parse as invalid shape syntax', async () => {
render(
<MermaidRenderer
code={[
'flowchart TD',
' D1[/api/dcl] --> D2[直接调用 dclService]',
].join('\n')}
/>,
)
await waitFor(() => {
expect(renderMock).toHaveBeenCalledWith(
expect.any(String),
[
'flowchart TD',
' D1["/api/dcl"] --> D2[直接调用 dclService]',
].join('\n'),
)
})
})
it('preserves slash-delimited Mermaid flowchart shapes', async () => {
const code = [
'flowchart TD',
' A[/Manual input/] --> B[OK]',
].join('\n')
render(<MermaidRenderer code={code} />)
await waitFor(() => {
expect(renderMock).toHaveBeenCalledWith(expect.any(String), code)
})
})
it('fits oversized diagrams inside the chat message surface', async () => {
const originalClientWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'clientWidth')
Object.defineProperty(HTMLElement.prototype, 'clientWidth', {

View File

@ -60,8 +60,17 @@ function isQuotedFlowchartLabel(label: string) {
)
}
function isSlashDelimitedFlowchartShape(label: string) {
const trimmed = label.trim()
return trimmed.length >= 2 && trimmed.startsWith('/') && trimmed.endsWith('/')
}
function shouldQuoteFlowchartLabel(label: string) {
return !isQuotedFlowchartLabel(label) && UNQUOTED_FLOWCHART_LABEL_UNSAFE.test(label)
return (
!isQuotedFlowchartLabel(label) &&
!isSlashDelimitedFlowchartShape(label) &&
UNQUOTED_FLOWCHART_LABEL_UNSAFE.test(label)
)
}
function escapeFlowchartLabel(label: string) {

View File

@ -772,9 +772,9 @@ export function ActivitySettings() {
style={{ animationDelay: `${index * 45}ms` }}
>
<div className="flex min-h-[68px] flex-col items-center justify-center gap-1.5">
<div className={`activity-summary-value max-w-full min-w-0 font-semibold leading-none tracking-tight text-[var(--color-text-primary)] tabular-nums ${
<div className={`activity-summary-value max-w-full min-w-0 truncate font-semibold leading-none tracking-tight text-[var(--color-text-primary)] tabular-nums ${
isPrimary ? 'text-[23px]' : 'text-[22px]'
}`} style={{ overflow: 'visible', whiteSpace: 'normal', textOverflow: 'clip' }}>
}`}>
{metric.value}
</div>
<div className="min-w-0 truncate text-[13px] font-medium leading-tight text-[var(--color-text-secondary)]">