Download webapp/components/tree-visualizer/CompactTreeNavigator.tsx from gaaaaaaaaaaa/trajectory_rag: direct link, hf CLI and curl.
- Browser
- Download file 6.72 kB
-
https://huggingface.co/gaaaaaaaaaaa/trajectory_rag/resolve/main/webapp/components/tree-visualizer/CompactTreeNavigator.tsx
- Command line
-
hf download hf://gaaaaaaaaaaa/trajectory_rag/webapp/components/tree-visualizer/CompactTreeNavigator.tsx
-
curl -L -o CompactTreeNavigator.tsx https://huggingface.co/gaaaaaaaaaaa/trajectory_rag/resolve/main/webapp/components/tree-visualizer/CompactTreeNavigator.tsx
6.72 kB
| "use client"; | |
| import { ArrowDown, ArrowUp, BookOpen, ChevronLeft, ChevronRight, Crosshair, Pause, Rows3 } from "lucide-react"; | |
| import { Button } from "@/components/ui/Button"; | |
| import { getTreeNavigationState } from "@/lib/tree/navigation"; | |
| import { visibleNodeIdsForStep } from "@/lib/tree/playback"; | |
| import type { TrajectoryTree } from "@/lib/tree/types"; | |
| import { cn } from "@/lib/utils"; | |
| export type CompactNavigatorFocusMode = "node" | "floor"; | |
| interface CompactTreeNavigatorProps { | |
| tree: TrajectoryTree | null; | |
| revealCount: number; | |
| selectedNodeId: string | null; | |
| autoReadEnabled: boolean; | |
| onNavigate: (nodeId: string, mode: CompactNavigatorFocusMode) => void; | |
| onToggleAutoRead: () => void; | |
| } | |
| function shortLabel(value: string) { | |
| const trimmed = value.trim(); | |
| if (trimmed.length <= 46) return trimmed; | |
| return `${trimmed.slice(0, 43)}...`; | |
| } | |
| export function CompactTreeNavigator({ | |
| tree, | |
| revealCount, | |
| selectedNodeId, | |
| autoReadEnabled, | |
| onNavigate, | |
| onToggleAutoRead | |
| }: CompactTreeNavigatorProps) { | |
| if (!tree || tree.nodes.length === 0) return null; | |
| const visibleIds = visibleNodeIdsForStep(tree.nodes, revealCount); | |
| const navigation = getTreeNavigationState(tree.nodes, visibleIds, selectedNodeId); | |
| const current = navigation.currentNode; | |
| if (!current) return null; | |
| const currentFloorPosition = navigation.floors.indexOf(navigation.currentDepth) + 1; | |
| const totalFloors = navigation.floors.length; | |
| return ( | |
| <div className="pointer-events-none absolute bottom-5 left-1/2 z-20 w-[min(calc(100%-2rem),900px)] -translate-x-1/2"> | |
| <div className="pointer-events-auto rounded-xl border border-paper-300 bg-white/86 p-2.5 shadow-panel backdrop-blur-xl"> | |
| <div className="flex flex-col gap-2 lg:flex-row lg:items-center"> | |
| <div className="flex min-w-0 flex-1 items-center gap-2"> | |
| <div className="flex items-center gap-1 rounded-lg border border-paper-300 bg-paper-50 p-1"> | |
| <Button | |
| size="icon" | |
| variant="ghost" | |
| disabled={!navigation.previousNode} | |
| title="View previous node" | |
| onClick={() => navigation.previousNode && onNavigate(navigation.previousNode.id, "node")} | |
| > | |
| <ChevronLeft className="h-4 w-4" /> | |
| </Button> | |
| <Button | |
| size="icon" | |
| variant="ghost" | |
| disabled={!navigation.nextNode} | |
| title="View next node" | |
| onClick={() => navigation.nextNode && onNavigate(navigation.nextNode.id, "node")} | |
| > | |
| <ChevronRight className="h-4 w-4" /> | |
| </Button> | |
| </div> | |
| <div className="min-w-0 flex-1"> | |
| <div className="flex items-center gap-2"> | |
| <span className="rounded-md bg-claude-500 px-2 py-1 font-mono text-[11px] font-semibold text-white"> | |
| {current.id} | |
| </span> | |
| <span className="truncate text-xs font-semibold text-ink-700">{current.kind}</span> | |
| <span className="hidden text-xs text-ink-400 sm:inline"> | |
| {navigation.currentIndex + 1}/{navigation.orderedNodes.length} | |
| </span> | |
| </div> | |
| <div className="mt-1 truncate text-xs text-ink-500">{shortLabel(current.goal || "Untitled node")}</div> | |
| </div> | |
| </div> | |
| <div className="flex flex-wrap items-center gap-2 lg:justify-end"> | |
| <div className="flex items-center gap-1 rounded-lg border border-paper-300 bg-paper-50 p-1"> | |
| <Button | |
| size="icon" | |
| variant="ghost" | |
| disabled={!navigation.previousFloorNode} | |
| title="View previous floor" | |
| onClick={() => navigation.previousFloorNode && onNavigate(navigation.previousFloorNode.id, "floor")} | |
| > | |
| <ArrowUp className="h-4 w-4" /> | |
| </Button> | |
| <div className="flex h-9 items-center gap-1 px-2 text-xs font-medium text-ink-600"> | |
| <Rows3 className="h-4 w-4 text-claude-500" /> | |
| {currentFloorPosition}/{totalFloors} | |
| </div> | |
| <Button | |
| size="icon" | |
| variant="ghost" | |
| disabled={!navigation.nextFloorNode} | |
| title="View next floor" | |
| onClick={() => navigation.nextFloorNode && onNavigate(navigation.nextFloorNode.id, "floor")} | |
| > | |
| <ArrowDown className="h-4 w-4" /> | |
| </Button> | |
| </div> | |
| <div className="flex items-center gap-1 rounded-lg border border-paper-300 bg-white/82 p-1 shadow-sm"> | |
| <Button | |
| size="sm" | |
| variant="ghost" | |
| title="Recenter current node" | |
| onClick={() => onNavigate(current.id, "node")} | |
| > | |
| <Crosshair className="h-4 w-4" /> | |
| Focus | |
| </Button> | |
| <Button | |
| className="min-w-[104px]" | |
| size="sm" | |
| variant={autoReadEnabled ? "primary" : "secondary"} | |
| title={autoReadEnabled ? "Pause auto read" : "Auto read notes"} | |
| onClick={onToggleAutoRead} | |
| > | |
| {autoReadEnabled ? <Pause className="h-4 w-4" /> : <BookOpen className="h-4 w-4" />} | |
| {autoReadEnabled ? "Pause" : "Auto read"} | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| {navigation.sameFloorNodes.length > 1 && ( | |
| <div className="mt-2 flex items-center gap-2 border-t border-paper-200 pt-2"> | |
| <span className="shrink-0 text-[11px] font-semibold uppercase text-ink-400">Neighbors</span> | |
| <div className="flex min-w-0 flex-1 gap-1 overflow-x-auto scrollbar-thin"> | |
| {navigation.sameFloorNodes.map((node) => ( | |
| <button | |
| key={node.id} | |
| type="button" | |
| className={cn( | |
| "shrink-0 rounded-full border px-2.5 py-1 text-xs font-semibold transition", | |
| node.id === current.id | |
| ? "border-claude-300 bg-claude-50 text-claude-700" | |
| : "border-paper-300 bg-white/70 text-ink-500 hover:border-claude-300 hover:text-claude-700" | |
| )} | |
| title={node.goal || node.id} | |
| onClick={() => onNavigate(node.id, "floor")} | |
| > | |
| {node.id} | |
| </button> | |
| ))} | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| ); | |
| } | |