fix: use actual lucide Footprints icon for trace badges
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m2s
CI / docker (push) Successful in 51s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 36s

Replace hand-drawn teardrop paths (looked like plants) with the real
lucide Footprints SVG paths. Configured = bare teal icon, data captured
= white icon in solid teal circle with staggered pulse rings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-29 13:49:36 +02:00
parent 3d71345181
commit a4fcb8810f

View File

@@ -1,9 +1,10 @@
import React from 'react';
import type { NodeConfig } from './types';
const BADGE_SIZE = 18;
const BADGE_GAP = 4;
const TRACE_ENABLED_COLOR = '#1A7F8E'; // teal — tracing configured
const TRACE_DATA_COLOR = '#3D7C47'; // green — data captured
const TRACE_COLOR = '#1A7F8E'; // teal
const TAP_COLOR = '#7C3AED'; // purple
interface ConfigBadgeProps {
nodeWidth: number;
@@ -12,45 +13,78 @@ interface ConfigBadgeProps {
hasTraceData?: boolean;
}
export function ConfigBadge({ nodeWidth, config, hasTraceData }: ConfigBadgeProps) {
const badges: { icon: 'tap' | 'trace'; color: string }[] = [];
if (config.tapExpression) badges.push({ icon: 'tap', color: '#7C3AED' });
// Show trace badge if tracing is enabled OR data was captured
if (config.traceEnabled || hasTraceData) {
badges.push({ icon: 'trace', color: hasTraceData ? TRACE_DATA_COLOR : TRACE_ENABLED_COLOR });
}
if (badges.length === 0) return null;
let xOffset = nodeWidth;
/** Lucide Footprints icon (24x24 viewBox) scaled to fit badge */
function FootprintsIcon({ color, size }: { color: string; size: number }) {
const scale = size / 24;
return (
<g className="config-badges">
{badges.map((badge, i) => {
xOffset -= BADGE_SIZE + (i > 0 ? BADGE_GAP : 0);
return (
<g key={badge.icon} transform={`translate(${xOffset}, ${-BADGE_SIZE - 4})`}>
<circle
cx={BADGE_SIZE / 2}
cy={BADGE_SIZE / 2}
r={BADGE_SIZE / 2}
fill={badge.color}
/>
{badge.icon === 'tap' ? (
/* Droplets icon (simplified) */
<g transform="translate(4, 4)" stroke="white" strokeWidth={1.4} fill="none" strokeLinecap="round" strokeLinejoin="round">
<path d="M5 1 C5 1 2 4.5 2 6.5a3 3 0 006 0C8 4.5 5 1 5 1z" />
</g>
) : (
/* Footprints icon (simplified) */
<g transform="translate(3.5, 3)" stroke="white" strokeWidth={1.3} fill="none" strokeLinecap="round" strokeLinejoin="round">
<path d="M4 1c0 0-1.5 2-1.5 3.5a1.5 1.5 0 003 0C5.5 3 4 1 4 1z" />
<path d="M7.5 4c0 0-1 1.5-1 2.5a1 1 0 002 0c0-1-1-2.5-1-2.5z" />
<line x1="3.5" y1="7" x2="3" y2="9.5" />
<line x1="7" y1="8" x2="7.5" y2="10" />
</g>
)}
</g>
);
})}
<g transform={`scale(${scale})`} stroke={color} strokeWidth={2} fill="none" strokeLinecap="round" strokeLinejoin="round">
<path d="M4 16v-2.38C4 11.5 2.97 10.5 3 8c.03-2.72 1.49-6 4.5-6C9.37 2 10 3.8 10 5.5c0 3.11-2 5.66-2 8.68V16a2 2 0 1 1-4 0Z" />
<path d="M20 20v-2.38c0-2.12 1.03-3.12 1-5.62-.03-2.72-1.49-6-4.5-6C14.63 6 14 7.8 14 9.5c0 3.11 2 5.66 2 8.68V20a2 2 0 1 0 4 0Z" />
<path d="M16 17h4" />
<path d="M4 13h4" />
</g>
);
}
export function ConfigBadge({ nodeWidth, config, hasTraceData }: ConfigBadgeProps) {
const showTap = !!config.tapExpression;
const showTrace = config.traceEnabled || hasTraceData;
if (!showTap && !showTrace) return null;
const badges: React.ReactNode[] = [];
let xOffset = nodeWidth;
// Tap badge: always solid purple circle with white droplet icon
if (showTap) {
xOffset -= BADGE_SIZE;
const cx = BADGE_SIZE / 2;
const cy = BADGE_SIZE / 2;
badges.push(
<g key="tap" transform={`translate(${xOffset}, ${-BADGE_SIZE - 4})`}>
<circle cx={cx} cy={cy} r={BADGE_SIZE / 2} fill={TAP_COLOR} />
<g transform="translate(4, 4)" stroke="white" strokeWidth={1.4} fill="none" strokeLinecap="round" strokeLinejoin="round">
<path d="M5 1 C5 1 2 4.5 2 6.5a3 3 0 006 0C8 4.5 5 1 5 1z" />
</g>
</g>,
);
}
// Trace badge
if (showTrace) {
xOffset -= BADGE_SIZE + (showTap ? BADGE_GAP : 0);
const cx = BADGE_SIZE / 2;
const cy = BADGE_SIZE / 2;
const r = BADGE_SIZE / 2;
if (hasTraceData) {
// Data captured: solid teal circle, white icon, pulsing rings
badges.push(
<g key="trace" transform={`translate(${xOffset}, ${-BADGE_SIZE - 4})`}>
<circle cx={cx} cy={cy} r={r} fill="none" stroke={TRACE_COLOR} strokeWidth={2} opacity={0.5}>
<animate attributeName="r" values={`${r};${r + 8}`} dur="1.5s" repeatCount="indefinite" />
<animate attributeName="opacity" values="0.5;0" dur="1.5s" repeatCount="indefinite" />
</circle>
<circle cx={cx} cy={cy} r={r} fill="none" stroke={TRACE_COLOR} strokeWidth={2} opacity={0.5}>
<animate attributeName="r" values={`${r};${r + 8}`} dur="1.5s" begin="0.75s" repeatCount="indefinite" />
<animate attributeName="opacity" values="0.5;0" dur="1.5s" begin="0.75s" repeatCount="indefinite" />
</circle>
<circle cx={cx} cy={cy} r={r} fill={TRACE_COLOR} />
<g transform={`translate(${(BADGE_SIZE - 14) / 2}, ${(BADGE_SIZE - 14) / 2})`}>
<FootprintsIcon color="white" size={14} />
</g>
</g>,
);
} else {
// Configured only: bare teal icon, no circle background
badges.push(
<g key="trace" transform={`translate(${xOffset}, ${-BADGE_SIZE - 4})`}>
<g transform={`translate(${(BADGE_SIZE - 14) / 2}, ${(BADGE_SIZE - 14) / 2})`}>
<FootprintsIcon color={TRACE_COLOR} size={14} />
</g>
</g>,
);
}
}
return <g className="config-badges">{badges}</g>;
}