updates on the agent ui and all those things
This commit is contained in:
@@ -271,6 +271,8 @@ export default function AdminLayout() {
|
||||
|
||||
const displayName = user?.displayname || user?.authname || user?.name || 'Operator';
|
||||
const isHome = location.pathname === '/doormile/home' || location.pathname === '/doormile' || location.pathname === '/';
|
||||
const isAgentsPage = location.pathname === '/doormile/agents' || location.pathname.startsWith('/doormile/agents');
|
||||
const hideAssistant = isHome || isAgentsPage;
|
||||
|
||||
return (
|
||||
/* Transparent so the ambient canvas painted behind the app reads through.
|
||||
@@ -519,7 +521,7 @@ export default function AdminLayout() {
|
||||
page runs the assistant as the page itself, so neither the docked
|
||||
panel nor its launcher belongs there. Splitting these two rules apart
|
||||
is how you end up with a button that opens nothing. */}
|
||||
{!isHome && (
|
||||
{!hideAssistant && (
|
||||
<>
|
||||
<AILauncher isOpen={assistantOpen} onToggle={() => toggleAssistant((open) => !open)} />
|
||||
<AIPanel isOpen={assistantOpen} onClose={() => toggleAssistant(false)} />
|
||||
|
||||
@@ -89,6 +89,31 @@ const AGENT_NETWORK_STRUCTURE = {
|
||||
],
|
||||
};
|
||||
|
||||
// ==============================|| COLOR BLENDING HELPER ||============================== //
|
||||
|
||||
/**
|
||||
* Smoothly blends between two hex colors based on progress (0 to 1).
|
||||
* Used for dynamic photon color transitions as events route through JARVIS.
|
||||
*/
|
||||
function interpolateColor(color1, color2, factor) {
|
||||
try {
|
||||
const c1 = color1.replace('#', '');
|
||||
const c2 = color2.replace('#', '');
|
||||
const r1 = parseInt(c1.substring(0, 2), 16);
|
||||
const g1 = parseInt(c1.substring(2, 4), 16);
|
||||
const b1 = parseInt(c1.substring(4, 6), 16);
|
||||
const r2 = parseInt(c2.substring(0, 2), 16);
|
||||
const g2 = parseInt(c2.substring(2, 4), 16);
|
||||
const b2 = parseInt(c2.substring(4, 6), 16);
|
||||
const r = Math.round(r1 + factor * (r2 - r1));
|
||||
const g = Math.round(g1 + factor * (g2 - g1));
|
||||
const b = Math.round(b1 + factor * (b2 - b1));
|
||||
return `rgb(${r}, ${g}, ${b})`;
|
||||
} catch {
|
||||
return color1;
|
||||
}
|
||||
}
|
||||
|
||||
// ==============================|| MINIMAL AGENT NODE ||============================== //
|
||||
|
||||
/**
|
||||
@@ -235,10 +260,23 @@ export default function AgentFlowVisualizer({
|
||||
const [particle, setParticle] = useState({
|
||||
x: 0,
|
||||
y: 0,
|
||||
color: '#059669',
|
||||
visible: false,
|
||||
trail: [],
|
||||
});
|
||||
|
||||
// Dynamic Impact Ripples (Tactile arrival shockwaves)
|
||||
const [impactRipples, setImpactRipples] = useState([]);
|
||||
|
||||
const triggerImpact = (x, y, color = currentScenario.color) => {
|
||||
if (!x || !y) return;
|
||||
const id = `${Date.now()}-${Math.random()}`;
|
||||
setImpactRipples((prev) => [...prev.slice(-4), { id, x, y, color }]);
|
||||
setTimeout(() => {
|
||||
setImpactRipples((prev) => prev.filter((r) => r.id !== id));
|
||||
}, 850);
|
||||
};
|
||||
|
||||
// Update SVG node coordinates dynamically on resize and scenario change
|
||||
const updateCoordinates = () => {
|
||||
if (!canvasRef.current) return;
|
||||
@@ -344,6 +382,15 @@ export default function AgentFlowVisualizer({
|
||||
const currentDist = progress * totalLen;
|
||||
const pt = pathElement.getPointAtLength(currentDist);
|
||||
|
||||
// Dynamic Photon Color: Shifts towards JARVIS royal indigo (#6366f1) on Path 2,
|
||||
// and transitions from JARVIS into target agent's color on Path 3
|
||||
let particleColor = currentScenario.color;
|
||||
if (segmentName === 'path2') {
|
||||
particleColor = interpolateColor(currentScenario.color, '#6366f1', progress);
|
||||
} else if (segmentName === 'path3') {
|
||||
particleColor = interpolateColor('#6366f1', currentScenario.color, progress);
|
||||
}
|
||||
|
||||
// Calculate trailing comet micro-beads
|
||||
const t1 = pathElement.getPointAtLength(Math.max(0, currentDist - 12));
|
||||
const t2 = pathElement.getPointAtLength(Math.max(0, currentDist - 22));
|
||||
@@ -352,6 +399,7 @@ export default function AgentFlowVisualizer({
|
||||
setParticle({
|
||||
x: pt.x,
|
||||
y: pt.y,
|
||||
color: particleColor,
|
||||
visible: true,
|
||||
trail: [t1, t2, t3],
|
||||
});
|
||||
@@ -373,7 +421,8 @@ export default function AgentFlowVisualizer({
|
||||
setIsProcessing(false);
|
||||
timeoutId = setTimeout(() => {
|
||||
runParticleAlongPath(path1Ref.current, 'path1', 850, () => {
|
||||
// Arrives at Stage 2
|
||||
// Arrives at Stage 2 Event Bus -> tactile arrival ripple
|
||||
triggerImpact(p2.x, p2.y, currentScenario.color);
|
||||
setCurrentEventFeed({
|
||||
text: currentScenario.step1Human,
|
||||
tech: currentScenario.step1Tech,
|
||||
@@ -387,18 +436,24 @@ export default function AgentFlowVisualizer({
|
||||
// Step 2: Information delivered, particle travels from Stage 2 to JARVIS
|
||||
timeoutId = setTimeout(() => {
|
||||
runParticleAlongPath(path2Ref.current, 'path2', 750, () => {
|
||||
// Arrives at JARVIS Orchestrator Apex -> royal indigo routing shockwave!
|
||||
triggerImpact(pJarvis.x, pJarvis.y, '#6366f1');
|
||||
setStep(3);
|
||||
});
|
||||
}, 400);
|
||||
} else if (step === 3) {
|
||||
// Step 3: JARVIS routes to Target Agent, Agent pulses & processes, then routes to Result
|
||||
runParticleAlongPath(path3Ref.current, 'path3', 600, () => {
|
||||
// Arrives at Target Agent -> agent activation ripple!
|
||||
triggerImpact(pAgent.x, pAgent.y, currentScenario.color);
|
||||
setIsProcessing(true);
|
||||
// Agent processes briefly for 800ms
|
||||
timeoutId = setTimeout(() => {
|
||||
setIsProcessing(false);
|
||||
// Particle travels from Agent to Result
|
||||
runParticleAlongPath(path4Ref.current, 'path4', 850, () => {
|
||||
// Arrives at Stage 4 Result -> successful resolution ripple!
|
||||
triggerImpact(p4.x, p4.y, currentScenario.color);
|
||||
if (onActivityLogged) {
|
||||
onActivityLogged({
|
||||
agent: currentScenario.agentName,
|
||||
@@ -471,7 +526,10 @@ export default function AgentFlowVisualizer({
|
||||
</div>
|
||||
|
||||
{/* Clean Scenario Pill Buttons */}
|
||||
<div className="flex items-center gap-1.5">
|
||||
{/* flex-wrap, because four scenario pills plus Pause measure 387px and a
|
||||
phone viewport is 375 — without it the row pushed the whole page 56px
|
||||
wide and every screen on the site scrolled sideways, not just this one. */}
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
{SCENARIOS.map((scen, idx) => {
|
||||
const isSelected = scenarioIndex === idx;
|
||||
return (
|
||||
@@ -610,6 +668,32 @@ export default function AgentFlowVisualizer({
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Dynamic Tactile Arrival Ripples (Shockwaves on node contact) */}
|
||||
{impactRipples.map((ripple) => (
|
||||
<g key={ripple.id} className="pointer-events-none">
|
||||
{/* Inner High-Energy Ring */}
|
||||
<motion.circle
|
||||
cx={ripple.x}
|
||||
cy={ripple.y}
|
||||
initial={{ r: 3, opacity: 0.85, strokeWidth: 2.5 }}
|
||||
animate={{ r: 24, opacity: 0, strokeWidth: 0.5 }}
|
||||
transition={{ duration: 0.6, ease: 'easeOut' }}
|
||||
fill="none"
|
||||
stroke={ripple.color}
|
||||
/>
|
||||
{/* Outer Shockwave Propagation Wave */}
|
||||
<motion.circle
|
||||
cx={ripple.x}
|
||||
cy={ripple.y}
|
||||
initial={{ r: 1, opacity: 0.5, strokeWidth: 1.5 }}
|
||||
animate={{ r: 42, opacity: 0, strokeWidth: 0.5 }}
|
||||
transition={{ duration: 0.8, ease: 'easeOut' }}
|
||||
fill="none"
|
||||
stroke={ripple.color}
|
||||
/>
|
||||
</g>
|
||||
))}
|
||||
|
||||
{/* The Moving Event Particle with Fading Comet Trail */}
|
||||
{particle.visible && !reduce && (
|
||||
<g className="transition-opacity duration-150">
|
||||
@@ -619,7 +703,7 @@ export default function AgentFlowVisualizer({
|
||||
cx={particle.trail[2].x}
|
||||
cy={particle.trail[2].y}
|
||||
r="1.8"
|
||||
fill={currentScenario.color}
|
||||
fill={particle.color || currentScenario.color}
|
||||
opacity="0.3"
|
||||
/>
|
||||
)}
|
||||
@@ -629,7 +713,7 @@ export default function AgentFlowVisualizer({
|
||||
cx={particle.trail[1].x}
|
||||
cy={particle.trail[1].y}
|
||||
r="2.5"
|
||||
fill={currentScenario.color}
|
||||
fill={particle.color || currentScenario.color}
|
||||
opacity="0.55"
|
||||
/>
|
||||
)}
|
||||
@@ -639,7 +723,7 @@ export default function AgentFlowVisualizer({
|
||||
cx={particle.trail[0].x}
|
||||
cy={particle.trail[0].y}
|
||||
r="3.2"
|
||||
fill={currentScenario.color}
|
||||
fill={particle.color || currentScenario.color}
|
||||
opacity="0.8"
|
||||
/>
|
||||
)}
|
||||
@@ -649,7 +733,7 @@ export default function AgentFlowVisualizer({
|
||||
cy={particle.y}
|
||||
r="4.5"
|
||||
fill="#ffffff"
|
||||
stroke={currentScenario.color}
|
||||
stroke={particle.color || currentScenario.color}
|
||||
strokeWidth="2.5"
|
||||
filter="url(#light-particle-glow)"
|
||||
/>
|
||||
@@ -664,7 +748,7 @@ export default function AgentFlowVisualizer({
|
||||
Mobile: 1-column vertical story
|
||||
(Step 4 has guaranteed reserved space and can never be pushed out!)
|
||||
═══════════════════════════════════════════════════════════════ */}
|
||||
<div className="relative z-10 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_minmax(0,1.35fr)_minmax(0,0.95fr)] gap-5 xl:gap-6 items-stretch">
|
||||
<div className="relative z-10 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_minmax(0,1.2fr)_minmax(0,1.1fr)] gap-5 xl:gap-6 items-stretch">
|
||||
|
||||
{/* ── STAGE 01: SOMETHING HAPPENS ── */}
|
||||
<div className="flex flex-col justify-between gap-3 min-w-0 box-border">
|
||||
@@ -801,8 +885,11 @@ export default function AgentFlowVisualizer({
|
||||
<div className="flex justify-center w-full">
|
||||
<MinimalAgentNode
|
||||
agent={AGENT_NETWORK_STRUCTURE.orchestrator}
|
||||
isActive={step === 3 && currentScenario.targetAgentId === 'JARVIS'}
|
||||
isDimmed={step === 3 && currentScenario.targetAgentId !== 'JARVIS'}
|
||||
isActive={
|
||||
(step === 2 && activeSegment === 'path2') ||
|
||||
(step === 3 && (activeSegment === 'path3' || currentScenario.targetAgentId === 'JARVIS'))
|
||||
}
|
||||
isDimmed={step === 3 && currentScenario.targetAgentId !== 'JARVIS' && activeSegment !== 'path3'}
|
||||
isSelected={activeWorkflowAgentId === 'JARVIS'}
|
||||
onClick={handleAgentClick}
|
||||
nodeRef={jarvisRef}
|
||||
@@ -914,10 +1001,10 @@ export default function AgentFlowVisualizer({
|
||||
<CheckCircle2 className="h-3.5 w-3.5" />
|
||||
</span>
|
||||
<div className="flex flex-col gap-0.5 min-w-0">
|
||||
<span className="text-xs font-bold text-slate-900 leading-tight uppercase tracking-wide truncate">
|
||||
<span className="text-xs font-bold text-slate-900 leading-snug break-words">
|
||||
{currentScenario.step4Human}
|
||||
</span>
|
||||
<span className="text-[10px] text-slate-500 leading-tight">
|
||||
<span className="text-[10.5px] text-slate-500 leading-normal break-words">
|
||||
{currentScenario.step4Sub}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user