style(agents): animate processor diagram connections

This commit is contained in:
RockChinQ
2026-08-27 15:18:34 +08:00
parent e20a73c83a
commit 05fe93e108
2 changed files with 59 additions and 1 deletions
@@ -5,6 +5,36 @@ import type { AgentKind } from '@/app/infra/entities/api';
const LANGBOT_BLUE = '#2288ee';
const LANGBOT_CYAN = '#19b8c9';
function DiagramMotionStyles() {
return (
<style>{`
@keyframes processor-line-flow {
to { stroke-dashoffset: -30; }
}
@keyframes processor-link-breathe {
0%, 100% { stroke-opacity: 0.28; }
50% { stroke-opacity: 0.72; }
}
.processor-line-flow {
animation: processor-line-flow 1.8s linear infinite;
}
.processor-link-breathe {
animation: processor-link-breathe 2.8s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
.processor-line-flow,
.processor-link-breathe {
animation: none;
}
}
`}</style>
);
}
function ArrowMarker({ id }: { id: string }) {
return (
<defs>
@@ -80,6 +110,7 @@ function AgentDiagram() {
>
<desc>{t('agents.agentDiagramDescription')}</desc>
<ArrowMarker id={arrowId} />
<DiagramMotionStyles />
<rect width="760" height="620" fill="var(--card)" />
@@ -100,8 +131,12 @@ function AgentDiagram() {
fill="none"
stroke={LANGBOT_BLUE}
strokeWidth="2"
strokeDasharray="4 11"
opacity="0.32"
markerEnd={`url(#${arrowId})`}
className="processor-line-flow"
data-motion="flow"
style={{ animationDelay: `${index * -0.35}s` }}
/>
<rect
x="64"
@@ -158,7 +193,7 @@ function AgentDiagram() {
{t('agents.diagramAgentCanUse')}
</text>
{outputs.map((item) => (
{outputs.map((item, index) => (
<g key={item.label}>
<path
d={`M436 310 C468 310 474 ${item.y + 29} 538 ${item.y + 29}`}
@@ -167,6 +202,9 @@ function AgentDiagram() {
strokeWidth="1.8"
strokeDasharray="5 6"
opacity="0.48"
className="processor-link-breathe"
data-motion="relation"
style={{ animationDelay: `${index * -0.45}s` }}
/>
<rect
x="538"
@@ -224,6 +262,7 @@ function PipelineDiagram() {
>
<desc>{t('agents.pipelineDiagramDescription')}</desc>
<ArrowMarker id={arrowId} />
<DiagramMotionStyles />
<rect width="760" height="620" fill="var(--card)" />
<text
@@ -243,6 +282,16 @@ function PipelineDiagram() {
opacity="0.32"
markerEnd={`url(#${arrowId})`}
/>
<path
d="M156 310H724"
fill="none"
stroke={LANGBOT_BLUE}
strokeWidth="3"
strokeDasharray="4 14"
strokeLinecap="round"
className="processor-line-flow"
data-motion="flow"
/>
{steps.map((step, index) => {
const x = 40 + index * 142;
+9
View File
@@ -183,11 +183,20 @@ test.describe('frontend CRUD smoke flows', () => {
);
await expect(agentTypeCard).toHaveAttribute('aria-checked', 'true');
await expect(page.getByTestId('agent-diagram')).toBeVisible();
await expect(
page.getByTestId('agent-diagram').locator('[data-motion="flow"]'),
).toHaveCount(3);
await expect(
page.getByTestId('agent-diagram').locator('[data-motion="relation"]'),
).toHaveCount(3);
await expect(page.getByTestId('pipeline-diagram')).toHaveCount(0);
await page.getByRole('radio', { name: /^Pipeline/ }).click();
await expect(pipelineTypeCard).toHaveAttribute('aria-checked', 'true');
await expect(page.getByTestId('pipeline-diagram')).toBeVisible();
await expect(
page.getByTestId('pipeline-diagram').locator('[data-motion="flow"]'),
).toHaveCount(1);
await expect(page.getByTestId('agent-diagram')).toHaveCount(0);
await expect(page.locator('input[name="name"]')).toBeVisible();