How the Egonex-AI Dashboard Implements Persona-Adaptive UI: A Technical Deep Dive
The Egonex-AI dashboard implements persona-adaptive UI through a centralized Zustand state machine that supports three distinct user personas—non-technical, junior, and experienced—dynamically adjusting graph complexity, export granularity, and interface density based on the selected expertise level.
The Egonex-AI/Understand-Anything repository uses a persona-driven architecture to tailor its visualization dashboard to different technical audiences. Rather than maintaining separate applications for beginners versus advanced users, the codebase employs a single adaptive interface that filters data and adjusts layouts through a shared state management layer.
Core Architecture: The Persona State Machine
The foundation of the persona-adaptive UI lives in understand-anything-plugin/packages/dashboard/src/store.ts, where a Zustand store defines the allowed persona values and manages state transitions.
Persona Type Definition
The system recognizes three strict persona types enforced through TypeScript:
export type Persona = "non-technical" | "junior" | "experienced";
The store initializes with a default persona of "junior" (line 10), positioning the dashboard in "learn mode" for new users while allowing escalation to advanced views.
State Management and Cache Invalidation
The setPersona action (lines 34-41) performs more than a simple state update. It clears multiple layout caches to ensure the UI redraws correctly when node visibility changes:
setPersona: (persona) =>
set({
persona,
// Reset caches that depend on node-type visibility
containerLayoutCache: new Map(),
containerSizeMemory: new Map(),
expandedContainers: new Set(),
pendingFocusContainer: null,
}),
This cache invalidation prevents stale positioning data from leaking between personas, as each mode displays different subsets of graph nodes.
The Persona Selector Component
User-facing persona switching occurs through the PersonaSelector component in src/components/PersonaSelector.tsx. This widget renders three toggle buttons mapping to the defined personas, each with localized labels and tooltips.
export default function PersonaSelector() {
const persona = useDashboardStore((s) => s.persona);
const setPersona = useDashboardStore((s) => s.setPersona);
const { t } = useI18n();
const personas = [
{ id: "non-technical", label: t.personaSelector.overview, description: t.personaSelector.overviewDesc },
{ id: "junior", label: t.personaSelector.learn, description: t.personaSelector.learnDesc },
{ id: "experienced", label: t.personaSelector.deepDive, description: t.personaSelector.deepDiveDesc },
];
return (
<div className="flex items-center gap-1 bg-elevated rounded-lg p-0.5">
{personas.map((p) => (
<button
key={p.id}
onClick={() => setPersona(p.id)}
title={p.description}
className={`px-2.5 py-1 rounded text-[11px] font-medium transition-colors ${
persona === p.id ? "bg-accent/20 text-accent" : "text-text-muted hover:text-text-secondary hover:bg-surface"
}`}
>
{p.label}
</button>
))}
</div>
);
}
The selector embeds across multiple layout surfaces including App.tsx, the mobile drawer (MobileDrawer.tsx), and various sidebars, ensuring persistent access regardless of viewport size.
Persona-Driven Rendering Logic
Components throughout the dashboard consume the persona state to conditionally render interface elements and filter data. This creates distinct user experiences without duplicating component logic.
Graph Filtering by Expertise Level
In src/components/GraphView.tsx (line 439), the non-technical persona triggers aggressive node filtering to hide implementation details:
const persona = useDashboardStore((s) => s.persona);
...
if (persona === "non-technical" && subFileTypes.has(n.type)) return false;
This conditional removes functions, classes, and other sub-file entities from the visualization, presenting high-level architectural overviews rather than implementation minutiae.
Export Behavior Adaptation
The ExportMenu component (src/components/ExportMenu.tsx, lines 186-190) applies similar filtering when generating exportable data. When a non-technical user exports the graph, the system strips function and class nodes to produce stakeholder-friendly documentation.
Layout Mode Detection
Higher-level layout components reference the persona to determine global UI behavior. In src/App.tsx (lines 232-393), the dashboard computes an isLearnMode boolean true for junior personas or when guided tours are active. Meanwhile, MobileLayout.tsx (line 66) specifically treats the junior persona as a distinct learning mode for mobile interfaces.
Programmatic Persona Control
While the toggle UI serves standard interactions, the store architecture supports imperative persona changes for automated workflows or deep-linking:
import { useDashboardStore } from "./store";
// Immediately switch to experienced view
useDashboardStore.getState().setPersona("experienced");
Conditional rendering based on persona follows standard React patterns with Zustand selectors:
const persona = useDashboardStore((s) => s.persona);
return (
<>
{persona === "non-technical" && <NonTechnicalHelpBanner />}
{persona !== "non-technical" && <AdvancedControls />}
</>
);
Manual node filtering outside the standard graph view requires checking both the node type and current persona:
const nodes = useDashboardStore((s) => s.graph?.nodes ?? []);
const persona = useDashboardStore((s) => s.persona);
const visibleNodes = nodes.filter((n) => {
if (persona === "non-technical" && ["function", "class"].includes(n.type)) {
return false;
}
return true;
});
Summary
- Centralized state: The
Personatype andsetPersonaaction instore.tsprovide the single source of truth for user expertise levels. - Three distinct modes: Non-technical (overview), junior (learn), and experienced (deep dive) personas control feature visibility throughout the application.
- Automatic cache clearing: Switching personas triggers cache invalidation for layout and container states, ensuring accurate visual positioning.
- Component-level filtering: GraphView and ExportMenu filter nodes differently per persona, while App.tsx and MobileLayout.tsx adjust macro-level UI modes.
- Persistent controls: The PersonaSelector component surfaces these options consistently across desktop and mobile layouts.
Frequently Asked Questions
What are the three user personas supported by the Egonex-AI dashboard?
The dashboard supports non-technical, junior, and experienced personas. The non-technical mode hides implementation details like functions and classes, junior mode enables learning features and guided tours, and experienced mode reveals the full graph complexity and advanced export options.
How does the dashboard prevent layout bugs when switching personas?
The setPersona action in store.ts clears four critical caches (containerLayoutCache, containerSizeMemory, expandedContainers, and pendingFocusContainer) during the state transition. This ensures layout calculations recompute based on the visible node set for the new persona rather than recycling positions from the previous view.
Can applications switch personas programmatically without user interaction?
Yes. The Zustand store exposes setPersona directly on the store state, allowing imperative updates via useDashboardStore.getState().setPersona("experienced"). This enables deep-linking to specific personas or automated context switching based on user authentication or project requirements.
Which components implement persona-specific filtering logic?
The primary filtering occurs in GraphView.tsx for visual node hiding and ExportMenu.tsx for data export filtering. Additionally, App.tsx uses the persona to determine isLearnMode status, while MobileLayout.tsx applies persona-aware responsive behaviors for mobile learning modes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →