updates on the agent ui and all those things

This commit is contained in:
2026-09-25 11:19:13 +05:30
parent 9c50de1142
commit beff5a1cbe
2 changed files with 101 additions and 12 deletions

View File

@@ -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)} />

View File

@@ -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>