diff --git a/src/app/ontology/ontology-graph-3d.tsx b/src/app/ontology/ontology-graph-3d.tsx index 28719ae..295cae8 100644 --- a/src/app/ontology/ontology-graph-3d.tsx +++ b/src/app/ontology/ontology-graph-3d.tsx @@ -382,8 +382,8 @@ export function OntologyGraph3D({ schemas, edges, selectedId, onSelect }: Props) strokeLinecap="round" strokeLinejoin="round" > - - + + @@ -394,6 +394,7 @@ export function OntologyGraph3D({ schemas, edges, selectedId, onSelect }: Props) makeDefault dollySpeed={0.5} truckSpeed={1} + dollyToCursor /> s.searchTerm) const hasResults = useGraphStore((s) => s.nodes.length > 0) + const schemas = useSchemaStore((s) => s.schemas) + const fetchSchemas = useSchemaStore((s) => s.fetchAll) const searchPanelOpen = !!searchTerm && hasResults + // Schemas power display-name resolution (title_key / index) for search + // results and any other node chrome — load once on mount if not already + // populated (e.g. by the ontology page). + useEffect(() => { + if (useMocks() || schemas.length > 0) return + fetchSchemas() + }, [schemas.length, fetchSchemas]) + // Auto-close sources when search results appear useEffect(() => { if (searchPanelOpen) setSourcesOpen(false) diff --git a/src/components/layout/search-results-panel.tsx b/src/components/layout/search-results-panel.tsx index 7add0ff..513aee5 100644 --- a/src/components/layout/search-results-panel.tsx +++ b/src/components/layout/search-results-panel.tsx @@ -6,14 +6,33 @@ import { Separator } from "@/components/ui/separator" import { Badge } from "@/components/ui/badge" import { useGraphStore } from "@/stores/graph-store" import { useAppStore } from "@/stores/app-store" +import { useSchemaStore } from "@/stores/schema-store" import type { GraphNode } from "@/lib/graph-api" +import type { SchemaNode } from "@/app/ontology/page" -function NodeRow({ node }: { node: GraphNode }) { - const name = - (node.properties?.name as string) ?? - node.ref_id +const DISPLAY_KEY_FALLBACKS = ["name", "title", "label", "text", "content", "body"] as const +function pickString(props: Record | undefined, key: string | undefined): string | undefined { + if (!props || !key) return undefined + const v = props[key] + return typeof v === "string" && v.length > 0 ? v : undefined +} + +function NodeRow({ node, schemas }: { node: GraphNode; schemas: SchemaNode[] }) { const nodeType = node.node_type ?? "Unknown" + const schema = schemas.find((s) => s.type === nodeType) + // Priority: title_key → index (sphinx convention) → common display-ish + // property names → ref_id. The last step catches nodes whose schema key + // isn't populated on this particular row. + const props = node.properties + let name = pickString(props, schema?.title_key) ?? pickString(props, schema?.index) + if (!name) { + for (const key of DISPLAY_KEY_FALLBACKS) { + name = pickString(props, key) + if (name) break + } + } + if (!name) name = node.ref_id return (