fix(agents): improve diagram contrast in dark mode

This commit is contained in:
RockChinQ
2026-08-27 15:32:35 +08:00
parent e6f6789e8b
commit 7671aefed7
3 changed files with 61 additions and 16 deletions
@@ -145,7 +145,7 @@ export default function AgentCreateContent({
<Icon className="size-4" /> <Icon className="size-4" />
</span> </span>
<span className="min-w-0 space-y-1.5"> <span className="min-w-0 space-y-1.5">
<span className="block text-sm font-medium"> <span className="block text-sm font-medium text-foreground">
{option.title} {option.title}
</span> </span>
<span className="block text-sm font-normal leading-relaxed text-muted-foreground"> <span className="block text-sm font-normal leading-relaxed text-muted-foreground">
@@ -226,7 +226,7 @@ export default function AgentCreateContent({
</Card> </Card>
</div> </div>
<Card className="min-h-[600px] overflow-hidden py-0 lg:min-h-[680px]"> <Card className="min-h-[600px] overflow-hidden py-0 dark:border-white/16 lg:min-h-[680px]">
<CardContent className="flex h-full items-center p-0"> <CardContent className="flex h-full items-center p-0">
<ProcessorTypeDiagram kind={kind} /> <ProcessorTypeDiagram kind={kind} />
</CardContent> </CardContent>
@@ -13,8 +13,32 @@ function DiagramMotionStyles() {
} }
@keyframes processor-link-breathe { @keyframes processor-link-breathe {
0%, 100% { stroke-opacity: 0.28; } 0%, 100% { stroke-opacity: var(--processor-relation-opacity-min); }
50% { stroke-opacity: 0.72; } 50% { stroke-opacity: var(--processor-relation-opacity-max); }
}
.processor-diagram {
--processor-blue-node-fill: color-mix(in srgb, ${LANGBOT_BLUE} 7%, var(--card));
--processor-cyan-node-fill: color-mix(in srgb, ${LANGBOT_CYAN} 7%, var(--card));
--processor-node-stroke-opacity: 0.18;
--processor-flow-opacity: 0.34;
--processor-relation-opacity-min: 0.28;
--processor-relation-opacity-max: 0.72;
--processor-capability-fill: color-mix(in srgb, ${LANGBOT_CYAN} 4%, var(--card));
--processor-capability-stroke-opacity: 0.45;
--processor-icon-fill: color-mix(in srgb, ${LANGBOT_CYAN} 10%, var(--card));
}
.dark .processor-diagram {
--processor-blue-node-fill: color-mix(in srgb, ${LANGBOT_BLUE} 17%, var(--card));
--processor-cyan-node-fill: color-mix(in srgb, ${LANGBOT_CYAN} 15%, var(--card));
--processor-node-stroke-opacity: 0.5;
--processor-flow-opacity: 0.72;
--processor-relation-opacity-min: 0.58;
--processor-relation-opacity-max: 1;
--processor-capability-fill: color-mix(in srgb, ${LANGBOT_CYAN} 10%, var(--card));
--processor-capability-stroke-opacity: 0.78;
--processor-icon-fill: color-mix(in srgb, ${LANGBOT_CYAN} 20%, var(--card));
} }
.processor-line-flow { .processor-line-flow {
@@ -105,7 +129,7 @@ function AgentDiagram() {
viewBox="0 0 760 620" viewBox="0 0 760 620"
role="img" role="img"
aria-label={t('agents.agentDiagramTitle')} aria-label={t('agents.agentDiagramTitle')}
className="h-full w-full" className="processor-diagram h-full w-full"
data-testid="agent-diagram" data-testid="agent-diagram"
> >
<desc>{t('agents.agentDiagramDescription')}</desc> <desc>{t('agents.agentDiagramDescription')}</desc>
@@ -132,7 +156,7 @@ function AgentDiagram() {
stroke={LANGBOT_BLUE} stroke={LANGBOT_BLUE}
strokeWidth="2" strokeWidth="2"
strokeDasharray="4 11" strokeDasharray="4 11"
opacity="0.32" opacity="var(--processor-flow-opacity)"
markerEnd={`url(#${arrowId})`} markerEnd={`url(#${arrowId})`}
className="processor-line-flow" className="processor-line-flow"
data-motion="flow" data-motion="flow"
@@ -144,7 +168,9 @@ function AgentDiagram() {
width="158" width="158"
height="58" height="58"
rx="14" rx="14"
fill={`color-mix(in srgb, ${LANGBOT_BLUE} 7%, var(--card))`} fill="var(--processor-blue-node-fill)"
stroke={LANGBOT_BLUE}
strokeOpacity="var(--processor-node-stroke-opacity)"
/> />
<circle <circle
cx="91" cx="91"
@@ -201,7 +227,6 @@ function AgentDiagram() {
stroke={LANGBOT_CYAN} stroke={LANGBOT_CYAN}
strokeWidth="1.8" strokeWidth="1.8"
strokeDasharray="5 6" strokeDasharray="5 6"
opacity="0.48"
className="processor-link-breathe" className="processor-link-breathe"
data-motion="relation" data-motion="relation"
style={{ animationDelay: `${index * -0.45}s` }} style={{ animationDelay: `${index * -0.45}s` }}
@@ -212,18 +237,18 @@ function AgentDiagram() {
width="158" width="158"
height="58" height="58"
rx="14" rx="14"
fill="var(--card)" fill="var(--processor-capability-fill)"
stroke={LANGBOT_CYAN} stroke={LANGBOT_CYAN}
strokeOpacity="0.45" strokeOpacity="var(--processor-capability-stroke-opacity)"
strokeWidth="1.5" strokeWidth="1.5"
/> />
<circle <circle
cx="565" cx="565"
cy={item.y + 29} cy={item.y + 29}
r="11" r="11"
fill={`color-mix(in srgb, ${LANGBOT_CYAN} 10%, var(--card))`} fill="var(--processor-icon-fill)"
stroke={LANGBOT_CYAN} stroke={LANGBOT_CYAN}
strokeOpacity="0.5" strokeOpacity="var(--processor-capability-stroke-opacity)"
/> />
<CapabilityIcon kind={item.kind} x={565} y={item.y + 29} /> <CapabilityIcon kind={item.kind} x={565} y={item.y + 29} />
<text <text
@@ -257,7 +282,7 @@ function PipelineDiagram() {
viewBox="0 0 760 620" viewBox="0 0 760 620"
role="img" role="img"
aria-label={t('agents.pipelineDiagramTitle')} aria-label={t('agents.pipelineDiagramTitle')}
className="h-full w-full" className="processor-diagram h-full w-full"
data-testid="pipeline-diagram" data-testid="pipeline-diagram"
> >
<desc>{t('agents.pipelineDiagramDescription')}</desc> <desc>{t('agents.pipelineDiagramDescription')}</desc>
@@ -279,7 +304,7 @@ function PipelineDiagram() {
fill="none" fill="none"
stroke={LANGBOT_BLUE} stroke={LANGBOT_BLUE}
strokeWidth="2" strokeWidth="2"
opacity="0.32" opacity="var(--processor-flow-opacity)"
markerEnd={`url(#${arrowId})`} markerEnd={`url(#${arrowId})`}
/> />
<path <path
@@ -308,7 +333,19 @@ function PipelineDiagram() {
fill={ fill={
active active
? LANGBOT_BLUE ? LANGBOT_BLUE
: `color-mix(in srgb, ${index === 3 ? LANGBOT_CYAN : LANGBOT_BLUE} 7%, var(--card))` : index === 3
? 'var(--processor-cyan-node-fill)'
: 'var(--processor-blue-node-fill)'
}
stroke={
active
? LANGBOT_BLUE
: index === 3
? LANGBOT_CYAN
: LANGBOT_BLUE
}
strokeOpacity={
active ? 1 : 'var(--processor-node-stroke-opacity)'
} }
/> />
{!active && ( {!active && (
+9 -1
View File
@@ -191,6 +191,15 @@ test.describe('frontend CRUD smoke flows', () => {
).toHaveCount(3); ).toHaveCount(3);
await expect(page.getByTestId('pipeline-diagram')).toHaveCount(0); await expect(page.getByTestId('pipeline-diagram')).toHaveCount(0);
await page.evaluate(() => document.documentElement.classList.add('dark'));
await expect(page.getByTestId('agent-diagram')).toHaveCSS(
'--processor-flow-opacity',
'0.72',
);
await expect(page.getByTestId('agent-diagram')).toHaveCSS(
'--processor-node-stroke-opacity',
'0.5',
);
await page.getByRole('radio', { name: /^Pipeline/ }).click(); await page.getByRole('radio', { name: /^Pipeline/ }).click();
await expect(pipelineTypeCard).toHaveAttribute('aria-checked', 'true'); await expect(pipelineTypeCard).toHaveAttribute('aria-checked', 'true');
await expect(page.getByTestId('pipeline-diagram')).toBeVisible(); await expect(page.getByTestId('pipeline-diagram')).toBeVisible();
@@ -198,7 +207,6 @@ test.describe('frontend CRUD smoke flows', () => {
page.getByTestId('pipeline-diagram').locator('[data-motion="flow"]'), page.getByTestId('pipeline-diagram').locator('[data-motion="flow"]'),
).toHaveAttribute('data-dash-cycle', '15'); ).toHaveAttribute('data-dash-cycle', '15');
await expect(page.getByTestId('agent-diagram')).toHaveCount(0); await expect(page.getByTestId('agent-diagram')).toHaveCount(0);
await expect(page.locator('input[name="name"]')).toBeVisible(); await expect(page.locator('input[name="name"]')).toBeVisible();
await page.locator('input[name="name"]').fill('Escalation Pipeline'); await page.locator('input[name="name"]').fill('Escalation Pipeline');
await page await page