Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions understand-anything-plugin/packages/dashboard/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ function Dashboard({ accessToken }: { accessToken: string }) {
const togglePathFinder = useDashboardStore((s) => s.togglePathFinder);
const nodeTypeFilters = useDashboardStore((s) => s.nodeTypeFilters);
const toggleNodeTypeFilter = useDashboardStore((s) => s.toggleNodeTypeFilter);
const detailLevel = useDashboardStore((s) => s.detailLevel);
const setDetailLevel = useDashboardStore((s) => s.setDetailLevel);
const showFunctionsInClassView = useDashboardStore((s) => s.showFunctionsInClassView);
const toggleShowFunctionsInClassView = useDashboardStore((s) => s.toggleShowFunctionsInClassView);
const [loadError, setLoadError] = useState<string | null>(null);
const [graphIssues, setGraphIssues] = useState<GraphIssue[]>([]);
const [showKeyboardHelp, setShowKeyboardHelp] = useState(false);
Expand Down Expand Up @@ -443,6 +447,52 @@ function Dashboard({ accessToken }: { accessToken: string }) {
<div className="flex-1 min-w-0 overflow-x-auto scrollbar-hide">
<div className="flex items-center gap-4 w-max">
<DiffToggle />
{/* Detail level: file view (architecture) / class view (code structure) */}
{!isKnowledgeGraph && viewMode !== "domain" && (
<>
<div className="w-px h-5 bg-border-subtle" />
<div className="flex items-center bg-elevated rounded-lg p-0.5">
<button
type="button"
onClick={() => setDetailLevel("file")}
title="Files only — architecture-level dependencies (fast)"
className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${
detailLevel === "file"
? "bg-accent/20 text-accent"
: "text-text-muted hover:text-text-secondary"
}`}
>
Files
</button>
<button
type="button"
onClick={() => setDetailLevel("class")}
title="Files + Classes — code structure with inheritance"
className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${
detailLevel === "class"
? "bg-accent/20 text-accent"
: "text-text-muted hover:text-text-secondary"
}`}
>
+Classes
</button>
</div>
{detailLevel === "class" && (
<button
type="button"
onClick={toggleShowFunctionsInClassView}
title="Toggle function nodes (may slow down rendering)"
className={`text-[10px] font-semibold uppercase tracking-wider px-2 py-1 rounded border transition-colors ${
showFunctionsInClassView
? "border-amber-500/50 bg-amber-500/10 text-amber-400"
: "border-border-medium bg-elevated text-text-muted hover:text-text-secondary"
}`}
>
fn
</button>
)}
</>
)}
<div className="flex items-center gap-1">
{(isKnowledgeGraph ? [
{ key: "knowledge" as const, label: "All", color: "var(--color-node-article)" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ function useLayerDetailTopology(): LayerDetailTopology & {
layoutStatus: "computing" | "ready";
} {
const graph = useDashboardStore((s) => s.graph);
const nodesById = useDashboardStore((s) => s.nodesById);
const activeLayerId = useDashboardStore((s) => s.activeLayerId);
const selectNode = useDashboardStore((s) => s.selectNode);
const persona = useDashboardStore((s) => s.persona);
Expand All @@ -375,6 +376,8 @@ function useLayerDetailTopology(): LayerDetailTopology & {
const focusNodeId = useDashboardStore((s) => s.focusNodeId);
const nodeTypeFilters = useDashboardStore((s) => s.nodeTypeFilters);
const drillIntoLayer = useDashboardStore((s) => s.drillIntoLayer);
const detailLevel = useDashboardStore((s) => s.detailLevel);
const showFunctionsInClassView = useDashboardStore((s) => s.showFunctionsInClassView);

const handleNodeSelect = useCallback(
(nodeId: string) => {
Expand Down Expand Up @@ -404,10 +407,20 @@ function useLayerDetailTopology(): LayerDetailTopology & {

// Expand layer membership to include sub-file nodes (function/class)
// whose parent file is in this layer. Joined via "contains" edges.
// File view: file nodes only (architecture-level dependencies).
// Class view: file nodes + class nodes, functions only when toggle is on.
const expandedLayerNodeIds = new Set(layerNodeIds);
for (const edge of graph.edges) {
if (edge.type === "contains" && layerNodeIds.has(edge.source)) {
expandedLayerNodeIds.add(edge.target);
if (detailLevel !== "file") {
for (const edge of graph.edges) {
if (edge.type === "contains" && layerNodeIds.has(edge.source)) {
const child = nodesById.get(edge.target);
if (!child) continue;
if (child.type === "class") {
expandedLayerNodeIds.add(edge.target);
} else if (child.type === "function" && showFunctionsInClassView) {
expandedLayerNodeIds.add(edge.target);
}
}
}
}

Expand Down Expand Up @@ -626,6 +639,7 @@ function useLayerDetailTopology(): LayerDetailTopology & {
};
}, [
graph,
nodesById,
activeLayerId,
persona,
diffMode,
Expand All @@ -634,6 +648,8 @@ function useLayerDetailTopology(): LayerDetailTopology & {
focusNodeId,
nodeTypeFilters,
drillIntoLayer,
detailLevel,
showFunctionsInClassView,
handleNodeSelect,
handleContainerToggle,
]);
Expand Down
31 changes: 31 additions & 0 deletions understand-anything-plugin/packages/dashboard/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type NodeType = "file" | "function" | "class" | "module" | "concept" | "c
export type Complexity = "simple" | "moderate" | "complex";
export type EdgeCategory = "structural" | "behavioral" | "data-flow" | "dependencies" | "semantic" | "infrastructure" | "domain" | "knowledge";
export type ViewMode = "structural" | "domain" | "knowledge";
export type DetailLevel = "file" | "class";

export interface FilterState {
nodeTypes: Set<NodeType>;
Expand Down Expand Up @@ -146,6 +147,13 @@ interface DashboardStore {
nodeTypeFilters: Record<NodeCategory, boolean>;
toggleNodeTypeFilter: (category: NodeCategory) => void;

// Detail level: "file" shows only file nodes (architecture view),
// "class" shows files + class nodes (code structure view) with optional function expansion.
detailLevel: DetailLevel;
setDetailLevel: (level: DetailLevel) => void;
showFunctionsInClassView: boolean;
toggleShowFunctionsInClassView: () => void;

setGraph: (graph: KnowledgeGraph) => void;
selectNode: (nodeId: string | null) => void;
navigateToNode: (nodeId: string) => void;
Expand Down Expand Up @@ -331,6 +339,29 @@ export const useDashboardStore = create<DashboardStore>()((set, get) => ({
pendingFocusContainer: null,
})),

detailLevel: "file",
setDetailLevel: (level) =>
set({
detailLevel: level,
// Detail level changes which nodes are visible; cached positions stale.
// Reset fn toggle so it doesn't resurrect when re-entering class view.
showFunctionsInClassView: false,
containerLayoutCache: new Map(),
containerSizeMemory: new Map(),
expandedContainers: new Set(),
pendingFocusContainer: null,
}),

showFunctionsInClassView: false,
toggleShowFunctionsInClassView: () =>
set((state) => ({
showFunctionsInClassView: !state.showFunctionsInClassView,
containerLayoutCache: new Map(),
containerSizeMemory: new Map(),
expandedContainers: new Set(),
pendingFocusContainer: null,
})),

setGraph: (graph) => {
const searchEngine = new SearchEngine(graph.nodes);
const query = get().searchQuery;
Expand Down